Эффект Javascript для возврата функции из функции самоисполнения?

В firefox, похоже, существует большая разница в производительности между двумя следующими функциями:

var n1 = 12;

var add1 = function(n2){
    return n1 + n2;
}

var add2 = (function(){
    return function(n2){
            return n1 + n2;
    }
})();

Я думал, что это должно быть связано с внедрением другого уровня видимости, поэтому был создан третий пример с переменной, кэшированной на один уровень вверх. Но это показывает еще большее снижение (80%!)

var add3 = (function(){
    var cn1 = n1;
    return function(n2){
            return cn1 + n2;
    }
})();

Я бы подумал, что закрытие здесь закроет разрыв в производительности, а не увеличит его. Кто-нибудь знает, что здесь происходит?

Страница тестовой страницы jsPerf: http://jsperf.com/variable-scope-speed

Ответ 1

Это интересно, но мое небольшое тестирование здесь, похоже, подтверждает мое предыдущее предположение, то есть среда jsPerf влияет на области видимости и видимость в цепочке. Я не искал "как" и "почему", но это мой маленький testcript:

var n1 = 12;

var add1 = function(n2){
        return n1 + n2;
}

var add2 = (function(){
        return function(n2){
                return n1 + n2;
        }
})();

var add3 = (function(){
        var cn1 = n1;
        return function(n2){
                return cn1 + n2;
        }
})();

var add4 = function( n2, n1 ) {
    return n2 + n1;
}.bind(null, n1);


var tests = [add1, add2, add3, add4],
    start, stop, loops;

tests.forEach(function( fnc ) {
    loops = 100000;

    start = Date.now();
    while( loops-- ) {
        fnc( 2 );
    }
    stop = Date.now();

    console.info('Method ', fnc.name || fnc.toString(), ': ', stop - start, ' ms.');
});

И результаты моего FireFox 14 выглядят так:

Метод add1: 570ms.
Метод add2: 566ms.
Метод add3: 414ms.
Метод add4: 479ms.

Последние результаты Chrome:

Метод add1:199ms.
Метод add2: 136ms.
Метод add3: 85ms.
Метод add4: 144 мс.

Что определенно выглядит более разумным. Закрытый поиск по цепочке должен быть всегда быстрее, просто потому, что существует более короткая цепочка поиска. Даже если все современные браузеры обычно не выполняют классический поиск по цепочке, я знаю об этом. Во всяком случае, поскольку браузеры создают довольно умные таблицы поиска для бесплатных (или вне сферы видимости) переменных, все результаты должны быть по крайней мере одинаковыми. Не имеет смысла переопределять глобальный доступ к объектам IMO.

Как вы заметили, я создал еще один тестовый файл для связанного метода.

Ответ 2

var n1 = 12;

// "add1" is superfast in Firefox, but not in Chrome. Why?
// it seems Firefox is smarter in retrieving n1 value from global scope
var add1 = function(n2){
        return n1 + n2;
}

// "add2" is much slower in Firefox, but in Chrome the speed is almost equal to "add2"
// it seems that Firefox smart retrieving from global scope is not applied in this case
// it is understandable that "add2" is slower, because the interpreter needs to go back two levels in the scope chain to find the "n1" variable value
var add2 = (function(){
        return function(n2){
                return n1 + n2;
        }
})();

// If JavaScript works as PHP, then it won't copy "n1" value in "cn1".
// If this is the case, then it is understandle why it is slower.
// The interpreter first needs to search and find "cn1" value;
// when it finally finds it in the previous scope level,
// then it needs to start a second "search and find travel", looking for the value.
// This means "cn1" does not contain "12", but a reference to it in the memory.
// While I don't know if this is how JavaScript engine works,
// I know that PHP does not create a copy of the value -
// both variables share the same content in the memory.
// Short story being: it is slower because it must perform two "searches" instead of one.
var add3 = (function(){
        var cn1 = n1;
        return function(n2){
                return cn1 + n2;
        }
})();