Як працює цей синтаксис JavaScript / jQuery: (функція (вікно, не визначено) {}) (вікно)?


153

Ви коли-небудь заглядали під капот на вихідний код jQuery 1.4 і помічали, як він інкапсульований таким чином:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

Я прочитав статтю про розширення імен JavaScript і ще одну під назвою " Важлива пара батьків ", тому мені відомо про те, що відбувається тут.

Але я ніколи раніше не бачив цього конкретного синтаксису. Що це undefinedробиш там? І чому це windowпотрібно пройти, а потім знову з’явитися в кінці?


3
Я хотів додати, що Пол Ірланд розповідає про це у цьому відео: paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie

@Bergi, ти позначив моє запитання як дублікат іншого, але я поставив це питання майже за рік до того, як дублікат. Акторський склад повинен бути навпаки.
dkinzer

@dkinzer: Справа не в тому, щоб її задавали раніше, а про найвищу якість відповіді. Щоправда, у цьому випадку це шия та шия, але я знайшов відповідь CMS найкращою. Заходьте на chat.stackoverflow.com/rooms/17, щоб обговорити це, однак
Бергі

Відповіді:


161

Невизначена нормальна змінна і її можна просто змінити undefined = "new value";. Таким чином, jQuery створює локальну змінну "undefined", яка дійсно не визначена.

Змінна вікна робиться локальною з міркувань продуктивності. Тому що, коли JavaScript шукає змінну, вона спочатку проходить локальні змінні, поки не знайде ім'я змінної. Якщо його не знайти, JavaScript проходить через наступну область застосування тощо, поки не фільтрує глобальні змінні. Отже, якщо змінна вікна зроблена локально, JavaScript може шукати її швидше. Додаткова інформація: Пришвидшити Ваш JavaScript - Ніколас Закас


1
Дякую! це було дуже інформативне відео. Я думаю, вам потрібно відредагувати свою відповідь, щоб сказати, що "змінна вікно зроблено локальним". Я думаю, що це найкраща відповідь. Я підрахував і виявив, що об’єкт вікна викликається щонайменше 17 разів у вихідному коді JQuery. Тож має бути вагомий ефект при переміщенні вікна в локальну область.
dkinzer

@DKinzer О, так, ви, звичайно, зроблені на місцевому рівні. Радий, що можу вам допомогти =)
Вінсент

1
Відповідно до цього оновленого тесту, jsperf.com/short-scope/5 проходження "вікна" насправді повільніше, коли доводиться отримати віконну константу через jQuery, і особливо погано для Safari. Тому, хоча у "невизначеному" є випадок використання, я не зовсім впевнений, що "вікно" особливо корисне у всіх випадках.
hexalys

1
Це також позитивно впливає на мінімізацію коду. Таким чином, кожне використання "вікна" та "невизначеного" може бути замінене мініфером на коротше ім'я.
TLindig

2
@ lukas.pukenis Коли ви створюєте бібліотеку, яка використовується на мільйонах веб-сайтів, розумно не робити припущень щодо того, що робитимуть божевільні.
JLRishe

54

Не визначено

Заявляючи undefinedяк аргумент, але ніколи не передаючи йому значення, це гарантує його завжди невизначеність, оскільки це просто змінна в глобальному масштабі, яку можна перезаписати. Це робить a === undefinedбезпечною альтернативою typeof a == 'undefined', яка економить кілька символів. Це також робить код більш зручним для мініфікаторів, як undefinedйого можна скоротити, uнаприклад, заощадивши ще кілька символів.

Вікно

Передача windowяк аргумент зберігає копію в локальному масштабі, що впливає на продуктивність: http://jsperf.com/short-scope . Усі доступні до windowтепер доведеться подорожувати на один рівень менше вгору по ланцюгу. Як і у випадку undefined, локальна копія знову дозволяє більш агресивне мінімізація.


Sidenote:

Хоча це, можливо, не було наміром розробників jQuery, передача даних windowдозволяє більш легко інтегрувати бібліотеку в середовищі JavaScript на сервері, наприклад node.js - там, де немає глобального windowоб'єкта. У такій ситуації потрібно замінити лише один рядок, щоб замінити windowоб'єкт іншим. У випадку jQuery, макетний windowоб’єкт може бути створений та переданий з метою скребкування HTML (така бібліотека, як jsdom може це зробити).


2
+1 для згадки про Мініфікацію, хотілося б, щоб я міг +2 для jsperf - У мінімізованій версії є (function(a,b){})(window);- aі bнабагато коротший, ніж windowі undefined:)
gnarf

+1 Я чув про ланцюг сфери застосування раніше, але забув цей термін. Дякую!
alex

