Две функции с тем же именем в JavaScript - как это может работать?

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

Итак, в этом примере переменная me должна быть не корректно разрешена внутри методов , и она не находится в Explorer 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/

Ответы

Ответ 1

Функция AFAIK 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 и выше. (Подробнее здесь: Double take и здесь: Именованные функциональные выражения demystified.)

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

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

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

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

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.)

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

  • Анонимная закрывающая функция создается, когда строка var foo = ... встречается в пошаговом коде, а затем она вызывается (потому что у меня есть () в самом конце).
  • При входе в контекст выполнения, созданный этой анонимной функцией, обрабатываются декларации функций bar1_me и bar2_me, и эти символы добавляются в область внутри этой анонимной функции (технически для объекта переменной для контекста выполнения).
  • Символ publicSymbols добавляется в область внутри анонимной функции. (Подробнее: Плохое непонятое var)
  • Пошаговый код начинается с назначения {} publicSymbols.
  • Пошаговый код продолжается с publicSymbols.bar1 = bar1_me; и publicSymbols.bar2 = bar2_me; и, наконец, return publicSymbols;
  • Результат анонимной функции присваивается 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, в той же строке).

Ответ 2

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

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


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

Короткие: каждый раз, когда вызывается функция, создается новый Context. Существует некоторый "черныймагический" тип парня, который называется Activation Object, который хранит некоторые вещи. Например,

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

Например:

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

    function visible() {
    }

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

Объект активации для foo будет выглядеть так:

  • аргументы: test, bar
  • переменные: hello (string), visible (function)
  • [[Область]]: (возможный родительский функция-контекст), Глобальный объект

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

Ответ 3

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

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

bar1 : function me() {
}

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

bar1 : function() {
}

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