var functionName = function () {} против функции functionName () {}

6876

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

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

Два способа:

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

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

Ричард Гарсайд
источник
199
permadi.com/tutorial/jsFunc/index.html - очень хорошая страница о функциях javascript
uzay95,
68
С этим связана отличная статья о выражениях именованных функций .
Phrogz
13
@CMS ссылки в этой статье: kangax.github.com/nfe/#expr-vs-decl
Upperstage
107
Есть две вещи, о которых вам нужно знать: # 1 В JavaScript объявления поднимаются. Это означает, что var a = 1; var b = 2;становится var a; var b; a = 1; b = 2. Поэтому, когда вы объявляете functionOne, он объявляется, но его значение не устанавливается сразу. Принимая во внимание, что так как functionTwo является просто объявлением, он помещается в верхнюю часть области видимости. # 2 functionTwo позволяет получить доступ к свойству name, что очень помогает при попытке отладки чего-либо.
xavierm02
65
Да, и кстати, правильный синтаксис с ";" после назначения и без объявления после. Например , function f(){}против var f = function(){};.
xavierm02

Ответы:

5047

Разница в том, что functionOneэто выражение функции, и поэтому оно определяется только при достижении этой строки, тогда как functionTwoявляется объявлением функции и определяется, как только его окружающая функция или скрипт выполняется (из-за подъема ).

Например, выражение функции:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

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

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

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

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

Greg
источник
633
@ Грег: Кстати, разница не только в том, что они анализируются в разное время. По сути, ваша functionOneпросто переменная, которой назначена анонимная функция, тогда как functionTwoна самом деле это именованная функция. Позвоните .toString()обоим, чтобы увидеть разницу. Это важно в некоторых случаях, когда вы хотите получить название функции программно.
Джейсон Бантинг
6
@Jason Bunting .. не уверен, что вы здесь делаете, .toString (), по-видимому, возвращает по существу одно и то же значение (определение функции) для обоих: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z
125
Есть и разные. Первый - function expressionвторой function declaration. Вы можете прочитать больше на эту тему здесь: javascriptweblog.wordpress.com/2010/07/06/…
Михал Куклис
128
@Greg Часть вашего ответа относительно времени разбора и времени выполнения неверна. В JavaScript объявления функций определяются не во время разбора, а во время выполнения. Процесс выглядит следующим образом: исходный код анализируется -> выполняется программа JavaScript -> инициализируется глобальный контекст выполнения -> выполняется создание экземпляра привязки объявления. Во время этого процесса создаются объявления функций (см. Шаг 5 главы 10.5 ).
Шиме Видас
103
Терминология для этого явления известна как подъем.
Колин Пир
1944

Во-первых, я хочу исправить Грега: function abc(){}это также ограничено - имя abcопределено в области, где встречается это определение. Пример:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

Во-вторых, можно сочетать оба стиля:

var xyz = function abc(){};

xyzбудет определен как обычно, abcне определен во всех браузерах, кроме Internet Explorer - не полагайтесь на его определение. Но это будет определено внутри его тела:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

Если вы хотите использовать псевдонимы для всех браузеров, используйте этот вид объявления:

function abc(){};
var xyz = abc;

В этом случае оба xyzи abcявляются псевдонимами одного и того же объекта:

console.log(xyz === abc); // prints "true"

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

function abc(){};
console.log(abc.name); // prints "abc"

его имя присваивается автоматически. Но когда вы определяете это как

var abc = function(){};
console.log(abc.name); // prints ""

его имя пустое - мы создали анонимную функцию и присвоили ее некоторой переменной.

Еще одна веская причина для использования комбинированного стиля - использовать короткое внутреннее имя для ссылки на себя, предоставляя длинное не конфликтующее имя для внешних пользователей:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

В приведенном выше примере мы можем сделать то же самое с внешним именем, но оно будет слишком громоздким (и медленным).

(Другой способ сослаться на себя - использовать arguments.callee, который все еще относительно длинный и не поддерживается в строгом режиме.)

В глубине души JavaScript обрабатывает оба утверждения по-разному. Это объявление функции:

function abc(){}

abc здесь определяется везде в текущей области:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Кроме того, он поднял через returnзаявление:

// We can call it here
abc(); // Works
return;
function abc(){}

Это функциональное выражение:

var xyz = function(){};

xyz здесь определяется с точки назначения:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

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

Интересный факт:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

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

var abc = function(){};

Я знаю, что я определил функцию локально. Когда я определяю функцию как

abc = function(){};