Це також альтернатива використанню void (0), яке було призначене з тієї ж причини: void (0) - це безпечний спосіб отримати невизначене значення.
glmxndr

«Те , що ви говорите» в якості посилання на цей інше питання: stackoverflow.com/questions/4945265 / ...
gnarf

@gnarf, дякую за повідомлення про те, що питання об’єднані. Я відредагую свою відповідь, щоб мати трохи більше сенсу;)
Девід Тан

16

Інші пояснили undefined. undefinedце як глобальна змінна, яку можна переосмислити на будь-яке значення. Цей прийом полягає у запобіганні злому всіх невизначених чеків, якщо хтось писав, скажімо, undefined = 10десь. Аргумент, який ніколи не передається, гарантовано є реальним undefinedнезалежно від значення змінної undefined .

Причину проходження вікна можна проілюструвати наступним прикладом.

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

Що робить журнал консолі? Значення права windowоб’єкта? Неправильно! 10? Неправильно! Це журнали undefined. Інтерпретатор Javascript (або компілятор JIT) переписує це так -

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

Однак якщо ви отримаєте windowзмінну як аргумент, то немає var і, отже, сюрпризів немає.

Я не знаю, чи робить це jQuery, але якщо ви windowз будь-якої причини переосмислюєте локальну змінну в будь-якій точці вашої функції, це гарна ідея запозичити її з глобальної сфери.


6

window передається так, про всяк випадок, якщо хтось вирішить переосмислити об’єкт вікна в IE, я припускаю те саме для undefined , у випадку, якщо він буде перепризначений якимось чином пізніше.

Вершина windowцього сценарію - це лише називання аргументу "вікно", аргументу, який є більш локальним, ніж глобальна windowпосилання та те, що використовуватиме код у цьому закритті. В windowкінці насправді вказується, що потрібно пройти для першого аргументу, в даному випадку - поточний сенс window... сподіваємось, ви ще не згрібали до того, windowяк це станеться.

Це може бути легше придумати, показавши найбільш типовий випадок, що використовується в jQuery, .noConflict()обробці плагінами , тому для більшості кодів ви все ще можете використовувати $, навіть якщо він означає щось інше, ніж jQueryпоза цією сферою:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);

Дякую. Це має багато сенсу. Хоча я думаю, що відповідь - це поєднання цього і того, що говорить @ C.Zakas.
dkinzer

@DKinzer Я боюсь, що я не C. Zakas;)
Вінсент

@Vincent, вибач за це :-)
dkinzer

5

Перевірено 1000000 ітерацій. Такий тип локалізації не впливав на продуктивність. Ні 100 мілісекунд у 1000000 ітерацій. Це просто марно.


2
Не кажучи вже про те, що закриття несе всю змінну разом із екземпляром функції, тому якщо у вас є 100 байтів у закритті та 1000 екземплярів, це на 100 кб більше пам'яті.
Акаш Кава

@AkashKava та @Semra, я не думаю, що цей тест насправді пояснює, чому ти пройдеш у вікно в реальній ситуації. Підвищення продуктивності буде видно лише тоді, коли ви глибоко вклали закриття, які отримують доступ до цієї змінної далі по ланцюгу області. очевидно, якщо ваша функція знаходиться лише в одному кроці від ланцюга області від змінної, ви не побачите великого прибутку. але якби це було там вниз і потрібно було дістати, windowви можете побачити певну вигоду в місцевій копії.
Габереаль

Нові двигуни @gabereal вирішують закриття під час самої компіляції, під час виконання підсилення не збільшується. Я сумніваюся, що є якийсь вимірюваний приріст продуктивності, якщо ви настільки впевнені, ви можете створити приклад jsperf для демонстрації продуктивності. Єдина ефективність, яку ми отримуємо, - ізолюючи закриття, що призводить до кращого мінімізації, що зменшує розмір та продуктивність завдяки швидшому завантаженню та розбору сценарію.
Акаш Кава

@AkashKava, що змушує тебе думати, що я такий впевнений? Я щойно сказав: "Можливо, я побачу певний прибуток" і "я не думаю". Я міг би створити тест, але я вважаю за краще, оскільки я ніколи не використовую цей зразок. Ви можете пояснити, що ви маєте на увазі під "компіляцією під час компіляції"? на відміну від альтернативи? Як раніше двигуни Javascript робили щось по-іншому?
Габереаль

Знайдіть код, у якому вікно криве в кінці, але не введене в параметри функції, що це означає? це гарантує, що параметри слідують вихідному глобальному об'єкту масштабу, навіть якщо немає жодної парами вікна, яку я припускаю?
Веб-жінка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.