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

6687

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

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

Двумя способами являются:

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

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

  • 188
    permadi.com/tutorial/jsFunc/index.html - очень хорошая страница о функциях JavaScript
  • 63
    С этим связана отличная статья о выражениях именованных функций .
Показать ещё 19 комментариев
Теги:
function
syntax
idioms

37 ответов

4486
Лучший ответ

Разница в том, что 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 действие, и в этом случае он просто вызывает ошибку.

  • 1
    Я проверил вашу первую точку (время компиляции и время выполнения) в Firefox и Google Chrome, и она кажется верной. Но ваше второе замечание о сфере видимости кажется некорректным: functionTwo тоже ограничена .
  • 1
    @ Джио Борхе: Точно так же. Это Javascript. Как вы думаете, почему это будет иметь значение ГДЕ это работает?
Показать ещё 26 комментариев
1766

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

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

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

var xyz = function abc(){};

xyz будет определяться как обычно, abc - undefined во всех браузерах, но 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"

Одной из убедительных причин использования комбинированного стиля является атрибут "name" для объектов функций (не поддерживается 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() — Ответ: Это зависит от браузера.

  • 66
    Я имею в виду Роборг, но его нигде не найти. Просто: Роборг === Грег. Вот как история может быть переписана в эпоху Интернета. ;-)
  • 10
    var xyz = function abc () {}; console.log (xyz === abc); Все браузеры, которые я тестировал (Safari 4, Firefox 3.5.5, Opera 10.10), выдают «Неопределенная переменная: abc».
Показать ещё 16 комментариев
613

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

Термины:

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

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

  • "Анонимная" function Expression (которая, несмотря на термин, иногда создает функции с именами)

  • Именованная function Expression

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

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

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

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

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

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

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

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

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

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

x(); // Works even though it 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 not in the same block)

Выражение "анонимная" function

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

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

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

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

Именованная function Expression

Третья форма - это выражение с именованной функцией ("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.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 для getFullName, которое является функцией, назначенной для Person.prototype.

  • 3
    тогда имя w просто игнорируется?
  • 0
    это предназначено для документации / комментария кода, где имя дает подсказку о том, что делает функция?
Показать ещё 8 комментариев
132

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

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

 alert(typeof foo); // 'function', it 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 в Strict Mode.

A должен читать:

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

  • 0
    Я не знал, что функции могут быть перезаписаны в JavaScript! Кроме того, этот порядок разбора является для меня большой точкой продажи. Я думаю, мне нужно посмотреть, как я создаю функции.
  • 2
    +0 к статье "Демистифицированные выражения функций имен", поскольку это 404ing. Возможное зеркало ?: kangax.github.com/nfe
Показать ещё 2 комментария
117

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

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

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

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

Дополнительная техническая информация

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

  • В вашем первом фрагменте показано выражение функции. Это связано с использованием оператора "function" для создания функции - результат этого оператора может быть сохранен в любой переменной или объекте. Выражение функции является таким мощным. Выражение функции часто называют "анонимной функцией", поскольку оно не должно иметь имени,
  • Второй пример - объявление функции . Для создания функции используется оператор "function". Функция предоставляется во время разбора и может быть вызвана в любом месте этой области. Вы можете сохранить его позже в переменной или объекте.
  • Третий способ определения функции - конструктор "Function()" , который не показан в исходном сообщении. Не рекомендуется использовать это, поскольку он работает так же, как eval(), у которого есть свои проблемы.
94

Лучшее объяснение Greg answer

functionTwo();
function functionTwo() {
}

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

Потому что:

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

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

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

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

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

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

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

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

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

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

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

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

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

С

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, а затем назначить ее позже, условно.
60

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

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

}

или

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

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

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

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

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

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

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

Variable

Javascript называется свободно типизированным языком. Это означает, что переменные Javascript могут содержать значение любого Data-Type. 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 ', Variable значение по умолчанию объявления также '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 за тратить и писать слишком много информации.
36

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

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

Изображение 1992

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

  • 7
    Я бы сказал, что удобство сопровождения является наиболее важным аспектом большинства кода. Производительность важна, но в большинстве случаев IO, вероятно, будет более узким местом, чем то, как вы определяете свои функции. Однако есть некоторые проблемы, когда вам нужен каждый бит производительности, который вы можете получить, и это полезно в этих случаях. Также хорошо иметь здесь ответ, который четко определяет четко определенную часть вопроса.
  • 3
    Ну, я обнаружил, что с Firefox все по-другому. jsperf.com/sandytest
Показать ещё 3 комментария
31

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

Однако существует разница в том, как и когда объект функции фактически связан с его переменной. Это различие связано с механизмом, называемым переменным подъемом в 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.

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

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

function abc(){}

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

var abc = function() {};

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

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

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

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

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

  • 1
    Ваш пример вроде того же, что и верхний ответ
  • 0
    Основной причиной размещения этого ответа было предоставление ссылки внизу. Это был кусок, который мне не хватало, чтобы полностью понять вышеупомянутый вопрос.
Показать ещё 1 комментарий
29

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

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

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

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

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

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

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

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

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

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

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