Я знаю, что я определил это глобально, при условии, что я не определил abcнигде в цепочке областей. Этот стиль определения устойчив, даже когда используется внутри eval(). Хотя определение

function abc(){};

зависит от контекста и может заставить вас угадать, где он определен, особенно в случае eval()- ответ таков: это зависит от браузера.

Евгений Лазуткин
источник
70
Я имею в виду Роборг, но его нигде не найти. Просто: Роборг === Грег. Вот как история может быть переписана в эпоху Интернета. ;-)
Евгений Лазуткин
10
var xyz = function abc () {}; console.log (xyz === abc); Все браузеры, которые я тестировал (Safari 4, Firefox 3.5.5, Opera 10.10), выдают «Неопределенная переменная: abc».
NVI
7
В целом, я думаю, что этот пост хорошо объясняет различия и преимущества использования объявления функции. Я согласен не согласиться с тем, насколько выгодно использование назначения выражений функций для переменной, тем более что «выгода», по-видимому, является пропагандой объявления глобальной сущности ... и все знают, что вы не должны загромождать глобальное пространство имен , правильно? ;-)
natlee75
84
По моему мнению, огромная причина использовать именованную функцию заключается в том, что отладчики могут использовать имя, чтобы помочь вам разобраться в вашем стеке вызовов или трассировке стека. это отстой, когда вы смотрите на стек вызовов и видите «анонимную функцию» глубиной 10 уровней ...
коза
3
var abc = function(){}; console.log(abc.name);больше не производит "", а "abc"вместо этого.
Qwerty
633

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

Сроки:

Быстрый список:

  • Объявление функции

  • «Анонимное» functionвыражение (которое, несмотря на термин, иногда создает функции с именами)

  • Именованное functionвыражение

  • Инициализатор функций доступа (ES5 +)

  • Выражение функции стрелки (ES2015 +) (которое, как и выражения анонимной функции, не содержит явного имени и все же может создавать функции с именами)

  • Объявление метода в инициализаторе объекта (ES2015 +)

  • Объявления конструктора и метода в class(ES2015 +)

Объявление функции

Первая форма - это объявление функции , которое выглядит так:

function x() {
    console.log('x');
}

Объявление функции - это объявление ; это не утверждение или выражение. Таким образом, вы не следуете за ним ;(хотя это безвредно).

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

Поскольку он обрабатывается перед любым пошаговым кодом в том же контексте, вы можете сделать что-то вроде этого:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

До ES2015, спецификация не не охватывает то , что двигатель JavaScript должен делать , если поместить объявление функции внутри структуры управления , как try, if, switch, whileи т.д., как это:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

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

Хотя это не было указано до ES2015, это было допустимое расширение для поддержки объявлений функций в блоках. К сожалению (и неизбежно), разные двигатели делали разные вещи.

Начиная с ES2015, в спецификации сказано, что делать. Фактически, это дает три отдельных действия:

  1. Если в свободном режиме нет в веб-браузере, движок JavaScript должен делать одно
  2. Если в свободном режиме в веб-браузере, движок JavaScript должен делать что-то еще
  3. Если в строгом режиме (браузер или нет), движок JavaScript должен делать еще одну вещь

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

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

«Анонимное» functionвыражение

Вторая распространенная форма называется выражением анонимной функции :

var y = function () {
    console.log('y');
};

Как и все выражения, оно оценивается, когда достигается при пошаговом выполнении кода.

В ES5 созданная функция не имеет имени (она анонимна). В ES2015, функции по возможности присваивается имя, выводя его из контекста. В приведенном выше примере имя будет y. Нечто подобное происходит, когда функция является значением инициализатора свойства. (Подробную информацию о том, когда это происходит и правилах, ищите SetFunctionNameв спецификации  - она ​​появляется повсюду .)

Именованное functionвыражение

Третья форма - это выражение с именованной функцией («NFE»):

var z = function w() {
    console.log('zw')
};

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

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

Обратите внимание, что NFE часто являются источником ошибок для реализаций JavaScript. Например, IE8 и более ранние версии обрабатывают NFE совершенно неправильно , создавая две разные функции в два разных момента времени. Ранние версии Safari также имели проблемы. Хорошей новостью является то, что в текущих версиях браузеров (IE9 и выше, текущий Safari) таких проблем больше нет. (Но на момент написания этой статьи, к сожалению, IE8 все еще широко используется, и поэтому использование NFE с кодом для Интернета в целом все еще проблематично.)

Инициализатор функций доступа (ES5 +)

Иногда функции могут проникнуть в значительной степени незамеченными; это в случае с функциями доступа . Вот пример:

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

