`nova função ()` com “f” minúsculo em JavaScript

106

Meu colega tem usado "new function ()" com "f" minúsculo para definir novos objetos em JavaScript. Parece funcionar bem em todos os principais navegadores e também parece ser bastante eficaz em ocultar variáveis ​​privadas. Aqui está um exemplo:

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

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

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

Assim que "this" for usado, ele se tornará propriedade pública de algumObj. Então someObj.foo, someObj.get_inner () e someObj.set_inner () estão todos disponíveis publicamente. Além disso, set_inner () e get_inner () são métodos privilegiados, então eles têm acesso a "interior" por meio de fechamentos.

No entanto, não vi nenhuma referência a essa técnica em qualquer lugar. Até o JSLint de Douglas Crockford reclama disso:

  • construção estranha. Excluir 'novo'

Estamos usando essa técnica na produção e parece estar funcionando bem, mas estou um pouco ansioso com isso porque não está documentado em lugar nenhum. Alguém sabe se esta é uma técnica válida?

Johnny Oshika
fonte
6
Eu prefiro sua construção ao invés do IIFE ('Immediately-Invoked Function'). 1: Você não precisa de um objeto 'instância' explícito, isso é exatamente o que 'this' é em JavaScript. 2: Você não precisa devolver nada, ou seja, você não precisa se lembrar de devolver. Até o autor da resposta aceita se esqueceu de retornar o objeto de instância inicialmente! As pessoas geralmente preferem usar um IIFE se detestam novidades e isso, com um bom motivo - se você tiver uma função que manipula um evento DOM, thisirá se referir ao elemento que disparou o evento, não ao seu objeto, mas você poderia apenas ter em var instance = thisvez disso.
Lee Kowalkowski
1
Por que é importante para a pergunta especificar "f minúsculo"?
ClearCloud8,
7
Porque em Javascript também existe a função 'Função' (com F maiúsculo), que é diferente: Função é uma função construtora que pode criar novos objetos de função, enquanto função é uma palavra-chave.
Stijn de Witt
@Bergi eu li seus links. Não vejo razão para desacreditar esse padrão. É válido. É simples. Então, oque há de errado. JSLint reclama de tudo, BTW :)
Stijn de Witt

Respostas:

64

Já vi essa técnica antes, é válida, você está usando uma expressão de função como se fosse uma função de construtor .

Mas, IMHO, você pode conseguir o mesmo com uma expressão de função de invocação automática, realmente não vejo sentido em usar o newoperador dessa maneira:

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;
})();

O objetivo do newoperador é criar novas instâncias do objeto, configurando a [[Prototype]]propriedade interna, você pode ver como isso é feito pela [Construct]propriedade interna.

O código acima produzirá um resultado equivalente.

CMS
fonte
1
A Especificação ECMAScript 262 na Seção 13 explica isso um pouco mais formalmente. Algo como function foo () {}retorna o resultado da criação de um Functionobjeto [presumivelmente com new Function ()]. É o açúcar da sintaxe.
Clinton Pierce
3
Acho que está faltando um return instance;no final. Caso contrário, someObjapenas será undefined. :-)
Matthew Crumley
1
Posso sugerir que, se você se preocupa com modularidade e ocultação de informações, desista disso e comece a usar algo como require.js? Você está no meio do caminho, por que parar aqui? A definição de módulo assíncrono (que é o que o require.js implementa) oferece suporte a esse caso de uso e fornece um conjunto de ferramentas completo para lidar com escopo, namespacing e gerenciamento de dependência.
Stijn de Witt
Observe que os parênteses em torno da declaração da função são desnecessários, pois a declaração já é uma expressão devido à presença de=
Pílulas de explosão
@StijndeWitt na metade do caminho significa que você precisaria fazer o dobro do trabalho para usar o require.js, mas isso pode ser tudo que você precisa em casos simples.
xr280xr
15

Seu código é semelhante à construção menos estranha

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

    ...
};
var someObj = new Foo;
Kennytm
fonte
9
Não é apenas semelhante, ele faz exatamente a mesma coisa ... com a única exceção de que eles não poderão reutilizar Foo para criar outro objeto.
kikito
3
A versão do OP pode ser reutilizada via novo someObj.constructor . Aqui, o construtor é adicionado ao namespace explicitamente; o estilo certo depende da finalidade pretendida da função. Além disso, esse estilo - embora certamente seja o padrão - permite que alguém preencha o namespace global se esquecer de new before Foo .
J Bryan Price
@kikito o que quer dizer com isso não permite reutilizar Foo para criar outro objeto? var newObj = new Foo () deve criar uma nova instância.
Bill Yang
1
@BillYang Isso foi há 5 anos. Nenhuma idéia. Não toquei no javascript desde então.
kikito
12

Para esclarecer alguns aspectos e fazer com que o JSLint de Douglas Crockford não reclame do seu código, aqui estão alguns exemplos de instanciação:

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

No exemplo 3. expressão em (...) como valor é uma função / construtor. Tem a seguinte aparência: new (function () {...}) (). Portanto, se omitirmos os colchetes finais como no exemplo 2, a expressão ainda é uma chamada de construtor válida e se parece com o exemplo 4.

O JSLint de Douglas Crockford "pensa" que você queria atribuir a função a someObj, não a sua instância. Afinal, é apenas um aviso, não um erro.

DUzun
fonte