23

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

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

var foo = function foo() {};

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

function foo() {};

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

Итак,

function foo() {};

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

var foo = function foo() {};

Расширяется далее:

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

И они оба подняты в верхней части кода.

Изображение 1993

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

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

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

18

@EugeneLazutkin дает пример, где он называет назначенную функцию, чтобы иметь возможность использовать shortcut() как внутренняя ссылка на себя. John Resig дает еще один пример - копирование рекурсивной функции, назначенной другому объекту в Изучение расширенного 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." );
  • 0
    Просто вызов this.yell тоже работает :)
16

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

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.

  • 0
    Имя в первом случае не будет определено, потому что его анонимная функция назначена переменной. Я думаю, что слово «анонимный» было придумано для вещей, которые не имеют своего имени :)
  • 0
    В этом примере two = new становится глобальной функцией, потому что нет переменной
14

Я перечисляю следующие различия:

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

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

    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
    

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

    Те же функции, что и функциональные выражения:

    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()}
    
  • 1
    @Arjun В чем проблема, если вопрос задавался годами раньше? Ответ полезен не только ОП, но и всем пользователям СО, независимо от того, когда был задан вопрос. А что плохого в ответах на вопросы, на которые уже есть принятый ответ?
  • 1
    @ Арджун, ты должен понять, что отвечать на старые вопросы неплохо. Если бы это было так, у СО был бы такой барьер. Представьте, что есть изменение в API (хотя и не в контексте этого вопроса), и кто-то замечает это и дает ответ с новым API, разве это не должно быть разрешено ?? До тех пор, пока ответ не имеет смысла и не относится к нему, он будет автоматически отклонен и удален. Вам не нужно беспокоиться об этом !!!!
14

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

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
  • 0
    Я не могу воспроизвести это. console.log(objectOne.__proto__); печатает "functionOne {}" в моей консоли. Любые идеи о том, почему это может иметь место?
  • 0
    Я не могу воспроизвести это также.
Показать ещё 1 комментарий
14

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

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

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

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

function foo() {
    return 3;
}

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

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

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

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

// 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}

11

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

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

Следующее работает, потому что 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;
}
  1. (Функция).name

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

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

В противном случае, если функция объявлена как function(){}, 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);
})();

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

"Ближайший блок" является ближайшей "функцией" (включая асинхронные функции, функции генератора и асинхронные функции генератора). Однако, что интересно, 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;
    }
})();
11

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

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

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

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

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

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

Жгут проводов (Fiddle)

'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

Chakra

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)

Нитро

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
10

В 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() {} vs function fn() {}

  • 1
    как насчет третьего варианта, var fn = function fn() {...} ?
  • 0
    Привет, Чхарви, не уверен насчет твоего вопроса, думаю, ты говоришь о выражении функций, о котором я уже упоминал. Однако, если все еще есть некоторая путаница, просто будьте более сложными.
Показать ещё 2 комментария
9

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

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

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

Chrome 62.0.3202 Изображение 1994

FireFox 55 Изображение 1995

Chrome Canary 63.0.3225 Изображение 1996


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


Firefox Изображение 1997 Chrome Canary Изображение 1998 Chrome Изображение 1999

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

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

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

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

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

Во второй строке мы назначаем функцию анонимной функции functionOne.

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

functionOne();
function functionOne() {
   // Some code
}
8

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

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

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

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

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

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

function functionTwo() {
    // Some code
}

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

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

Кроме того, если вам нужна дополнительная информация о том, как работает подъем в 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 , пожалуйста, уточните, мне интересно!
  • 0
    Одна из причин заключается в том, что все встроенные функции-конструкторы в JavaScript созданы как эта функция Number () {[native code]}, и вас не следует путать со встроенными, в дальнейшем ссылки в этом случае безопаснее, и вы заканчиваете до аккуратного кода, но не используя подъем ...
6

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

  • 0
    Пожалуйста, разработайте и предоставьте рабочие фрагменты кода
5

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

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

secondFunction()
  • 0
    Хотя это хорошо и верно, как именно это относится к задаче?
2

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

var getRectArea = function(width, height) {
    return width * height;
};

console.log("Area of Rectangle: " + getRectArea(3,4));
// This should return the following result in the console: 
// Area of Rectangle: 12

Это называется объявлением функции:

var w = 5;
var h = 6;

function RectArea(width, height) {  //declaring the function
  return area = width * height;
}                                   //note you do not need ; after }

RectArea(w,h);                      //calling or executing the function
console.log("Area of Rectangle: " + area);
// This should return the following result in the console: 
// Area of Rectangle: 30

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

  • 0
    На что я смотрю? Я не думаю, что это объясняет вопрос, кроме того, что вы понимаете, что сделали. Во втором примере единственная причина, по которой вы можете получить доступ к переменной 'area', заключается в том, что вы не объявили ее, поэтому она зарегистрирована в window.area вместо.
1

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

Именованные функции против Анонимные функции

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

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

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

  • Self-Reference: что если функция должна ссылаться на себя, например, для рекурсии.