Обратите внимание, что когда я использовал функцию, я не использовал ()! Это потому, что это функция доступа для свойства. Мы получаем и устанавливаем свойство обычным способом, но за кулисами вызывается функция.

Вы также можете создавать функции доступа с помощью Object.defineProperty, Object.definePropertiesи менее известный второй аргумент для Object.create.

Выражение функции стрелки (ES2015 +)

ES2015 приносит нам функцию стрелки . Вот один пример:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

Видишь эту n => n * 2вещь, скрывающуюся в map()звонке? Это функция.

Несколько вещей о функциях стрелок:

  1. У них нет своего this. Вместо этого, они близко надthis из контекста , в котором они определены. (Они также закрываются argumentsи, где это уместно super.) Это означает, что thisвнутри них то же самое, что и то, thisгде они созданы, и их нельзя изменить.

  2. Как вы уже заметили, вы не используете ключевое слово function; вместо этого вы используете =>.

Приведенный n => n * 2выше пример является одной из них. Если у вас есть несколько аргументов для передачи функции, вы используете parens:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(Помните, что Array#mapзапись передается как первый аргумент, а индекс как второй.)

В обоих случаях тело функции является просто выражением; возвращаемое значение функции будет автоматически результатом этого выражения (вы не используете явное return).

Если вы делаете больше, чем просто одно выражение, используйте {}и явное return(если вам нужно вернуть значение), как обычно:

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

Версия без { ... }называется функцией стрелки с телом выражения или кратким телом . (Также: краткая функция стрелки.) Функция с { ... }определением тела - это функция стрелки с телом функции . (Также: функция многословной стрелки.)

Объявление метода в инициализаторе объекта (ES2015 +)

ES2015 допускает более короткую форму объявления свойства, которое ссылается на функцию, называемую определением метода ; это выглядит так:

var o = {
    foo() {
    }
};

почти эквивалент в ES5 и более ранних версиях:

var o = {
    foo: function foo() {
    }
};

Разница (кроме многословия) в том, что метод может использовать super, а функция - нет. Так, например, если бы у вас был объект, который определил (скажем) valueOfс использованием синтаксиса метода, его можно было бы использовать super.valueOf()для получения значения, Object.prototype.valueOfкоторое было бы возвращено (прежде чем предположительно делать что-то еще с ним), тогда как вместо этого должна была бы быть версия ES5 Object.prototype.valueOf.call(this).

Это также означает, что метод имеет ссылку на объект, для которого он был определен, поэтому, если этот объект является временным (например, вы передаете его Object.assignкак один из исходных объектов), синтаксис метода может означать, что объект сохраняется в памяти, когда в противном случае он мог бы быть сборщиком мусора (если механизм JavaScript не обнаруживает эту ситуацию и не обрабатывает ее, если ни один из методов не использует super).

Объявления конструктора и метода в class(ES2015 +)

ES2015 приносит нам classсинтаксис, включая объявленные конструкторы и методы:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

Выше приведены два объявления функций: одно для конструктора, который получает имя Person, и другое для getFullName, которому назначена функция Person.prototype.

TJ Crowder
источник
3
тогда имя wпросто игнорируется?
BiAiB
8
@PellePenna: имена функций полезны для многих вещей. На мой взгляд, двумя важными факторами являются рекурсия и имя функции, отображаемой в стеках вызовов, трассировках исключений и тому подобном.
TJ Crowder
4
@ChaimEliyah - «Принятие не означает, что это лучший ответ, это просто означает, что это сработало для человека, который спросил». источник
ScrapCode
6
@ АР: Совершенно верно. Забавно, однако, прямо над ним написано: «Лучшие ответы появляются первыми, поэтому их всегда легко найти». Поскольку принятый ответ появляется первым даже над ответами с более высоким рейтингом, тур может быть несколько противоречивым. ;-) Также немного неточно, если мы определяем «лучший» по голосам (что ненадежно, это только то, что у нас есть), «лучшие» ответы отображаются первыми, только если вы используете вкладку «Голоса» - иначе, ответы, которые являются первыми, являются активными или самыми старыми.
TJ Crowder
1
@TJCrowder: Согласен. «Упорядочено по дате» иногда раздражает.
ScrapCode
145

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

Тонкое различие между этими двумя способами заключается в том, что при запуске процесса создания переменной (до фактического выполнения кода) все идентификаторы, объявленные с помощью, varбудут инициализированы undefined, а те, которые используются FunctionDeclarationс, будут доступны с того момента, например:

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

Назначение bar FunctionExpressionпроисходит до времени выполнения.

