Две функции с одинаковым именем в JavaScript - как это может работать? - PullRequest
17 голосов
/ 28 февраля 2011

Насколько я знаю, function foo() { aaa(); } это просто var foo = function(){ aaa() } в JavaScript.Поэтому добавление function foo() { bbb(); } должно либо перезаписать переменную foo, либо игнорировать второе определение - это не главное.Дело в том, что должна быть одна переменная foo.

Так что в этом примере переменная me должна не быть правильно разрешена изнутри методов , и этоне в проводнике 8: -) .Я пришел к этому примеру, пытаясь заключить их в другое замыкание, где (var) me было бы, но я был удивлен, что в этом нет необходимости:

    var foo = {
        bar1 : function me() {
            var index = 1;
            alert(me);
        },
        bar2 : function me() {
            var index = 2;
            alert(me);
        }    
    };

    foo.bar1(); // Shows the first one
    foo.bar2(); // Shows the second one

Демонстрация: http://jsfiddle.net/W5dqy/5/

Ответы [ 3 ]

33 голосов
/ 28 февраля 2011

AFAIK function foo () {aaa (); } это просто var foo = function () {aaa ()} в JavaScript.

Это на самом деле неверно. У JavaScript есть две разные, но взаимосвязанные вещи: функция объявления и функция выражения . Они происходят в разное время в цикле синтаксического анализа и имеют разные эффекты.

Это функция объявление :

function foo() {
    // ...
}

Объявления функций обрабатываются при входе в окружающую область до выполнения любого пошагового кода.

Это функция выражение (в частности, анонимная):

var foo = function() {
    // ...
};

Выражения функций обрабатываются как часть пошагового кода в том месте, где они появляются (как и любое другое выражение).

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

var x = function foo() {
    // ...
};

(В вашем случае он находится внутри литерала объекта, поэтому он находится справа от : вместо =, но это все еще выражение с именованной функцией.)

Это совершенно справедливо, игнорируя ошибки реализации (чуть позже). Он создает функцию с именем foo, , а не , не помещает foo в ограничивающую область, а затем назначает эту функцию переменной x (все это происходит, когда встречается выражение в пошаговом коде). Когда я говорю, что это не помещает foo в охватывающую область, я имею в виду именно это:

var x = function foo() {
    alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo);     // alerts "undefined" (in compliant implementations)

Обратите внимание, как это отличается от способа, которым работают объявления (где имя функции равно , добавленному в объем приложения).

Выражения именованных функций работают на совместимых реализациях. Исторически существовали ошибки в реализациях (ранние Safari, IE8 и более ранние). Современные реализации делают их правильными, включая IE9 и выше. (Подробнее здесь: Двойной дубль и здесь: Демистифицированные выражения именованных функций .)

Итак, в этом примере переменная me не должна быть разрешена изнутри методами

На самом деле, так и должно быть. Истинное имя функции (символ между function и открывающей скобкой) всегда находится внутри области действия (независимо от того, является ли функция объявлением или выражением именованной функции).

ПРИМЕЧАНИЕ : нижеследующее было написано в 2011 году. С достижениями в JavaScript с тех пор я больше не чувствую необходимости делать такие вещи, как ниже, если я не знаю, что буду иметь дело с IE8 ( что очень редко в наши дни).

Из-за ошибок в реализации я раньше избегал выражений именованных функций. Вы можете сделать это в своем примере, просто удалив me names, но Я предпочитаю именованные функции , и поэтому, как это ни странно, вот как я писал ваш объект:

var foo = (function(){
    var publicSymbols = {};

    publicSymbols.bar1 = bar1_me;
    function bar1_me() {
        var index = 1;
        alert(bar1_me);
    }

    publicSymbols.bar2 = bar2_me;
    function bar2_me() {
        var index = 2;
        alert(bar2_me);
    }

    return publicSymbols;
})();

(за исключением того, что я, вероятно, использовал бы более короткое имя, чем publicSymbols.)

Вот как это обрабатывается:

  1. Анонимная включающая функция создается, когда в пошаговом коде встречается строка var foo = ..., а затем вызывается (потому что у меня () в самом конце).
  2. При входе в контекст выполнения, созданный этой анонимной функцией, объявления функций bar1_me и bar2_me обрабатываются, и эти символы добавляются в область внутри этой анонимной функции (технически, в переменный объект *). 1093 * для контекста выполнения).
  3. Символ publicSymbols добавляется в область действия внутри анонимной функции. (Подробнее: Плохо понял var)
  4. Пошаговый код начинается с присвоения {} publicSymbols.
  5. Пошаговый код продолжается с publicSymbols.bar1 = bar1_me; и publicSymbols.bar2 = bar2_me; и, наконец, return publicSymbols;
  6. Результат анонимной функции присваивается foo.

Однако в наши дни, если я не пишу код, который, как я знаю, должен поддерживать IE8 (к сожалению, когда я пишу это в ноябре 2015 года, он все еще имеет значительную долю на мировом рынке , но, к счастью, эта доля стремительно падает ), Я не беспокоюсь об этом. Все современные движки JavaScript их прекрасно понимают.

Вы также можете написать это так:

var foo = (function(){
    return {
        bar1: bar1_me,
        bar2: bar2_me
    };

    function bar1_me() {
        var index = 1;
        alert(bar1_me);
    }

    function bar2_me() {
        var index = 2;
        alert(bar2_me);
    }
})();

... так как это объявления функций, и, следовательно, они поднимаются. Обычно я так не делаю, так как мне легче выполнять обслуживание больших структур, если я делаю объявление и присваиваемое свойство рядом друг с другом (или, если не пишу для IE8, в одной строке) .

3 голосов
/ 28 февраля 2011

Оба me поиска, только видимые / доступные внутри выражения функции.

На самом деле эти два * выражены именованными функциями , и спецификация ECMAscript сообщаетнам, что имя выражения не подвергается такому названию Variable object.


Ну, я пытался выразить это только в нескольких словах, но, пытаясь найти правильные слова, этозаканчивается довольно глубокой цепочкой поведения ECMAscript.Так, function expression не хранятся в Variable / Activation Object.(Это привело бы к вопросу, кто эти парни ...).

Short: Каждый раз, когда вызывается функция, создается новый Context.Есть какой-то «чернокожий» парень, которого зовут Activation object, который хранит некоторые вещи.Например,

  • аргументы функции
  • [[Scope]]
  • любые переменные, созданные var

Например:

function foo(test, bar) {
    var hello = "world";

    function visible() {
    }

    (function ghost() {
    }());
}

Объект активации для foo будет выглядеть следующим образом:

  • аргументы: тест, бар
  • переменные: привет (строка), видимый (функция)
  • [[Scope]]: (возможный родительский функциональный контекст), глобальный объект

ghost не сохраняется в AO!это было бы просто доступно под этим именем внутри самой функции.Хотя visible() является объявлением функции (или оператором функции ), оно сохраняется в AO.Это связано с тем, что объявление функции оценивается, когда синтаксический анализ и выражение функции оценивается во время выполнения.

1 голос
/ 28 февраля 2011

Что здесь происходит, так это то, что function() имеет много разных значений и применений.

Когда я говорю

bar1 : function me() {
}

, тогда это на 100% эквивалентно

bar1 : function() {
}

т.е. имя не имеет значения, когда вы используете функцию для присвоения переменной bar1.Внутри присваивается me, но как только определение функции остается (при назначении bar2), me снова создается как локальная переменная для определения функции, которое хранится в bar2.

...