Javascript.RU

Оптимальное ООП с Google Closure Compiler

В javascript есть несколько основных стилей объявления объектов и свойств.
Они основаны на разной структуре кода, и поэтому - по-разному сжимаются компилятором.

Цель этой статьи - выбрать наилучший стиль ООП с точки зрения минификации кода и понять, почему ООП в таком стиле сжимается лучше всего.

Сжатие будет осуществляться продвинутым способом: --compilation_level ADVANCED_OPTIMIZATIONS.

Мы рассмотрим различные варианты ООП для виджета SayWidget с двумя приватными методами init, setElemById и публичным методом setSayHandler.

Кроме того, для проверки, как работает удаление недостижимого кода, в виджете будет присутствовать неиспользуемый метод unused

Первый стиль ООП основывается на классическом механизме наследования javascript, при котором свойства и методы берутся из прототипа.

function SayWidget(id) {
	this.setElemById(id)
	this.init()
}

SayWidget.prototype = {
	init: function() {
		this.elem.style.display = 'none'
	},
	setElemById: function(id) {
		this.elem = document.getElementById(elem)
	},
	setSayHandler: function() {
		this.elem.onclick = function() { 
			alert('hi')
		}
	},
	unused: function() { alert("unused") }
}

window['SayWidget'] = SayWidget
SayWidget.prototype['setSayHandler'] = SayWidget.prototype.setSayHandler

Результат сжатия:

function a(b) {
  this.c(b);
  this.b()
}
a.prototype = {b:function() {
  this.a.style.display = "none"
}, c:function() {
  this.a = document.getElementById(elem)
}, d:function() {
  this.a.onclick = function() {
    alert("hi")
  }
}};
window.SayWidget = a;
a.prototype.setSayHandler = a.prototype.d;

Как видно, все свойства. кроме экстернов, были заменены на короткие.
Что очень кстати, был удален неиспользуемый метод unused.

Этот метод, в принципе, аналогичен предыдущему, но методы добавляются в прототип по одному.

function SayWidget(id) {
	this.setElemById(id)
	this.init()
}

SayWidget.prototype.init = function() {
	this.elem.style.display = 'none'
}	
SayWidget.prototype.setElemById = function(id) {
	this.elem = document.getElementById(id)
}
SayWidget.prototype.setSayHandler = function() {
	this.elem.onclick = function() { 
		alert('hi')
	}
}
SayWidget.prototype.unused = function() { alert("unused") }

window['SayWidget'] = SayWidget
SayWidget.prototype['setSayHandler'] = SayWidget.prototype.setSayHandler

После сжатия:

function a(b) {
  this.a = document.getElementById(b);
  this.a.style.display = "none"
}
a.prototype.b = function() {
  this.a.onclick = function() {
    alert("hi")
  }
};
window.SayWidget = a;
a.prototype.setSayHandler = a.prototype.b;

А здесь - уже интереснее. Благодаря тому, что каждая функция описана отдельно, Google Closure Compiler может построить граф взаимодействий и заинлайнить функции. Что и произошло: в прототипе осталась только одна функция a.prototype.b (бывшая setSayHandler).

В результате размер существенно уменьшился.

Это - концептуально другой подход: методы записываются не в прототип, а в сам объект во время создания. При этом приватные свойства и методы являются локальными переменными функции-конструктора.

В следующем коде находится два неиспользуемых метода unused: один публичный и один - приватный.

function SayWidget(id) {
    var elem
 
    setElemById(id)
    init()
 
    function init() {
        elem.style.display = 'none'
    }    
 
    function setElemById(id) {
        elem = document.getElementById(id)
    }    

    function unused() {
        alert("unused")
    }    
 
    this.setSayHandler = function() {
        elem.onclick = function() { 
            alert('hi')
        }
    }
    this.unused = function() { 
	alert("unused") 
    }

}
window['SayWidget'] = SayWidget

После сжатия:

function b(c) {
  function d() {
    a.style.display = "none"
  }
  function e(f) {
    a = document.getElementById(f)
  }
  var a;
  e(c);
  d();
  this.a = function() {
    a.onclick = function() {
      alert("hi")
    }
  };
  this.b = function() {
    alert("unused")
  }
}
window.SayWidget = b;

Этот стиль ООП обычно сжимается лучше прототипного, т.к. обычный компрессор (или Google Closure Compiler в безопасном режиме) сжимает только локальные переменные.

Но продвинутый режим Google Closure Compiler уравнивает локальные переменные с обычными (кроме экстернов) - он сжимает и то и другое.

Поэтому никакого преимущества этот стиль ООП не показал. Более того, в данном компилятору не удалось ни заинлайнить вызовы ни удалить неиспользуемый публичный метод.