Глобальное свойство, созданное с помощью a, FunctionDeclarationможет быть перезаписано без каких-либо проблем, как значение переменной, например:

 function test () {}
 test = null;

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

Что foo = function() { alert('hello!'); };касается вашего отредактированного первого примера ( ), это незадекларированное задание, я настоятельно рекомендую вам всегда использовать varключевое слово.

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

Кроме того, необъявленные назначения выбрасывают в ReferenceErrorECMAScript 5 в строгом режиме .

Должен читать:

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

CMS
источник
Я не знал, что функции могут быть перезаписаны в JavaScript! Кроме того, этот порядок разбора является для меня большой точкой продажи. Думаю, мне нужно посмотреть, как я создаю функции.
Xeoncross
2
+0 к статье "Демистифицированные выражения функций имен", поскольку это 404ing. Возможно зеркало ?: kangax.github.com/nfe
Mr_Chimp
@CMS Хороший. Имейте в виду, хотя я никогда не видел оригинал, поэтому я не знаю, зеркало это или просто другая статья с таким же названием!
Mr_Chimp
@Mr_Chimp Я уверен, что это так, thewaybackmachine говорит, что он получил 302 во время сканирования, и перенаправление было на ссылку, которую вы предоставили.
Джон
125

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

Однако различие в поведении состоит в том, что с первым вариантом ( var functionOne = function() {}) эта функция может быть вызвана только после этой точки в коде.

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

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

Больше технической информации

В JavaScript есть три способа определения функций.

  1. Ваш первый фрагмент показывает выражение функции . Это включает использование оператора «function» для создания функции - результат этого оператора может быть сохранен в любой переменной или свойстве объекта. Таким образом, выражение функции является мощным. Выражение функции часто называют «анонимной функцией», потому что оно не обязательно должно иметь имя,
  2. Ваш второй пример - объявление функции . При этом используется оператор «function» для создания функции. Функция становится доступной во время анализа и может вызываться в любом месте этой области. Вы все еще можете сохранить его в переменной или свойстве объекта позже.
  3. Третий способ определения функции - это конструктор «Function ()» , который не показан в исходном посте. Не рекомендуется использовать это, поскольку это работает так же, как eval(), что имеет свои проблемы.
thomasrutter
источник
104

Лучшее объяснение ответа Грега

functionTwo();
function functionTwo() {
}

Почему нет ошибки? Нас всегда учили, что выражения выполняются сверху вниз (??)

Потому что:

Объявления функций и объявления переменных всегда hoistedнезаметно перемещаются ( ) в верхнюю часть их содержащей области действия интерпретатором JavaScript. Параметры функций и определяемые языком имена, очевидно, уже есть. Бен Черри

Это означает, что код такой:

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

Обратите внимание, что часть назначений объявлений не была поднята. Поднимается только имя.

Но в случае с объявлениями функций также будет поднято все тело функции :

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------
suhailvs
источник
HI suhail спасибо за ясную информацию о функциональной теме. Теперь мой вопрос: какое из них будет первым объявлением в иерархии объявлений: объявлением переменной (functionOne) или объявлением функции (functionTwo)?
Sharathi RB
91

Другие комментаторы уже рассмотрели семантическое различие двух вариантов выше. Я хотел бы отметить стилистическую разницу: только вариант «присваивания» может установить свойство другого объекта.

Я часто строю модули JavaScript с таким шаблоном:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

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

(Обратите также внимание, что для присваивания требуется точка с запятой после оператора, в то время как объявление запрещает это.)

Шон Макмиллан
источник
4
Насколько я могу судить, yuiblog.com/blog/2007/06/12/module-pattern является основной ссылкой на шаблон модуля. (Хотя эта статья использует var foo = function(){...}синтаксис даже для частных переменных.
Шон Макмиллан
На самом деле это не совсем так в некоторых старых версиях IE. (function window.onload() {} была вещь.)
Ry-
77

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

С

if (condition){
    function myfunction(){
        // Some code
    }
}

это определение myfunctionпереопределит любое предыдущее определение, поскольку оно будет выполнено во время синтаксического анализа.

Пока

if (condition){
    var myfunction = function (){
        // Some code
    }
}

делает правильную работу по определению myfunctionтолько тогда, когда conditionвстречается.

Мбенге Ассане
источник
1
этот пример хорош и близок к совершенству, но может быть улучшен. лучшим примером будет определение var myFunc = null;вне цикла или вне блока if / elseif / else. Затем вы можете условно назначить разные функции одной и той же переменной. В JS лучше назначить отсутствующее значение для нуля, а затем для неопределенного. Поэтому вы должны сначала объявить myFunction как null, а затем назначить ее позже, условно.
Александр Миллс
62

Важной причиной является добавление одной и только одной переменной в качестве «корня» вашего пространства имен ...

var MyNamespace = {}
MyNamespace.foo= function() {

}

или

var MyNamespace = {
  foo: function() {
  },
  ...
}

Есть много методов для пространства имен. Это становится все более важным с множеством доступных модулей JavaScript.

Также см. Как мне объявить пространство имен в JavaScript?

обкрадывать
источник
3
Похоже, этот ответ был объединен с этим вопросом из другого вопроса, и формулировка может показаться немного не связанной с этим вопросом. Не могли бы вы отредактировать ответ, чтобы он был направлен именно на этот вопрос? (повторюсь; это не ваша вина вообще ... просто побочный эффект объединенного вопроса). Вы также можете удалить его, и я думаю, что вы сохранили бы свою репутацию. Или вы можете оставить это; поскольку он старый, он может не иметь большого значения.
Эндрю Барбер
55

Подъем - это действие интерпретатора JavaScript по перемещению всех объявлений переменных и функций в начало текущей области видимости.

Однако, только фактические декларации подняты. оставляя задания там, где они есть.

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

переменная

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

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777 Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

функция

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • функции, объявленные внутри страницы, поднимаются в верхнюю часть страницы, имеющей глобальный доступ.
  • функции, объявленные внутри функционального блока, поднимаются в верхнюю часть блока.
  • Возвращаемое значение функции по умолчанию: « undefined », значение объявления переменной по умолчанию также «undefined»

    Scope with respect to function-block global. 
    Scope with respect to page undefined | not available.

Объявление функции

function globalAccess() {                                  function globalAccess() {      
}                                  -------------------     }
globalAccess();                    |                 |     function globalAccess() { « Re-Defined / overridden.
localAccess();                     «   Hoisted  As   «         function localAccess() {
function globalAccess() {          |                 |         }
     localAccess();                -------------------         localAccess(); « function accessed with in globalAccess() only.
     function localAccess() {                              }
     }                                                     globalAccess();
}                                                          localAccess(); « ReferenceError as the function is not defined

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

        10;                 « literal
       (10);                « Expression                (10).toString() -> '10'
var a;                      
    a = 10;                 « Expression var              a.toString()  -> '10'
(function invoke() {        « Expression Function
 console.log('Self Invoking');                      (function () {
});                                                               }) () -> 'Self Invoking'

var f; 
    f = function (){        « Expression var Function
    console.log('var Function');                                   f ()  -> 'var Function'
    };

Функция, назначенная переменной Пример:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

var anonymous = function (){
    console.log('anonymous function Expression');
};

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

JavaScript интерпретируется как

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

Вы можете проверить объявление функции, тестирование выражений в разных браузерах, используя jsperf Test Runner


Классы функций конструктора ES5 : объекты функций, созданные с использованием Function.prototype.bind

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

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

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

ArrowFunction : ArrowParameters => ConciseBody,

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
Яши
источник
3
ааа, твой ответ ... разве это не двусмысленно? хорошо написано, хотя +1 для того, чтобы тратить и писать слишком много информации.
датский
40

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

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

введите описание изображения здесь

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

Леон Габан
источник
8
Я бы сказал, что удобство сопровождения является наиболее важным аспектом большинства кода. Производительность важна, но в большинстве случаев IO может стать более узким местом, чем то, как вы определяете свои функции. Однако есть некоторые проблемы, когда вам нужен каждый бит производительности, который вы можете получить, и это полезно в этих случаях. Также хорошо иметь здесь ответ, который четко определяет четко определенную часть вопроса.
Ричард Гарсайд
3
Ну, я обнаружил, что с Firefox все по-другому. jsperf.com/sandytest
Сандип Наяк
Просто обновление, так как теперь у меня полностью функциональный стиль программирования в JavaScript, я никогда не использую объявления, только выражения функций, чтобы я мог связывать и вызывать свои функции по именам переменных. Проверьте RamdaJS ...
Леон Габан
1
@SandeepNayak Я просто запустил свой собственный тест в Firefox 50.0.0 / Windows 7 0.0.0, и он на самом деле такой же, как у Леона. Поэтому, если ваш тест верен, я бы пришел к выводу, что тесты jsperf не являются ориентировочными, и все они зависят от вашего браузера и / или версии ОС, или от конкретного состояния текущего компьютера в данный конкретный момент.
ocramot
33

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

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

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

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

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

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

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

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

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

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.

eljenso
источник
3
У вас есть немного странный способ разместить закрывающие скобки. Вы кодер Python? Похоже, вы пытаетесь сделать Javascript похожим на Python. Боюсь, это сбивает с толку других людей. Если бы мне пришлось поддерживать ваш код JavaScript, я бы сначала пропустил ваш код через автоматический prettyprinter.
Вскоре
1
Отличный пост. «Самовыполняющуюся функцию» или «немедленно вызванное выражение функции» должно быть достаточно легко увидеть, и его предпочтения по стилю не должны отвлекать от его поста, который является точным и в целом суммирует «подъем». +1
Ricalsin
32

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

function abc(){}

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

var abc = function() {};

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

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

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

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

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

sla55er
источник
1
Ваш пример
похож
Основной причиной размещения этого ответа было предоставление ссылки внизу. Это был кусок, который мне не хватало, чтобы полностью понять вышеупомянутый вопрос.
sla55er
1
Это очень круто, что вы хотели поделиться ссылкой. Но ссылки на дополнительную информацию в SO должны быть просто комментарием к вопросу или вашему любимому ответу. Очень неоптимально загромождать длинную и сложную страницу, подобную этой, с повторяющейся информацией, просто чтобы добавить одну полезную ссылку в конце. Нет, вы не получите очки репутации за предоставление ссылки, но вы будете помогать сообществу.
XML
31

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

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

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

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

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
Саша Фирсов
источник
2
Есть тест для подтверждения: blog.firsov.net/2010/01/… Тест производительности JS - область применения и именованные функции - Аналитика
Саша Фирсов
25

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

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

Кафка
источник
Начиная с ES2015 (через шесть с половиной лет после опубликования вашего ответа) обе функции в вопросе названы.
TJ Crowder
25

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

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

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

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

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

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

Херц
источник
25

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

Функциональное выражение:

var foo = function foo() {};

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

function foo() {};

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

Так

function foo() {};

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

var foo = function foo() {};

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

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

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

Скриншот из видео

Рохан
источник
7
Извините, но это неверно - я не знаю, что Крокфорд пытается сказать на этом слайде. Обе функции и объявления переменных всегда поднимаются к вершине своей области видимости. Разница заключается в том, что присваивания переменных (независимо от того, назначаете ли вы их с помощью строки, логического значения или функции) не поднимаются наверх, тогда как тела функций (с помощью объявления функции).
Томас Хейманн
Посмотрите на эти примеры кода: gist.github.com/cyberthom/36603fbc20de8e04fd09
Томас Хейманн,
24

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

  1. Наличие (объем) функции

Следующее работает, потому что function add()ограничено до ближайшего блока:

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

function add(a, b){
  return a + b;
}

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

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function(a, b){
  return a + b;
}

Приведенный выше код по функциональности идентичен приведенному ниже. Обратите внимание, что явное присвоение add = undefinedявляется излишним, потому что простое выполнение var add;точно так же, как var add=undefined.

var add = undefined;

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

add = function(a, b){
  return a + b;
}

Следующее не работает, потому что var add=superseeds function add().

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (функция) .name

Имя функции function thefuncname(){}является thefuncname , когда она объявлена таким образом.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

В противном случае, если функция объявлена ​​как function(){}, функция .name является первой переменной, используемой для хранения функции.

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

Если для функции не заданы переменные, то именем функции является пустая строка ( "").

console.log((function(){}).name === "");

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

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. Представление

В Google V8 и Firefox Spidermonkey может быть несколько микросекундных различий в компиляции JIST, но в конечном итоге результат будет точно таким же. Чтобы доказать это, давайте проверим эффективность JSPerf в микробенчмарках, сравнив скорость двух пустых фрагментов кода. В тестах JSPerf найдены здесь . И тесты jsben.ch находятся здесь . Как вы можете видеть, есть заметная разница, когда их не должно быть. Если вы действительно фанат производительности, как я, то, возможно, стоит попытаться уменьшить количество переменных и функций в области и, в частности, устранить полиморфизм (например, использовать одну и ту же переменную для хранения двух разных типов).

  1. Изменчивость изменчивости

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

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Однако, когда мы используем оператор const, ссылка на переменную становится неизменной. Это означает, что мы не можем присвоить новое значение переменной. Обратите внимание, однако, что это не делает содержимое переменной неизменным: если вы это сделаете const arr = [], вы все равно можете это сделать arr[10] = "example". Только делать что-то вроде arr = "new value"или arr = []будет выдавать ошибку, как показано ниже.

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Интересно, что если мы объявим переменную как function funcName(){}, то неизменность переменной будет такой же, как и при объявлении переменной var.

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Что такое «Ближайший блок»

«Ближайший блок» является ближайшей «функцией» (включая асинхронные функции, функции генератора и асинхронные функции генератора). Тем не менее, что интересно, a function functionName() {}ведет себя как var functionName = function() {}когда находится в незамкнутом блоке для элементов за пределами указанного замыкания. Обратите внимание.

  • Нормальный var add=function(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}');
  }
} catch(e) {
  console.log("Is a block");
}
var add=function(a, b){return a + b}

  • Нормальный function add(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
function add(a, b){
  return a + b;
}

  • функция

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(function () {
    function add(a, b){
      return a + b;
    }
})();

  • Заявление (такой , как if, else, for, while, try/ catch/ finally, switch, do/ while, with)

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
{
    function add(a, b){
      return a + b;
    }
}

  • Функция стрелки с var add=function()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    var add=function(a, b){
      return a + b;
    }
})();

  • Функция стрелки с function add()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    function add(a, b){
      return a + b;
    }
})();

