Як називається цей шаблон JavaScript і для чого він використовується?


100

Я вивчаю THREE.js і помітив шаблон, де функції визначені так:

var foo = ( function () {
    var bar = new Bar();

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());

(Приклад см метод raycast тут ).

Нормальне зміна такого методу буде виглядати наступним чином :

var foo = function () {
    var bar = new Bar();

    //actual logic.
    //return result;
};

Порівнюючи першу версію із звичайною варіацією, перша здається відрізняється від цього:

  1. Він призначає результат самовиконання функції.
  2. Він визначає локальну змінну в межах цієї функції.
  3. Він повертає фактичну функцію, що містить логіку, яка використовує локальну змінну.

Отже, головна відмінність полягає в тому, що в першому варіанті смуга призначається лише один раз, при ініціалізації, а в другому варіанті створюється ця тимчасова змінна щоразу, коли вона викликається.

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

Мої запитання:

  1. Чи правильне це припущення?
  2. Чи є назва цього шаблону?
  3. Чому це використовується?

1
@ChrisHayes досить справедливо. Я позначив це як THREE.js, тому що вважав, що учасники THREE.js є найбільш кваліфікованими, щоб відповісти на це, але так, це загальне питання JS.
Патрік Клуг

2
Я вважаю, це називається закриттям. Ви можете прочитати про них.
StackFlowed

1
Якщо це єдине місце , бар конкретизується, то це Сінглтон шаблон.
Пол,

8
Не обов’язково для збереження пам’яті, але це може підтримувати стан у викликах
Хуан Мендес,

2
@wrongAnswer: не зовсім так. тут анонімна функція (яка була б закриттям) виконується негайно.
njzk2

Відповіді:


100

Ваші припущення майже вірні. Розглянемо їх спочатку.

  1. Він призначає повернення функції самовиконання

Це називається виразом функції негайно виклику функції або IIFE

  1. Він визначає локальну змінну в межах цієї функції

Це спосіб наявності приватних об’єктних полів у JavaScript, оскільки це не забезпечує privateключове слово чи функціональність в іншому випадку.

  1. Він повертає фактичну функцію, що містить логіку, яка використовує локальну змінну.

Знову ж таки, головний момент полягає в тому, що ця локальна змінна є приватною .

Чи є назва цього шаблону?

AFAIK ви можете назвати цей шаблон Шаблон модуля . Цитування:

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

Порівнюючи ці два приклади, мої найкращі здогадки про те, для чого використовується перший:

  1. Він реалізує модель дизайну Singleton.
  2. Можна керувати способом створення об'єкта певного типу, використовуючи перший приклад. Одне тісне узгодження з цією точкою можуть бути статичними заводськими методами, як описано в Ефективній Java.
  3. Це ефективно, якщо вам потрібно кожен раз однаковий стан об'єкта.

Але якщо вам просто потрібен об'єкт ванілі кожного разу, то, мабуть, цей візерунок не додасть ніякої цінності.


1
+1 для правильного визначення візерунка з книги Адді Османі. Ви маєте рацію у своєму іменуванні - це, власне, модель модуля - виявляючий шаблон модуля, до речі.
Бенджамін Грюнбаум

4
Я погоджуюся з вашою відповіддю, за винятком частини "без приватних змінних". Усі змінні JS мають лексичний діапазон "поза коробкою", що є більш потужним / загальним механізмом, ніж "приватні" змінні (наприклад, як це знайдено в Java); тому JS підтримує "приватні" змінні як особливий випадок способу обробки всіх змінних.
Варбо

Я думаю, що під приватними змінними ви мали на увазі приватне "об'єктне поле"
Ян Рінроуз


4
@LukaHorvat: насправді javascript не є більш "потужним", ніж інші мови (я віддаю перевагу терміну експресивний). Насправді, це менш виразно, тому що єдиний спосіб захистити свої змінні - це включити їх у функцію, щоб уникнути будь-яких дурниць із повторним використанням повторно. Модуль модуля є остаточним необхідним умовою для створення хорошого коду javascript, але це не особливість мови, це скоріше сумний спосіб вирішення, щоб уникнути укусу слабкістю мови.
Фаланве

11

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

Хоча можливо, що це обмежує використання пам’яті, зазвичай GC збирає невикористані об’єкти в будь-якому випадку, так що ця модель, ймовірно, не дуже допоможе.

Ця закономірність є специфічною формою закриття .


1
У JS зазвичай це "модуль"
Леша Огоньков,

2
Я б не назвав це "специфічною формою закриття", як такою. Це шаблон, який використовує закриття. Назва шаблону все ще знайдено для захоплення.
Кріс Хейс

4
Чи справді потрібне ім’я? Чи все має бути зразком? Чи справді нам потрібна нескінченна таксономія варіантів "шаблон модуля"? Чи не може бути просто "IIFE з деякими локальними змінними, що повертає функцію?"
Dagg Nabbit

3
@DaggNabbit Коли виникає питання "як називається ця модель"? Так, йому потрібне ім'я або ще переконливий аргумент, що його немає. Крім того, закономірності існують не просто так. Я не розумію, чому ти тут проти них бігаєш.
Кріс Хейс

4
@ChrisHayes, якщо йому потрібне ім'я, навіщо вам робити аргумент, що його немає? Це не має сенсу. Якщо вона потрібна, вона не повинна мати її. У мене немає проблем з візерунками, але я не вважаю за необхідне класифікувати кожну просту ідіому як зразок. Це призводить до роздумів обмеженими способами ("Це шаблон модуля? Чи правильно я використовую шаблон модуля?", Порівняно з "У мене є IIFE з деякими локальними змінними, що повертає функцію, чи працює ця конструкція для мене?")
Dagg Nabbit

8

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

Закриття (ідентифіковане функцією у межах функції) забезпечує внутрішню функцію доступу до змінних всередині зовнішньої функції.

У наведеному вами прикладі внутрішня функція повертається (і присвоюється їй foo), виконуючи зовнішню функцію, що означає, що tmpObjectпродовжує жити в рамках закриття, і кілька викликів до внутрішньої функції foo()працюватимуть на тому самому екземплярі tmpObject.


5

Ключова відмінність вашого коду від коду Three.js полягає в тому, що в коді Three.js змінна tmpObjectініціалізується лише один раз, а потім ділиться кожним викликом повернутої функції.

Це було б корисно для збереження певного стану між дзвінками, подібно до того, staticяк використовуються змінні в мовах, подібних С.

tmpObject є приватною змінною, видимою лише для внутрішньої функції.

Він змінює використання пам'яті, але не призначений для економії пам'яті.


5

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

введіть тут опис зображення

В останньому випадку метод додавання буде називатися Calculator.add ();


0

У наданому прикладі перший фрагмент буде використовувати один і той же екземпляр tmpObject для кожного виклику функції foo (), де, як і у другому фрагменті, tmpObject буде кожен раз новим екземпляром.

Однією з причин, що може бути використаний перший фрагмент, є те, що змінна tmpObject може бути розділена між викликами до foo (), без того, щоб її значення просочилося в область, у якій foo () оголошено.

Невиконана версія функції першого фрагмента насправді виглядатиме так:

var tmpObject = new Bar();

function foo(){
    // Use tmpObject.
}

Однак зауважте, що ця версія має tmpObject в тому ж обсязі, що і foo (), тому з нею можна маніпулювати пізніше.

Кращим способом досягнення тієї ж функціональності було б використання окремого модуля:

Модуль 'foo.js':

var tmpObject = new Bar();

module.exports = function foo(){
    // Use tmpObject.
};

Модуль 2:

var foo = require('./foo');

Порівняння продуктивності IEF та іменованої функції foo Creator: http://jsperf.com/ief-vs-named-function


3
Ваш "кращий" приклад працює лише в NodeJS, і ви не пояснили, як це краще.
Бенджамін Груенбаум

Зробити окремий модуль не «краще», це просто інше. Зокрема, це спосіб згортання функцій вищого порядку до об'єктів першого порядку. Код першого порядку, як правило, простіше переступити, але, як правило, більш багатослівний і змушує нас повторювати проміжні результати.
Варбо

@BenjaminGruenbaum Модулі не тільки в Node, існує багато рішень на стороні клієнта, наприклад, перегляньте сторінку. Я вважаю, що рішення модуля є "кращим", оскільки воно легше читається, простіше налагоджувати і чіткіше визначає те, що в області застосування, а де.
Kory Nunn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.