Последний из рассматриваемых стилей работает без вызова оператора new. Он просто возвращает объект с нужными методами.

// object = sayWidget(id)
function sayWidget(id) {
	var elem
	
	setElemById(id)
	init()
	
	function init() {
		elem.style.display = 'none'
	}

	function setElemById(id) {
		elem = document.getElementById(id)
	}


	function unused() { 
		alert("unused") 
	}

	var me = {  // новый объект
		setSayHandler: function() {
			elem.onclick = function() { 
				alert('hi')
			}
		},
		unused: function() { 
			alert("unused") 
		}
	}
	me['setSayHandler'] = me.setSayHandler

	return me
}
window['sayWidget'] = sayWidget

После сжатия:

function c(a) {
  function d() {
    b.style.display = "none"
  }
  function e(f) {
    b = document.getElementById(f)
  }
  var b;
  e(a);
  d();
  a = {a:function() {
    b.onclick = function() {
      alert("hi")
    }
  }, b:function() {
    alert("unused")
  }};
  a.setSayHandler = a.a;
  return a
}
window.sayWidget = c;

Результат аналогичен предыдущему.

Победил в итоге, вот сюрприз, самый длинный способ - добавление каждого свойства через прототип кодом вида: SayWidget.prototype.methodXXX = ....

Более того, эта победа не случайна. Такой способ позволяет компилятору построить самый полный граф взаимодействий и произвести инлайны.
А определение функций в прототипе вместо замыкания - дает возможность успешно вырезать неиспользуемые символы. В результате - уменьшение размера кода.

В библиотеке Google Closure Library, под которую заточен Closure Compiler, используется именно такой стиль объявления свойств.


Автор: Alex S (не зарегистрирован), дата: 23 апреля, 2010 - 15:28
#permalink

На самом деле можно еще описать класс в таком стиле:

var oop = {};

(function (ns){

function KeyType() {}

/**
* @constructor
*/
function TestObject() {
this._markedObj = new KeyType()
}

TestObject.prototype = {
_markedObj : null,

doIt: function() {
opns.addEvent("keydown", function(obj) {
return obj instanceof KeyType
})
},

getMarked: function() {
return this._markedObj
},

dummy: function(event) {
opns.isEvent(event)
}
}

/* export TestObject */
ns.TestObject = TestObject
})(oop)

В данной записи пространство имен oop будет содержать "публичный" класс TestObject, который в свою очередь использует "приватный" класс KeyType. В данном случае closure сможет убрать, к примеру, метод dummy, если он не используется. И вообще, похоже, closure сможет выполнить все оптимизации за исключением одной - вызов функции вводящей определение TestObject не будет заинлайнен.

У приведенного кода есть только одно преимущество перед описанным добавлением через прототип - этот способ позволяет вводить приватные сущности, используемые публично видимыми классами (типа KeyType в описанном примере).


Автор: Зарегистрирован (не зарегистрирован), дата: 16 ноября, 2011 - 15:57
#permalink

К слову

a.prototype.b = ...;
a.prototype.setSayHandler = a.prototype.b;

- не полный аналог

a.prototype.setSayHandler = ...;

Полный аналог выглядел бы так:

a.prototype.b = ...;
a.prototype.setSayHandler = a.prototype.b;
delete a.prototype.b; // <<<<<

Короче, GCC не просто переименовывает свойства, а дублирует их, что теоретически может выйти боком, когда свойства по ходу дела меняются, или когда делается перебор свойств (например, для сериализации).


Автор: tyrus.mlearner (не зарегистрирован), дата: 2 февраля, 2012 - 13:21
#permalink

А что же делать, если unused - мое API в конструкторе для объекта, и самим объектом не используется, но используется внешним (либо другим js-кодом, либо даже flash'ом)? Получается он его похерит Sad


Автор: picos school (не зарегистрирован), дата: 9 июля, 2021 - 06:30
#permalink

окончательная декомпрессия этой кодовой последовательности с количеством циклов достаточна только для одного файла, если файлов больше, это будет невозможно, поэтому выберите более широкое поле и добавьте код, который автоматически дублирует количество циклов.
picos school


Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 01:23
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 02:08
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 12:49
#permalink

Автор: ichylu (не зарегистрирован), дата: 5 октября, 2022 - 12:28
#permalink

Because all games in free games unblocked are currently unblocked, you might even utilize a smart device like a smartphone, iPad, or PC to play them at work or school.


Автор: allanpetter, дата: 2 ноября, 2022 - 17:26
#permalink

This is a very great post and the way you express all your post details is too good. Thanks for sharing with us this useful post interior painting near me Raleigh NC


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
11 + 5 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum