var functionName = function () {} против функции functionName () {} - PullRequest
6466 голосов
/ 03 декабря 2008

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

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

Два способа:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

Каковы причины использования этих двух разных методов и каковы плюсы и минусы каждого? Можно ли что-то сделать одним методом, а другим - нет?

Ответы [ 37 ]

37 голосов
/ 01 мая 2015

Я добавляю свой собственный ответ только потому, что все остальные подробно рассмотрели подъемную часть.

Я долго думал о том, какой путь лучше, и благодаря http://jsperf.com теперь я знаю :)

enter image description here

Объявления функций быстрее, и вот что действительно имеет значение в web dev, верно? ;)

32 голосов
/ 06 февраля 2013

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

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

По сути, все объявления функций и объявления переменных подняты наверх функции , в которой происходит объявление (поэтому мы говорим, что JavaScript имеет область действия ). 1015 *

  • Когда объявление функции поднято, тело функции «следует» поэтому, когда тело функции вычисляется, переменная немедленно быть привязанным к функциональному объекту.

  • Когда объявление переменной поднято, инициализация не следовать, но "оставлен позади". Переменная инициализируется как undefined в начале тела функции и будет назначено значение в исходном месте в коде. (На самом деле, ему будет присвоено значение в каждом месте, где происходит объявление переменной с тем же именем.)

Порядок подъема также важен: объявления функций имеют приоритет над объявлениями переменных с тем же именем, а последнее объявление функции имеет приоритет над предыдущими объявлениями функций с тем же именем.

Некоторые примеры ...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

Переменная foo выводится в начало функции, инициализируется undefined, так что !foo равен true, поэтому foo назначается 10. foo вне области действия bar не играет никакой роли и остается нетронутым.

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

Объявления функций имеют приоритет над объявлениями переменных, а последнее объявление функции «залипает».

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

В этом примере a инициализируется объектом функции, полученным в результате оценки второго объявления функции, а затем присваивается 4.

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

Здесь объявление функции сначала поднимается, объявляя и инициализируя переменную a. Далее этой переменной присваивается значение 10. Другими словами: присваивание не присваивается внешней переменной a.

31 голосов
/ 05 июня 2014

Первый пример - это объявление функции:

function abc(){}

Второй пример - это выражение функции:

var abc = function() {};

Основное различие заключается в том, как они поднимаются (поднимаются и декларируются). В первом примере вся декларация функции поднята. Во втором примере поднят только var 'abc', его значение (функция) будет неопределенным, а сама функция останется в той позиции, в которой она объявлена.

Проще говоря:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

Чтобы узнать больше об этой теме, я настоятельно рекомендую вам это ссылка

28 голосов
/ 23 января 2010

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

  • Независимо от места, где они объявлены (но все еще ограничено областью действия).
  • Более устойчивы к ошибкам, таким как условная инициализация (вы все равно можете переопределить, если хотите).
  • Код становится более читабельным благодаря выделению локальных функций отдельно от функциональности области. Обычно в области действия сначала идет функциональность, за которой следуют объявления локальных функций.
  • В отладчике вы четко увидите имя функции в стеке вызовов вместо «анонимной / оцененной» функции.

Я подозреваю, что следующие PROS для именованных функций приведены ниже. И то, что перечислено как преимущество именованных функций, является недостатком для анонимных.

Исторически анонимные функции возникали из-за невозможности JavaScript как языка перечислять членов с именованными функциями:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
24 голосов
/ 29 ноября 2012

Я использую переменный подход в своем коде по очень конкретной причине, теория которой была абстрактно рассмотрена выше, но пример может помочь некоторым людям, таким как я, с ограниченным опытом работы с JavaScript.

У меня есть код, который мне нужно запустить с 160 индивидуально разработанными брендами. Большая часть кода находится в общих файлах, но материал, относящийся к брендингу, находится в отдельном файле, по одному для каждого бренда.

Некоторые бренды требуют определенных функций, а некоторые нет. Иногда мне нужно добавлять новые функции, чтобы делать новые вещи, специфичные для брендинга. Я рад изменить код общего доступа, но мне не нужно менять все 160 наборов фирменных файлов.

Используя синтаксис переменной, я могу объявить переменную (по существу, указатель функции) в общем коде и либо назначить тривиальную функцию-заглушку, либо установить значение null.

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

Исходя из комментариев людей выше, я понимаю, что, возможно, можно также переопределить статическую функцию, но я думаю, что решение для переменной хорошо и понятно.

22 голосов
/ 18 декабря 2008

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

Для получения дополнительной информации об анонимных функциях и лямбда-исчислении, Wikipedia - хорошее начало (http://en.wikipedia.org/wiki/Anonymous_function).

22 голосов
/ 21 июля 2015

Ответ Грега достаточно хорош, но я все же хотел бы добавить к нему кое-что, чему научился только сейчас, наблюдая видео Дугласа Крокфорда .

Выражение функции:

var foo = function foo() {};

Оператор функции:

function foo() {};

Оператор function - это просто сокращение для оператора var со значением function.

So

function foo() {};

расширяется до

var foo = function foo() {};

Что расширяется до:

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

И они оба поднимаются наверх кода.

Screenshot from video

18 голосов
/ 04 августа 2012

@ EugeneLazutkin приводит пример, где он называет назначенную функцию, чтобы иметь возможность использовать shortcut() в качестве внутренней ссылки на себя. Джон Резиг приводит другой пример - копирование рекурсивной функции, назначенной другому объекту , в своем учебнике Advanced Javascript . Хотя назначение функций свойствам здесь не является строгим вопросом, я рекомендую активно опробовать учебное пособие - запустите код, нажав кнопку в верхнем правом углу, и дважды щелкните код, чтобы изменить его по своему вкусу.

Примеры из учебника: рекурсивные вызовы в yell():

Сбой тестирования при удалении исходного объекта ниндзя. (стр. 13)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

Если вы назовете функцию, которая будет вызываться рекурсивно, тесты пройдут. (стр. 14)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
16 голосов
/ 15 октября 2012

Другое отличие, которое не упоминается в других ответах, заключается в том, что если вы используете анонимную функцию

var functionOne = function() {
    // Some code
};

и используйте это как конструктор, как в

var one = new functionOne();

тогда one.constructor.name не будет определено. Function.name не является стандартным, но поддерживается Firefox, Chrome, другими браузерами, производными от Webkit, и IE 9 +.

С

function functionTwo() {
    // Some code
}
two = new functionTwo();

можно получить имя конструктора в виде строки с two.constructor.name.

14 голосов
/ 05 января 2013

Первый (функция doSomething (x)) должен быть частью нотации объекта.

Второй (var doSomething = function(x){ alert(x);}) просто создает анонимную функцию и присваивает ее переменной doSomething. Так что doSomething () вызовет функцию.

Возможно, вы захотите узнать, что такое объявление функции и выражение функции .

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

function foo() {
    return 3;
}

ECMA 5 (13.0) определяет синтаксис как
Идентификатор функции (FormalParameterList opt ) {FunctionBody}

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

А в выражении функции

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

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5 (13.0) определяет синтаксис как
Идентификатор функции opt (FormalParameterList opt ) {FunctionBody}

...