Функция Javascript «Отдых»? - PullRequest
2 голосов
/ 25 апреля 2011

Я прочитал много кода JavaScript и вижу много разных стилей создания так называемых классов. Я разрабатываю легкий класс DOMish, который содержит минимум для моего шаблонного скрипта, работающего на Node.JS (который преобразует экземпляр DOMish в другой экземпляр DOMish с использованием JSON, а затем сериализует его в HTML, кэшируя исходный экземпляр DOMish и клонирование его по шаблону-запросу).

Выше все не имеет отношения к моему вопросу. После прочтения http://www.phpied.com/3-ways-to-define-a-javascript-class/, Раздел 1.2. Методы, определенные внутри

Недостаток 1.1. является то, что метод getInfo () воссоздается каждый раз, когда вы создаете новый объект.

Способ определения классов, как описано в Разделе 1.1, (немного изменен, чтобы отразить в моем собственном случае, используя локальные / частные переменные):

function Apple (type) {
    var that = this;

    // local-variable
    var color = 'red';

    // local-(extern)-function
    var infoProvider = function() { // inner-function
        // note the danger of this and that!
        return that.color + ' ' + that.type + ' ' + this.type;
    };

    this.__defineGetter__("type", function() { return type; });

    this.getInfo = function(otherContext) {
        return infoProvider.call(otherContext); // other "this" scope
    };
}

var apple = new Apple('iPod');
apple.getInfo({type: 'music player'}); // 'red iPod music player'

Я использую тот же стиль, поскольку теперь у функции есть доступ к локальным / частным переменным и функциям, определенным внутри конструктора. Но предложение «[Функция] воссоздается каждый раз, когда вы создаете новый объект». пугает меня ( производительность мудрый)! Поскольку я использую Node, который использует V8, я всегда думал, что функции создаются только один раз и кешируются каким-то образом при вызове различных объектов, используя только другой контекст (this es и that s) .

Должен ли я бояться "отдыха"? Насколько это плохо по сравнению с функциями на основе прототипов? Или это чисто эстетическое (люди любят хранить вещи вместе, в конструкторе, люди любят прототипы)?

Ответы [ 2 ]

3 голосов
/ 25 апреля 2011

Несмотря на то, что V8 действительно кеширует много вещей, это не один из тех случаев, о чем свидетельствует следующий код :

> function foo(){ this.bar = function(){ return this.x; }; this.x = Math.random(); }
> var ary = [];
> for(var i=0; i<1000000; i++){ ary.push(new foo()); }

, приведенное выше использует 144 МБ памяти.

> function foo(){ this.x = Math.random(); }
> foo.prototype.bar = function(){ return this.x; }
> var ary = [];
> for(var i=0; i<1000000; i++){ ary.push(new foo()); }

И это использует 68 МБ памяти.

Обратите внимание, что заголовок источника V8 в:

http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/compilation-cache.h?r=5284

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

Кэш компиляции хранит информацию о совместно используемых функциях для скомпилированных скриптов и evals.Информация об общей функции ищется с использованием исходной строки в качестве ключа.Для регулярных выражений данные компиляции кэшируются.

Но тест показывает, что даже если компиляция компилируется, новые объекты все равно создаются, как если бы вы не создавали новые объекты, вы бы сломали JS :)

РЕДАКТИРОВАТЬ

Дальнейший тест:

function foo(){
    this.bar = function(){
        this.x = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';
        return this.x;
    };
    this.x = Math.random();
}
var ary = [];
for(var i=0; i<1000000; i++){ ary.push(new foo()); }

Использует 144 МБ памяти.Из-за того, что я добавил строку из 100 символов, если бы сама функция не была кэширована, мы использовали бы дополнительные 100 МБ памяти или около того.

Таким образом, приведенное выше означает, что да, сама функция кэшируется,Но он все еще должен быть представлен новым объектом.

2 голосов
/ 25 апреля 2011

следующий код:

this.getInfo = function(otherContext) {
    return infoProvider.call(otherContext); // other "this" scope
};

... создает новую функцию и присваивает ссылку на нее свойству getInfo вновь созданного объекта. Это происходит каждый раз, когда вы вызываете new Apple. Каждый новый экземпляр получает свою собственную копию.

Однако, этот код:

Apple.prototype.getInfo = function(otherContext) {};

... создает один общий экземпляр функции getInfo, назначенный прототипу конструктора.

Это различие не чисто эстетическое. Это принципиально другой способ создания экземпляров методов. Функция похожа на любой другой объект (т. Е. Вы можете назначить ему свойства). Например, вы можете сделать это:

this.getInfo.classVariable = "whatever" 

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

...