Выражение функции именования:

Предоставление имени для выражения вашей функции довольно эффективно устраняет все эти недостатки и не имеет ощутимых недостатков. Лучше всего всегда называть выражения вашей функции:

setTimeout(function timeHandler() { // <-- look, a name here!
  console.log("I've waited 1 second");
}, 1000);

Именование IIFE (выражение для немедленной вызванной функции):

(function IIFE(str) { // <-- look, always name IIFEs!
  console.log(str); // "Hello!"
})('Hello!');

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

1

Один важный момент, на который следует обратить внимание:

пусть есть две функции: -

sum(1,2);

const sum = function(first, second) {
  return first + second;
}

В приведенном выше случае это даст ошибку, что сумма не определена, но

sum(1,2);

function sum(first, second) {
  return first + second;
}

Эта функция не будет какая - либо ошибка, как Подъемно будет иметь место в данном случае.

1

Выражение в JS: то, что возвращает значение
Пример. Попробуйте выполнить в консоли хром:

a = 10
output : 10

(1 + 3)
output = 4

Декларация/Заявление: то, что не возвращает значение
Пример:

if (1 > 2) {
 // do something. 
}

здесь (1> 2) является выражением, но "если" statament нет. Его ничего не возвращает.


Аналогично, у нас есть функция Объявление/Выражение vs Выражение функции
Давайте возьмем пример:

// test.js

var a = 10;

// function expression
var fun_expression = function() {
   console.log("Running function Expression");
}

// funciton expression

function fun_declaration() {
   console.log("Running function Statement");
}

Важно. Что происходит, когда JavaScript-движки запускают указанный выше js файл.

  • Когда это будет выполняться js, произойдет следующее:

    1. Память будет создана переменной 'a' и 'fun_expression'. И память будет создана для выражения функции fun_declaration
    2. "a" будет присвоен "undefined". "fun_expression" будет присвоен "undefined". "fun_declaration" будет в памяти полностью.
      Примечание. Этапы 1 и 2 выше называются "Контекст выполнения - Фаза создания".

Теперь предположим, что мы обновляем js до.

// test.js

console.log(a)  //output: udefined (No error)
console.log(fun_expression)  // output: undefined (No error)
console.log(fun_expression()) // output: Error. As we trying to invoke undefined. 
console.log(fun_declaration()) // output: running function statement  (As fun_declaration is already hoisted in the memory). 

var a = 10;

// function expression
var fun_expression = function() {
   console.log('Running function expression')
}

// function declaration

function fun_declaration() {
   console.log('running function declaration')
}

console.log(a)   // output: 10
console.log(fun_expression()) //output: Running function expression
console.log(fun_declaration()) //output: running function declaration

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

1

Я предпочитаю определять функцию как переменную:

let first = function(x){
   return x[0];
}

Вместо:

function first(){
    ....
}

Потому что я могу использовать выражения и декораторы при определении функции. Например:

let safe = function(f){
  try {f()...}
}
let last = safe(function(x){return x[0]}).

Также с ES6 его намного короче:

 let last = x => x[0]
 ...........
 function last(x){
     return x[0];
 }
......

let last = safe(x => x[0]);
  • 0
    Я не вижу, как этот стиль кодирования предлагает наименьшую производительность.
0

Еще одно различие между обеими функциями заключается в том, что functionOne можно использовать как переменную, которая может содержать несколько функций внутри, а functionTwo содержит некоторый блок кода, который выполняется все при вызове. Пожалуйста, проверьте ниже:

   var functionOne = (function() {
      return {

         sayHello: function(){
                console.log('say hello')

         },
         redirectPage:function(_url){
                window.location.href = _url;
         }

      }
})();

У вас есть выбор, какую функцию вызывать. например functionOne.sayHello или functionOne. redirectPage. И если вы вызовете functionTwo, тогда будет выполнен весь блок кода.

-1

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

функции в условных выражениях

Общая практика, рекомендованная MDN, заключается в использовании функциональных выражений вместо объявлений внутри операторов if. Из примера в ссылке, объявления функций внутри, if операторы будут вести себя одинаково в Chrome и Firefox, но не в Safari.

Отказ от ответственности: MacOS не может запустить Microsoft Edge, поэтому я не могу проверить.

// function declaration example

var hoisted = "foo" in this;

console.log(''foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}');

if (false) {
  function foo() {
    return 1;
  }
}

// In Chrome: 
// 'foo' name is hoisted. typeof foo is undefined

// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined

// In Safari:
// 'foo' name is hoisted. typeof foo is function

Преобразование foo в выражение функции даст согласованные результаты во всех трех веб-браузерах.

// function expression example

var hoisted = "foo" in this;

console.log(''foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}');

if (false) {
  var foo = function() {
    return 1;
  }
}

// In Chrome: 
// 'foo' name is hoisted. typeof foo is undefined

// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined

// In Safari:
// 'foo' name is hoisted. typeof foo is undefined
  • 0
    Посмотрите таблицу совместимости браузера для деталей

Ещё вопросы

Сообщество Overcoder
Наверх
Меню