Тайны функциональных выражений в JavaScript

от автора

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

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

function A(){};             // декларация функции var B = function(){};       // функциональное выражение var C = (function(){});     // функциональное выражение с оператором группировки var D = function foo(){};   // именованное функциональное выражение var E = (function(){})();   // самовызывающееся функциональное выражение var F = new Function();     // конструктор функции var G = new function(){};   // вырожденный случай: конструктор объекта 

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

Согласно документации ECMA синтаксис определения функции следующий:

ДекларацияФункции:     function Идентификатор ( Параметры ) { ТелоФункции }  ФункциональноеВыражение:     function Идентификатор (опционально)  ( Параметры ) { ТелоФункции } 

Хоть и выглядят эти определения вполне схожими, но между декларацией функции и функциональным выражением есть большая разница. Декларация функции (Function Declaration) создается до выполнения любого кода, в то время как функциональное выражение (Function Expression) будет создано только в момент, когда интерпретатор дойдёт до данной строки кода.

Функциональное выражение — это объявление функции в контексте какого-либо выражения.

Рассмотрим несколько примеров функциональных выражений:

Оператор присваивания

var a = function() {}; 

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

var a = function(){return 1}() + 12; // 13 var b = function(){return 1} + ''; // function (){return 1} var c = function(){return 1} + '' - 1; //NaN 
Оператор группировки

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

function foo() { return 1; } // undefined	 function foo() { return 1; }(); // Uncaught SyntaxError: Expected () to start arrow function, but got '}' instead of '=>' (function foo() { return 1; }()) // 1 (function foo() { return 1; })() // 1 

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

Оператор запятая

Оператор запятая вычисляет значение каждого своего операнда (слева направо) и возвращает значение последнего операнда.

0, function() { return 1; }(); // 1 
Операторы (+, -, !, ~, void)

+function() { return false; }(); // 0 -function() { return false; }(); // -0 !function() { return false; }(); // true ~function() { return false; }(); // -1 void function() { return false; }(); //undefined 
Комбинированные операторы:

!-function(){ return false; }(); // true var c =  5 * (2 - function(){return 1}()) // 5 var c =  5 * 2 - -~function(){return 1}() // 8 

Отличие именованных функциональных выражений от не именованных:

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

var f = function getFactorial (n) {   return n ? n * getFactorial(n - 1) : 1; };  f(5); // 120 

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

ссылка на оригинал статьи http://habrahabr.ru/post/274139/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *