Какова цель упаковки целых файлов Javascript в анонимные функции, такие как «(function () {…}) ()»? - PullRequest
558 голосов
/ 11 марта 2010

В последнее время я много читал Javascript и заметил, что весь файл упакован, как показано ниже, в импортируемые файлы .js.

(function() {
    ... 
    code
    ...
})();

В чем причина этого, а не простой набор функций конструктора?

Ответы [ 8 ]

764 голосов
/ 11 марта 2010

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

В Javascript вы можете вкладывать функции. Итак, законно следующее:

function outerFunction() {
   function innerFunction() {
      // code
   }
}

Теперь вы можете позвонить outerFunction(), но видимость innerFunction() ограничена областью действия outerFunction(), что означает, что она является приватной outerFunction(). Он в основном следует тому же принципу, что и переменные в Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

Соответственно:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

В приведенном выше сценарии вы можете позвонить globalFunction() из любого места, но вы не можете позвонить localFunction1 или localFunction2.

Когда вы пишете (function() { ... code ... })(), вы делаете код внутри функции литеральным (то есть весь «объект» на самом деле является функцией). После этого вы вызываете функцию самостоятельно (финал ()). Итак, главное преимущество этого, как я уже упоминал ранее, заключается в том, что вы можете иметь частные методы / функции и свойства:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})()

В первом примере, globalFunction () была публичной функцией, которую можно было вызывать для доступа к публичной функциональности, но как вы ее называете в приведенном выше примере? Здесь функция самозапуска заставляет код автоматически запускаться при запуске. Так же, как вы можете добавить initMyStuff (); в начало любого файла .js, и он будет автоматически запускаться как часть глобальной области видимости, эта автоматически вызываемая функция также будет запускаться автоматически, хотя, поскольку она является неназванной функцией, ее нельзя вызывать несколько раз, как это может быть initMyStuff (). 1026 *

Удобно то, что вы также можете определять вещи внутри и открывать их внешнему миру (пример пространства имен, так что вы можете создать свою собственную библиотеку / плагин):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

Теперь вы можете позвонить myPlugin.public_function1(), но не можете получить доступ к private_function()! Очень похоже на определение класса. Чтобы лучше это понять, я рекомендую следующие ссылки для дальнейшего чтения:

EDIT

Я забыл упомянуть. В этом последнем () вы можете передать все, что захотите, внутрь. Например, когда вы создаете подключаемые модули jQuery, вы передаете jQuery или $ следующим образом:

(function(jQ) { ... code ... })(jQuery) 

То, что вы здесь делаете, - это определение функции, которая принимает один параметр (называемый jQ, локальной переменной и известной только этой функции). Затем вы вызываете функцию самостоятельно и передаете параметр (также называемый jQuery, но this из внешнего мира и ссылка на сам jQuery). В этом нет необходимости, но есть некоторые преимущества:

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

Ранее я описывал, как эти функции запускаются автоматически при запуске, но если они запускаются автоматически, кто передает аргументы? Этот метод предполагает, что все параметры определены как глобальные переменные. Так что, если бы jQuery не был определен как глобальная переменная, этот пример не работал бы и не мог быть вызван другим способом, так как наш пример - анонимная функция. Как вы можете догадаться, во время инициализации jquery.js определяет глобальную переменную jQuery, а также более известную глобальную переменную $, которая позволяет этому коду работать после включения jquery.js.

76 голосов
/ 04 ноября 2014

Короче

Краткое описание

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

Помогает уменьшить шансы:

  • конфликт с другими приложениями / библиотеками
  • загрязняющий превосходящий (наиболее вероятный глобальный) охват

Он не не определяет, когда документ готов - это не какой-то document.onload или window.onload

Он обычно известен как Immediately Invoked Function Expression (IIFE) или Self Executing Anonymous Function.

объясненный код

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

В приведенном выше примере любая переменная, определенная в функции (т. Е. Объявленная с использованием var), будет "частной" и доступна ТОЛЬКО в области действия функции (как выразился Вивин Палиат). Другими словами, эти переменные не видны / недоступны вне функции. Смотрите живое демо .

Javascript имеет функцию определения области видимости. «Параметры и переменные, определенные в функции, не видны вне функции, и что переменная, определенная где-либо внутри функции, видна везде внутри функции». (из "Javascript: хорошие части").


Подробнее

Альтернативный код

В конце код, размещенный ранее, также можно сделать следующим образом:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Смотрите демо-версию .


Корни

Итерация 1

Однажды кто-то, вероятно, подумал: «Должен быть способ избежать именования« myMainFunction », поскольку все, что нам нужно, - это выполнить его немедленно».

Если вы вернетесь к основам, вы обнаружите, что:

  • expression: что-то, оценивающее значение. т.е. 3+11/x
  • statement: строки кода делают что-то, НО это делает , а не вычисляет значение. т.е. if(){}

Аналогичным образом, выражения функций оцениваются как значения. И одно из следствий (я полагаю?) Заключается в том, что они могут быть немедленно вызваны:

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

Итак, наш более сложный пример:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Посмотреть демо-версию .

Итерация 2

Следующим шагом является мысль «зачем иметь var myMainFunction =, если мы даже не используем его!?».

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

 function(){ console.log('mamamia!'); }();

Смотрите демо-версию .

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

Хитрость в том, что, удалив var myMainFunction =, мы преобразовали выражение функции в объявление функции . Для получения более подробной информации см. Ссылки в разделе «Ресурсы».

Следующий вопрос: «Почему я не могу сохранить его как выражение функции с чем-то отличным от var myMainFunction =?

Ответ: «Вы можете», и на самом деле есть много способов сделать это: добавив +, !, - или, возможно, заключив в скобки пару (как сейчас сделано по договоренности) и больше я верю. Как пример:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

или

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

или

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

Таким образом, как только соответствующая модификация добавлена ​​к тому, что когда-то было нашим «Альтернативным кодом», мы возвращаемся к тому же коду, который использовался в примере «Объясненный код»

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Подробнее о Expressions vs Statements:


Демистифицирующие прицелы

Одна вещь, которая может задаться вопросом: «что происходит, когда вы НЕ определяете переменную« должным образом »внутри функции - т.е. вместо этого делаете простое присваивание?»

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

Смотрите демо-версию .

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

В браузерной среде (по сравнению с серверной средой, такой как nodejs) глобальная область определяется объектом window. Следовательно, мы можем сделать window.myOtherFunction().

Мой совет «Надлежащей практики» по этой теме: всегда используйте var при определении чего-либо : будь то число, объект или функция и даже в глобальной области. Это делает код намного проще.

Примечание:

  • JavaScript не не имеет block scope (Обновление: локальные переменные области видимости блока добавлены в ES6 .)
  • javascript имеет только function scope & global scope (window область действия в среде браузера)

Подробнее о Javascript Scopes:


Ресурсы


Следующие шаги

Как только вы получите эту концепцию IIFE, это приведет к module pattern, что обычно достигается путем использования этого шаблона IIFE. Веселитесь:)

26 голосов
/ 11 марта 2010

Javascript в браузере действительно имеет только несколько эффективных областей действия: область действия функции и глобальная область действия.

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

19 голосов
/ 11 марта 2010

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

Пример. Предположим, я пишу:

(function() {

    var x = 2;

    // do stuff with x

})();

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

8 голосов
/ 11 марта 2010

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

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }
7 голосов
/ 11 марта 2010

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

(function($) { ...code...})(jQuery);
3 голосов
/ 26 октября 2016
  1. Чтобы избежать конфликта с другими методами / библиотеками в том же окне,
  2. Избегайте глобальной области, сделайте ее локальной,
  3. Чтобы ускорить отладку (локальная область),
  4. JavaScript имеет только область действия функции, поэтому он также поможет в компиляции кодов.
1 голос
/ 24 ноября 2015

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

(function() {
    'use strict';

    //Your code from here
})();
...