var functionName = function() {} против функции functionName() {}
Недавно я начал поддерживать чужой код JavaScript. Я исправляю ошибки, добавляю функции, а также пытаюсь привести код в порядок и сделать его более согласованным.
Предыдущий разработчик использовал два способа объявления функций, и я не могу понять, есть ли причина этого или нет.
Два способа:
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
Каковы причины использования этих двух разных методов и каковы плюсы и минусы каждого? Есть ли что-нибудь, что можно сделать одним методом, но нельзя сделать другим?
42 ответа
Разница в том, что functionOne
является выражением функции и поэтому определяется только при достижении этой строки, тогда как functionTwo
является объявлением функции и определяется, как только его окружающая функция или скрипт выполняется (из-за подъема).
Например, выражение функции:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
И объявление функции:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
Это также означает, что вы не можете условно определять функции, используя объявления функций:
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
Выше фактически определяет functionThree
независимо от test
ценность - если use strict
действует, и в этом случае просто возникает ошибка.
Сначала я хочу исправить Грег: 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()
- ответ: это зависит от браузера.
Вот краткое изложение стандартных форм, которые создают функции: (Первоначально написано для другого вопроса, но адаптировано после перехода в канонический вопрос.)
Термины:
- ES5: ECMAScript 5-е издание, 2009
- ES2015: ECMAScript 2015 (также известный как "ES6")
Быстрый список:
Объявление функции
"Anonymous"
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, в спецификации сказано, что делать. Фактически, это дает три отдельных действия:
- Если в свободном режиме нет в веб-браузере, движок JavaScript должен делать одно
- Если в свободном режиме в веб-браузере, движок JavaScript должен делать что-то еще
- Если в строгом режиме (браузер или нет), движок 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)
"Anonymous" 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()
вызов? Это функция.
Несколько вещей о функциях стрелок:
У них нет своего
this
, Вместо этого они закрываютthis
контекста, где они определены. (Они также закрываютarguments
и, в случае необходимости,super
.) Это означает, чтоthis
внутри них так же, какthis
где они созданы, и не могут быть изменены.Как вы уже заметили, вы не используете ключевое слово
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() {
}
};
Объявления конструктора и метода в 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
,
Говоря о глобальном контексте, оба 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
имеет место до времени выполнения.
Глобальное свойство, созданное FunctionDeclaration
может быть перезаписано без проблем, как значение переменной, например:
function test () {}
test = null;
Другое очевидное различие между вашими двумя примерами состоит в том, что первая функция не имеет имени, но вторая имеет его, что может быть очень полезно при отладке (то есть проверке стека вызовов).
О вашем отредактированном первом примере (foo = function() { alert('hello!'); };
), это незадекларированное задание, я настоятельно рекомендую вам всегда использовать var
ключевое слово.
С заданием, без var
оператор, если указанный идентификатор не найден в цепочке областей действия, он станет удаляемым свойством глобального объекта.
Кроме того, незадекларированные ReferenceError
на ECMAScript 5 в строгом режиме.
Должен читать:
Примечание. Этот ответ был объединен с другим вопросом, в котором основным сомнением и неправильным представлением ФП было то, что идентификаторы, объявленные с FunctionDeclaration
, не может быть перезаписано, что не так.
Два фрагмента кода, которые вы разместили там, будут практически для всех целей вести себя одинаково.
Однако разница в поведении заключается в том, что с первым вариантом (var functionOne = function() {}
), эта функция может быть вызвана только после этой точки в коде.
Со вторым вариантом (function functionTwo()
), функция доступна для кода, который запускается выше, где функция объявлена.
Это связано с тем, что в первом варианте функция назначается переменной foo
во время выполнения. Во втором функция назначается этому идентификатору, foo
на время разбора.
Больше технической информации
В JavaScript есть три способа определения функций.
- Ваш первый фрагмент показывает выражение функции. Это включает использование оператора "function" для создания функции - результат этого оператора может быть сохранен в любой переменной или свойстве объекта. Таким образом, выражение функции является мощным. Выражение функции часто называют "анонимной функцией", потому что оно не обязательно должно иметь имя,
- Ваш второй пример - объявление функции. При этом используется оператор "function" для создания функции. Функция становится доступной во время анализа и может вызываться в любом месте этой области. Вы все еще можете сохранить его в переменной или свойстве объекта позже.
- Третий способ определения функции - это конструктор "Function()", который не показан в исходном посте. Не рекомендуется использовать это, так как он работает так же, как
eval()
, которая имеет свои проблемы.
Лучшее объяснение ответа Грега
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();
---------------
Другие комментаторы уже рассмотрели семантическое различие двух вариантов выше. Я хотел бы отметить стилистическую разницу: только вариант "присваивания" может устанавливать свойство другого объекта.
Я часто строю модули JavaScript с таким шаблоном:
(function(){
var exports = {};
function privateUtil() {
...
}
exports.publicUtil = function() {
...
};
return exports;
})();
С этим шаблоном все ваши публичные функции будут использовать присваивание, в то время как ваши частные функции будут использовать объявление.
(Обратите также внимание, что для присваивания требуется точка с запятой после оператора, в то время как объявление запрещает это.)
Иллюстрацией того, когда следует предпочесть первый метод второму, является случай, когда вам нужно избежать переопределения предыдущих определений функции.
С
if (condition){
function myfunction(){
// Some code
}
}
это определение myfunction
переопределит любое предыдущее определение, так как это будет сделано во время анализа.
В то время как
if (condition){
var myfunction = function (){
// Some code
}
}
делает правильную работу по определению myfunction
только когда condition
встретил
Важной причиной является добавление одной и только одной переменной в качестве "корня" вашего пространства имен...
var MyNamespace = {}
MyNamespace.foo= function() {
}
или же
var MyNamespace = {
foo: function() {
},
...
}
Есть много методов для пространства имен. Это становится более важным с множеством доступных модулей JavaScript.
Подъем - это действие интерпретатора 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 появилась функция Arrow: выражение функции стрелки имеет более короткий синтаксис, они лучше всего подходят для функций, не относящихся к методам, и их нельзя использовать в качестве конструкторов.
ArrowFunction : ArrowParameters => ConciseBody
,const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; }; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd
Существует четыре заслуживающих внимания сравнения между двумя различными объявлениями функций, перечисленными ниже.
- Наличие (объем) функции
Следующее работает, потому что function add()
находится в пределах ближайшего блока:
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
function add(a, b){
return a + b;
}
Следующее не работает (потому что var add=
заменяет function add()
).
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=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;
}
- (функция).name
Название функции function 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);
- Спектакль
В Google V8 и Firefox Spidermonkey может быть разница в несколько микросекундных JIST-компиляций, но в итоге результат будет точно таким же. Чтобы доказать это, давайте рассмотрим эффективность JSPerf в микробенчмарках, сравнив скорость двух пустых фрагментов кода. Тесты JSPerf находятся здесь. И тесты jsben.ch находятся здесь. Как видите, есть заметная разница, когда их не должно быть. Если вы действительно фанат производительности, как я, то, возможно, вам стоит попытаться уменьшить количество переменных и функций в области и, в частности, устранить полиморфизм (например, использовать одну и ту же переменную для хранения двух разных типов).
- Изменчивость изменчивости
Когда вы используете 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);
})();
Что такое "Ближайший блок"
"Ближайший блок" является ближайшей "функцией" (включая асинхронные функции, функции генератора и асинхронные функции генератора). Однако, интересно, 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;
}
})();
Я добавляю свой собственный ответ только потому, что все остальные подробно рассмотрели подъемную часть.
Я долго думал, какой путь лучше, и теперь я знаю, что благодаря http://jsperf.com/:)
Объявления функций быстрее, и вот что действительно имеет значение в Web Dev, верно?;)
Объявление функции и выражение функции, присвоенные переменной, ведут себя одинаково после установления привязки.
Однако есть разница в том, как и когда объект функции действительно связан с его переменной. Это различие связано с механизмом, называемым подъемом переменных в 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
Сфера не играет никакой роли и остается нетронутой.
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
,
Первый пример - объявление функции:
function abc(){}
Второй пример - это выражение функции:
var abc = function() {};
Основное отличие заключается в том, как они поднимаются (поднимаются и декларируются). В первом примере, объявление целиком поднято. Во втором примере поднимается только переменная 'abc', ее значение (функция) будет неопределенным, а сама функция останется в той позиции, в которой она объявлена.
Проще говоря:
//this will work
abc(param);
function abc(){}
//this would fail
abc(param);
var abc = function() {}
Чтобы узнать больше об этой теме, я настоятельно рекомендую вам эту ссылку
С точки зрения стоимости обслуживания кода, именованные функции являются более предпочтительными:
- Независимо от места, где они объявлены (но все еще ограничены областью действия).
- Более устойчивы к ошибкам, таким как условная инициализация (вы все равно можете переопределить, если хотите).
- Код становится более читабельным благодаря выделению локальных функций отдельно от функциональности области. Обычно в области действия сначала идет функциональность, за которой следуют объявления локальных функций.
- В отладчике вы четко увидите имя функции в стеке вызовов вместо "анонимной / оцененной" функции.
Я подозреваю, что больше PROS для именованных функций следуют. И то, что перечислено как преимущество именованных функций, является недостатком для анонимных.
Исторически анонимные функции возникали из-за невозможности JavaScript как языка перечислять членов с именованными функциями:
{
member:function() { /* How do I make "this.member" a named function? */
}
}
В терминах информатики мы говорим об анонимных функциях и именованных функциях. Я думаю, что наиболее важным отличием является то, что анонимная функция не связана с именем, следовательно, имя анонимной функции. В JavaScript это объект первого класса, динамически объявленный во время выполнения.
Для получения дополнительной информации об анонимных функциях и лямбда-исчислении Википедия является хорошим началом ( http://en.wikipedia.org/wiki/Anonymous_function).
Ответ Грега достаточно хорош, но я все же хотел бы добавить к нему кое-что, чему я научился только сейчас, смотря видеофильмы Дугласа Крокфорда.
Функциональное выражение:
var foo = function foo() {};
Функциональное утверждение:
function foo() {};
Оператор функции - это просто сокращение для var
заявление с function
значение.
Так
function foo() {};
расширяется до
var foo = function foo() {};
Что расширяется дальше:
var foo = undefined;
foo = function foo() {};
И они оба поднимаются на вершину кода.
Я использую переменный подход в своем коде по очень конкретной причине, теория которой была абстрактно рассмотрена выше, но пример может помочь некоторым людям, таким как я, с ограниченным опытом работы с JavaScript.
У меня есть код, который мне нужно запустить с 160 независимо разработанными брендами. Большая часть кода находится в общих файлах, но специфические для брендинга вещи находятся в отдельном файле, по одному для каждого брендинга.
Некоторые марки требуют определенных функций, а некоторые нет. Иногда мне нужно добавлять новые функции, чтобы делать новые вещи, специфичные для брендинга. Я рад изменить код общего доступа, но мне не нужно менять все 160 наборов фирменных файлов.
Используя синтаксис переменной, я могу объявить переменную (по существу, указатель на функцию) в общем коде и либо назначить тривиальную функцию-заглушку, либо установить значение null.
Одна или две маркировки, которые нуждаются в конкретной реализации функции, могут затем определить свою версию функции и назначить ее переменной, если они хотят, а остальные ничего не делают. Я могу проверить нулевую функцию, прежде чем выполнить ее в общем коде.
Судя по комментариям людей выше, я понимаю, что, возможно, можно также переопределить статическую функцию, но я думаю, что решение с переменной является хорошим и понятным.
Eugene Lazutkin приводит пример, где он называет назначенную функцию, которую можно использовать 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." );
Другое отличие, которое не упоминается в других ответах, заключается в том, что если вы используете анонимную функцию
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
,
Первый (функция doSomething(x)) должен быть частью нотации объекта.
Второй (var doSomething = function(x){ alert(x);}
) просто создает анонимную функцию и присваивает ее переменной, doSomething
, Так что doSomething() вызовет функцию.
Возможно, вы захотите узнать, что такое объявление функции и выражение функции.
Объявление функции определяет именованную переменную функции без необходимости назначения переменной. Объявления функций выполняются как отдельные конструкции и не могут быть вложены в нефункциональные блоки.
function foo() {
return 3;
}
ECMA 5 (13.0) определяет синтаксис как
Идентификатор функции (FormalParameterListopt) {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 (FormalParameterListopt) {FunctionBody}
Я перечисляю различия ниже:
Объявление функции может быть размещено в любом месте кода. Даже если оно вызывается до того, как определение появляется в коде, оно выполняется, когда объявление функции фиксируется в памяти или каким-либо образом, до того, как любой другой код на странице начнет выполнение.
Посмотрите на функцию ниже:
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
Не безопасно писать объявления функций в нефункциональных блоках, как будто, потому что они не будут доступны.
if (test) { function x() { doSomething(); } }
Выражение именованной функции, подобное приведенному ниже, может не работать в браузерах Internet Explorer до версии 9.
var today = function today() {return new Date()}
О производительности:
Новые версии V8
представил несколько скрытых оптимизаций и так же SpiderMonkey
,
Теперь почти нет разницы между выражением и объявлением.
Выражение функции теперь выглядит быстрее.
Anonymous
выражения функций, кажется, имеют лучшую производительность противNamed
выражение функции.
Если бы вы использовали эти функции для создания объектов, вы бы получили:
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
Именованные функции против анонимных функций
Выражения анонимных функций быстро и легко набираются, и многие библиотеки и инструменты поддерживают этот идиоматический стиль кода. Однако у них есть несколько недостатков:
- У анонимных функций нет полезного имени для отображения в следах стека, что может усложнить отладку.
- Без имени, если функция должна ссылаться на себя, для рекурсии и т. Д.
- Анонимные функции опускают имя, которое может вызвать менее читаемый код.
Выражения именования функций
Предоставление имени для выражения вашей функции довольно эффективно устраняет все эти недостатки и не имеет ощутимых недостатков. Лучше всего всегда называть выражения вашей функции:
setTimeout(function timeHandler() { // <-- named function here!
console.log('I've waited 1 second');
}, 1000);
Именование выражения немедленной вызванной функции (IIFE):
var a = 42;
(function IIFE(global) { // <-- named IIFEs!
console.log(global.a); // 42
})(window);
Примечание: для функций, назначенных переменной, наименование функции, в этом случае, не очень распространено и может вызвать путаницу, в этом случае лучше использовать функцию стрелки.
В JavaScript есть два способа создания функций:
Объявление функции:
function fn(){ console.log("Hello"); } fn();
Это очень простой, не требующий пояснений, используемый на многих языках и стандартный для всей семьи языков C. Мы объявили функцию, определили ее и выполнили, вызвав ее.
Что вы должны знать, так это то, что функции на самом деле являются объектами в JavaScript; внутренне мы создали объект для вышеуказанной функции и дали ему имя с именем fn, или ссылка на объект хранится в fn. Функции являются объектами в JavaScript; экземпляр функции на самом деле является экземпляром объекта.
Функциональное выражение:
var fn=function(){ console.log("Hello"); } fn();
JavaScript имеет первоклассные функции, то есть создает функцию и присваивает ее переменной так же, как вы создаете строку или число и присваиваете ее переменной. Здесь переменная fn назначается функции. Причиной этого понятия являются функции в JavaScript; fn указывает на экземпляр объекта вышеуказанной функции. Мы инициализировали функцию и присвоили ее переменной. Это не выполнение функции и присвоение результата.
Ссылка: Синтаксис объявления функции JavaScript: var fn = function() {} против функции fn() {}
В свете аргумента "именованные функции отображаются в следах стека" современные движки 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
Оба являются разными способами определения функции. Разница в том, как браузер интерпретирует и загружает их в контекст выполнения.
Первый случай - это выражения функций, которые загружаются только тогда, когда интерпретатор достигает этой строки кода. Поэтому, если вы сделаете это следующим образом, вы получите ошибку, что functionOne не является функцией.
functionOne();
var functionOne = function() {
// Some code
};
Причина в том, что в первой строке никакое значение не назначено для functionOne, и, следовательно, оно не определено. Мы пытаемся вызвать его как функцию и, следовательно, получаем ошибку.
Во второй строке мы присваиваем ссылку на анонимную функцию для functionOne.
Второй случай - это объявления функций, которые загружаются перед выполнением любого кода. Поэтому, если вам нравится следующее, вы не получите никакой ошибки, поскольку объявление загружается до выполнения кода.
functionOne();
function functionOne() {
// Some code
}
Они очень похожи с некоторыми небольшими отличиями: первая - это переменная, назначенная анонимной функции (объявление функции), а вторая - нормальный способ создания функции в 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, перейдите по ссылке ниже:
Это всего лишь два возможных способа объявления функций, и, во-вторых, вы можете использовать функцию до объявления.