Джек Гиффин
источник
Это заслуживает того, чтобы быть принятым и наиболее одобренным ответом
Аарон Джон Сабу
18

@EugeneLazutkin приводит пример, где он называет назначенную функцию, которую можно использоватьshortcut() как внутреннюю ссылку на себя. Джон Резиг приводит другой пример - копирование рекурсивной функции, назначенной другому объекту, в своем обучающем расширенном 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." );
Джоэл Пурра
источник
17

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

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.

Инго Кегель
источник
Имя в первом случае не будет определено, потому что его анонимная функция назначена переменной. Я думаю, что слово «анонимный» было придумано для вещей, для которых не определено их имя :)
Ом Шанкар
В этом примере two = new становится глобальной функцией, потому что нет var
Waqas Tahir
16

Первый (функция 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}

NullPoiиteя
источник
16

Я перечисляю различия ниже:

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

    Взгляните на функцию ниже:

    function outerFunction() {
        function foo() {
           return 1;
        }
        return foo();
        function foo() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 2

    Это потому, что во время исполнения это выглядит так:

    function foo() {  // The first function declaration is moved to top
        return 1;
    }
    function foo() {  // The second function declaration is moved to top
        return 2;
    }
    function outerFunction() {
        return foo();
    }
    alert(outerFunction()); //So executing from top to bottom,
                            //the last foo() returns 2 which gets displayed

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

    Та же функция, используя выражения функций:

    function outerFunction() {
        var foo = function() {
           return 1;
        }
        return foo();
        var foo = function() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 1

    Это потому, что во время исполнения это выглядит так:

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. Не безопасно писать объявления функций в нефункциональных блоках, как будто, потому что они не будут доступны.

    if (test) {
        function x() { doSomething(); }
    }
  3. Выражение именованной функции, подобное приведенному ниже, может не работать в браузерах Internet Explorer до версии 9.

    var today = function today() {return new Date()}
варна
источник
1
@Arjun В чем проблема, если вопрос задавался годами раньше? Ответ не только приносит пользу OP, но и потенциально всем пользователям SO, независимо от того, когда был задан вопрос. А что плохого в ответах на вопросы, на которые уже есть принятый ответ?
SantiBailors
1
@ Арджун, ты должен понять, что отвечать на старые вопросы неплохо. Если бы это было так, у СО был бы такой барьер. Представьте, что есть изменение в API (хотя и не в контексте этого вопроса), и кто-то замечает это и дает ответ с новым API, разве это не должно быть разрешено ?? До тех пор, пока ответ не имеет смысла и не относится к нему, он будет отклонен и удален автоматически. Вам не нужно беспокоиться об этом !!!!
Судхансу Чоудхари
15

Если бы вы использовали эти функции для создания объектов, вы бы получили:

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
Павел Фурманиак
источник
Я не могу воспроизвести это. console.log(objectOne.__proto__);печатает "functionOne {}" в моей консоли. Любые идеи о том, почему это может иметь место?
Майк
Я не могу воспроизвести это также.
daremkd
1
Это возможность вашего отладчика (отображать «класс» зарегистрированного объекта), и большинство из них способны получить имя даже для выражений анонимных функций в наши дни. Кстати, вы должны пояснить, что между этими двумя экземплярами нет функциональной разницы.
Берги
12

В свете аргумента «именованные функции отображаются в следах стека» современные движки JavaScript на самом деле вполне способны представлять анонимные функции.

На момент написания этой статьи V8, SpiderMonkey, Chakra и Nitro всегда ссылаются на именованные функции по именам. Они почти всегда ссылаются на анонимную функцию по ее идентификатору, если она есть.

SpiderMonkey может выяснить имя анонимной функции, возвращаемой другой функцией. Остальные не могут.

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

[].forEach(function iterator() {});

Но по большей части это не стоит переоценивать.

Жгут ( Скрипка )

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

SpiderMonkey

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

чакра

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

Nitro

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
Джексон
источник
12

В JavaScript есть два способа создания функций:

  1. Объявление функции:

    function fn(){
      console.log("Hello");
    }
    fn();

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

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

  2. Функциональное выражение:

    var fn=function(){
      console.log("Hello");
    }
    fn();

    JavaScript имеет первоклассные функции, то есть создает функцию и присваивает ее переменной так же, как вы создаете строку или число и присваиваете ее переменной. Здесь переменная fn назначается функции. Причиной этого понятия являются функции в JavaScript; fn указывает на экземпляр объекта вышеуказанной функции. Мы инициализировали функцию и присвоили ее переменной. Это не выполнение функции и присвоение результата.

Ссылка: Синтаксис объявления функции JavaScript: var fn = function () {} против функции fn () {}

Anoop Rai
источник
1
а как насчет третьего варианта var fn = function fn() {...}?
Чарви
Привет, Чхарви, не уверен насчет твоего вопроса, я полагаю, ты говоришь о выражении функций, о котором я уже упоминал. Однако, если все еще есть некоторая путаница, просто будьте более сложными.
Anoop Rai
да, я спрашивал о выражении именованной функции. это похоже на ваш вариант № 2 за исключением того, что функция имеет идентификатор. обычно этот идентификатор совпадает с переменной, которой он назначается, но это не всегда так.
Чарви
1
Да Выражение именованной функции аналогично моему варианту №2. Хорошо иметь идентификатор не обязательно, так как он не используется. Всякий раз, когда вы будете выполнять выражение функции, вы будете использовать переменную, содержащую объект функции. Идентификатор не имеет смысла.
Anoop Rai
11

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

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

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

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

Во второй строке мы присваиваем ссылку на анонимную функцию для functionOne.

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

functionOne();
function functionOne() {
   // Some code
}
Nitin9791
источник
11

О производительности:

Новые версии V8представили несколько скрытых оптимизаций, что и сделали SpiderMonkey.

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

Chrome 62.0.3202 Chrome test

FireFox 55 Тест Firefox

Хром Канарейка 63.0.3225 Chrome Canary test


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


Firefox Chrome Канарейка ХромFirefox named_anonymous Хром канарейка named_anonymous Chrome named_anonymous

Панос Кал.
источник
1
Да, эта разница настолько незначительна, что, надеюсь, разработчики будут заботиться о том, какой подход более приемлем для их конкретных нужд, а не какой может быть быстрее (вы будете получать разные результаты jsperf при каждой попытке в зависимости от того, что делает браузер - большинству задач javascript не обязательно заниматься микрооптимизацией до такой степени).
Кальмар
@squidbe Разницы нет. Смотрите здесь: jsperf.com/empty-tests-performance
Джек Гиффин
10

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

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

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

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

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

2. Объявление функций

function functionTwo() {
    // Some code
}

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

Это нормальный способ вызова функции в JavaScript, эта функция может быть вызвана еще до того, как вы объявите ее, поскольку в JavaScript все функции получают Hoisted, но если вы используете 'строгий', это не будет Hoist, как ожидалось, это хороший способ вызывать все нормальные функции, которые не большие по строкам и не являются функциями-конструкторами.

Кроме того, если вам нужна дополнительная информация о том, как работает подъем в JavaScript, перейдите по ссылке ниже:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

Алиреза
источник
1
...also this way of declaring is a better way to create Constructor functions in JavaScriptПрошу уточнить, мне любопытно!
Карл Моррисон
Одна из причин этого заключается в том, что все встроенные функции-конструкторы в JavaScript созданы как эта функция Number () {[native code]}, и вас не следует путать со встроенными, в дальнейшем ссылки в этом случае безопаснее, и вы заканчиваете до аккуратным кода , но не с помощью подъемно ...
Реза
8

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

Tao
источник
7

new Function()может использоваться для передачи тела функции в строку. И, следовательно, это может быть использовано для создания динамических функций. Также передача сценария без выполнения сценария.

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()
SuperNova
источник
Хотя это хорошо и верно, как именно это относится к задаче?
Джек Гиффин