`нова функція ()` з малих букв 'f' в JavaScript


106

Мій колега використовував "нову функцію ()" з малої літери "f" для визначення нових об'єктів у JavaScript. Це, здається, працює добре у всіх основних браузерах, а також, здається, досить ефективно приховувати приватні змінні. Ось приклад:

    var someObj = new function () {
        var inner = 'some value';
        this.foo = 'blah';

        this.get_inner = function () {
            return inner;
        };

        this.set_inner = function (s) {
            inner = s;
        };
    };

Як тільки "це" використовується, воно стає суспільним надбанням деякихObj. Тож деякіObj.foo, someObj.get_inner () та someObj.set_inner () доступні для всіх. Крім того, set_inner () та get_inner () є пільговими методами, тому вони мають доступ до "внутрішнього" через закриття.

Однак я ніде не бачив жодної згадки про цю техніку. На це скаржиться навіть JSLint Дугласа Крокфорда:

  • дивна конструкція. Видалити "нове"

Ми використовуємо цю техніку у виробництві, і, здається, вона працює добре, але я трохи занепокоєний цим, оскільки це ніде не зафіксовано. Хтось знає, чи це дійсна техніка?


6
Я віддаю перевагу вашій конструкції над IIFE ("Функція негайно викликаних"). 1: Вам не потрібен явний об’єкт 'екземпляр', саме те, що 'це' є в JavaScript. 2: Вам нічого не потрібно повертати, а значить, не потрібно пам’ятати. Навіть автор прийнятої відповіді забув спочатку повернути об’єкт екземпляра! Зазвичай люди вважають за краще використовувати IIFE, якщо вони ненавидять нове & це, з поважною причиною. Якщо у вас є функція, що обробляє подію DOM, thisбуде посилатися на елемент, який запустив подію, а не на ваш об'єкт, але ви могли просто мати це var instance = this.
Лі Ковальковський

1
Чому в питанні важливо вказати "малі регістри f"?
ClearCloud8

7
Оскільки в Javascript також існує функція "Функція" (з великого регістру F), яка відрізняється: Функція - це конструктор, який може створювати нові об'єкти функції, тоді як функція - це ключове слово.
Stijn de Witt


@Bergi Я читав ваші посилання. Я не бачу причин дискредитувати цю модель. Це дійсно. Це просто. Отже, що не так. JSLint скаржиться на все BTW :)
Stijn de Witt

Відповіді:


64

Я бачив цю техніку раніше, вона дійсна, ви використовуєте вираз функції, як ніби це функція конструктора .

Але IMHO, ви можете домогтися того ж із виразом функції автоматичного виклику, я не бачу сенсу використовувати newоператор таким чином:

var someObj = (function () {
    var instance = {},
        inner = 'some value';

    instance.foo = 'blah';

    instance.get_inner = function () {
        return inner;
    };

    instance.set_inner = function (s) {
        inner = s;
    };

    return instance;
})();

Мета newоператора - створити нові екземпляри об'єкта, встановивши [[Prototype]]внутрішню властивість, ви можете побачити, як це робиться[Construct] внутрішнім властивістю.

Вищевказаний код дасть еквівалентний результат.


1
Специфікація ECMAScript 262 у Розділі 13 пояснює це дещо формальніше. Щось подібне function foo () {}повертає результат створення Functionоб’єкта [імовірно, з новою функцією ()]. Це синтаксичний цукор.
Клінтон Пірс

3
Я думаю, що ти не вистачаєш return instance;наприкінці. Інакше someObjпросто буде undefined. :-)
Меттью Крамлі

1
Чи можу я запропонувати, що якщо ви піклуєтеся про модульність та приховування інформації, ви просто відмовитесь від цього і почнете використовувати щось на зразок Requ.js? Ти на півдорозі, навіщо тут зупинятися? Визначення асинхронного модуля (що реалізує Requir.JS) підтримує цю службову скриньку і дає вам цілий набір інструментів для вирішення масштабування, простору імен та управління залежностями.
Штійн де Вітт

Зауважте, що в дужках навколо декларації функції непотрібні, оскільки це твердження вже є виразом через наявність=
Вибухові таблетки

@StijndeWitt на півдорозі означає, що вам потрібно буде виконати вдвічі більше роботи, щоб користуватися Requ.js, але це може бути все, що вам потрібно в простих випадках.
xr280xr

15

Ваш код так само схожий на менш дивну конструкцію

function Foo () {
    var inner = 'some value';
    this.foo = 'blah';

    ...
};
var someObj = new Foo;

9
Це не просто подібне, воно робить саме те саме ... за єдиним винятком, що вони не зможуть повторно використовувати Foo для створення іншого об'єкта.
кікіто

3
Версія OP може бути повторно використана через новий someObj.constructor . Тут конструктор явно додається до простору імен; правильний стиль залежить від передбачуваної мети функції. Крім того, цей стиль - хоч і звичайно стандартний - дозволяє комусь заповнити глобальний простір імен, якщо він забуде нове перед Foo .
Дж. Брайан Ціна

@kikito, що це означає, що це не дозволяє повторно використовувати Foo для створення іншого об'єкта? var newObj = new Foo () повинен створювати новий екземпляр.
Білл Ян

1
@BillYang Це було 5 років тому. Не маю уявлення. Я з тих пір не торкався JavaScript.
кікіто

12

Щоб уточнити деякі аспекти і змусити JSLint Дугласа Крокфорда не скаржитися на ваш код, ось кілька прикладів опису:

1. o = new Object(); // normal call of a constructor

2. o = new Object;   // accepted call of a constructor

3. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
})(); // normal call of a constructor

4. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
}); // accepted call of a constructor

У прикладі 3. вираження в (...) як значення є функцією / конструктором. Це виглядає приблизно так: new (function () {...}) (). Отже, якщо ми опускаємо дужки, що закінчуються, як у прикладі 2, вираз все ще є дійсним викликом конструктора і виглядає як приклад 4.

JSLint Дугласа Крокфорда "думає", що ви хотіли призначити функцію деякомуObj, а не його екземпляру. Адже це лише попередження, а не помилка.

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