`new function ()` со строчной буквой "f" в JavaScript

106

Мой коллега использовал «new function ()» со строчной буквой «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;
        };
    };

Как только "this" используется, оно становится общедоступным свойством someObj. Итак, someObj.foo, someObj.get_inner () и someObj.set_inner () все доступны публично. Кроме того, set_inner () и get_inner () являются привилегированными методами, поэтому они имеют доступ к «внутреннему» через замыкания.

Однако я нигде не встречал упоминания об этой технике. Даже JSLint Дугласа Крокфорда жалуется на это:

  • странная конструкция. Удалить "новый"

Мы используем эту технику в производстве, и она, кажется, работает хорошо, но меня это немного беспокоит, потому что она нигде не задокументирована. Кто-нибудь знает, действительна ли это техника?

Джонни Ошика
источник
6
Я предпочитаю вашу конструкцию IIFE («Немедленно вызываемая функция»). 1: Вам не нужен явный объект 'instance', это именно то, что 'this' в JavaScript. 2: Вам не нужно ничего возвращать, что означает, что вам не нужно помнить об этом. Даже автор принятого ответа изначально забыл вернуть объект экземпляра! Люди обычно предпочитают использовать IIFE, если они ненавидят новое и это по уважительной причине. Если у вас есть функция, обрабатывающая событие DOM, она thisбудет ссылаться на элемент, который инициировал событие, а не на ваш объект, но вы могли бы просто использовать var instance = thisвместо этого.
Ли Ковальковски
1
Почему в вопросе важно указывать «нижний регистр f»?
ClearCloud8
7
Поскольку в Javascript также существует функция «Функция» (с заглавной буквой F), которая отличается: функция - это функция-конструктор, которая может создавать новые объекты функций, тогда как функция - это ключевое слово.
Stijn de Witt
@Bergi Я читал ваши ссылки. Я не вижу причин дискредитировать этот образец. Это действительно так. Это просто. Так что не так. JSLint жалуется на все, кстати :)
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]внутренним свойством.

Приведенный выше код даст эквивалентный результат.

CMS
источник
1
Спецификация ECMAScript 262 в разделе 13 объясняет это немного более формально. Что-то вроде function foo () {}возвращает результат создания Functionобъекта [предположительно с помощью new Function ()]. Это синтаксический сахар.
Клинтон Пирс
3
Я думаю, вам не хватает return instance;в конце. Иначе someObjпросто будет undefined. :-)
Мэтью Крамли
1
Могу я предложить, если вы заботитесь о модульности и сокрытии информации, просто откажитесь от этого и начните использовать что-то вроде require.js? Вы на полпути, зачем останавливаться здесь? Определение асинхронного модуля (которое реализует require.js) поддерживает этот вариант использования и предоставляет вам полный набор инструментов для работы с областью видимости, пространством имен и управлением зависимостями.
Stijn de Witt
Обратите внимание, что круглые скобки, окружающие объявление функции, не нужны, поскольку оператор уже является выражением из-за присутствия=
Explosion Pills
@StijndeWitt на полпути означает, что вам нужно будет сделать в два раза больше работы, чтобы использовать require.js, но это может быть все, что вам нужно в простых случаях.
xr280xr
15

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

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

    ...
};
var someObj = new Foo;
Kennytm
источник
9
Он не просто похож, он делает то же самое ... с единственным исключением, что они не смогут повторно использовать Foo для создания другого объекта.
kikito
3
Версия OP может быть повторно использована через новый someObj.constructor . Здесь конструктор явно добавляется в пространство имен; правильный стиль зависит от предполагаемого назначения функции. Кроме того, этот стиль - хотя, безусловно, является стандартом - позволяет кому-то заполнить глобальное пространство имен, если они забывают new до Foo .
Джей Брайан Прайс
@kikito, что вы имеете в виду, это не позволяет повторно использовать Foo для создания другого объекта? var newObj = new Foo () должен создавать новый экземпляр.
Билл Ян
1
@BillYang Это было 5 лет назад. Без понятия. С тех пор я не трогал javascript.
kikito 03
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. выражение in (...) as value является функцией / конструктором. Выглядит это так: new (function () {...}) (). Поэтому, если мы опускаем закрывающие скобки, как в примере 2, выражение все еще является допустимым вызовом конструктора и выглядит как пример 4.

JSLint Дугласа Крокфорда «думает», что вы хотели назначить функцию someObj, а не его экземпляру. А ведь это просто предупреждение, а не ошибка.

Дузун
источник