`nueva función ()` con minúscula "f" en JavaScript

106

Mi colega ha estado usando "nueva función ()" con una "f" minúscula para definir nuevos objetos en JavaScript. Parece funcionar bien en todos los navegadores principales y también parece ser bastante eficaz para ocultar variables privadas. He aquí un ejemplo:

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

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

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

Tan pronto como se utiliza "this", se convierte en propiedad pública de someObj. Así que someObj.foo, someObj.get_inner () y someObj.set_inner () están todos disponibles públicamente. Además, set_inner () y get_inner () son métodos privilegiados, por lo que tienen acceso a "inner" mediante cierres.

Sin embargo, no he visto ninguna referencia a esta técnica en ninguna parte. Incluso JSLint de Douglas Crockford se queja de ello:

  • construcción extraña. Eliminar 'nuevo'

Estamos usando esta técnica en producción y parece que funciona bien, pero estoy un poco ansioso porque no está documentado en ninguna parte. ¿Alguien sabe si esta es una técnica válida?

Johnny Oshika
fuente
6
Prefiero su construcción sobre el IIFE ('Función invocada inmediatamente'). 1: No necesita un objeto de 'instancia' explícito, eso es exactamente lo que es 'esto' en JavaScript. 2: No es necesario que devuelva nada, lo que significa que no es necesario que se acuerde de hacerlo. ¡Incluso el autor de la respuesta aceptada olvidó devolver el objeto de instancia inicialmente! La gente generalmente prefiere usar un IIFE si odia lo nuevo y esto, con una buena razón: si tiene una función que maneja un evento DOM, thisse referirá al elemento que disparó el evento, no a su objeto, pero podría tener en su var instance = thislugar.
Lee Kowalkowski
1
¿Por qué es importante para la pregunta especificar "f minúscula"?
ClearCloud8
7
Porque en Javascript también existe la función 'Función' (con F mayúscula), que es diferente: la función es una función constructora que puede crear nuevos objetos de función, mientras que función es una palabra clave.
Stijn de Witt
@Bergi Leí tus enlaces. No veo ninguna razón para desacreditar este patrón. Es valido. Es sencillo. Así que qué hay de malo. JSLint se queja de todo por cierto :)
Stijn de Witt

Respuestas:

64

He visto esa técnica antes, es válida, estás usando una expresión de función como si fuera una función de constructor .

Pero en mi humilde opinión, puede lograr lo mismo con una expresión de función de invocación automática, realmente no veo el punto de usar el newoperador de esa manera:

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

El propósito del newoperador es crear nuevas instancias de objetos, configurando la [[Prototype]]propiedad interna, puede ver cómo esto lo hace la [Construct]propiedad interna.

El código anterior producirá un resultado equivalente.

CMS
fuente
1
La Especificación ECMAScript 262 en la Sección 13 explica esto de manera un poco más formal. Algo como function foo () {}devuelve el resultado de crear un Functionobjeto [presumiblemente con new Function ()]. Es azúcar de sintaxis.
Clinton Pierce
3
Creo que te falta un return instance;al final. De lo contrario, someObjsolo será undefined. :-)
Matthew Crumley
1
¿Puedo sugerirle que si le importa la modularidad y el ocultamiento de información, simplemente renuncie a esto y comience a usar algo como require.js? Estás a mitad de camino, ¿por qué detenerte aquí? La definición de módulo asincrónico (que es lo que require.js implementa) respalda este caso de uso y le brinda un conjunto de herramientas completo para lidiar con el alcance, el espacio de nombres y la administración de dependencias.
Stijn de Witt
Tenga en cuenta que los paréntesis que rodean la declaración de función son innecesarios, ya que la declaración ya es una expresión debido a la presencia de=
Píldoras de explosión
@StijndeWitt a mitad de camino significa que necesitaría hacer el doble de trabajo para usar require.js pero esto podría ser todo lo que necesita en casos simples.
xr280xr
15

Su código es similar a la construcción menos extraña

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

    ...
};
var someObj = new Foo;
Kennytm
fuente
9
No es solo similar, hace exactamente lo mismo ... con la única excepción de que no podrán reutilizar Foo para crear otro objeto.
kikito
3
La versión del OP se podría reutilizar a través del nuevo someObj.constructor . Aquí, el constructor se agrega explícitamente al espacio de nombres; el estilo correcto depende del propósito previsto de la función. Además, este estilo, aunque ciertamente es el estándar, permite a alguien poblar el espacio de nombres global si se olvida de nuevo antes de Foo .
J Bryan Price
@kikito ¿a qué te refieres con esto no permite reutilizar Foo para crear otro objeto? var newObj = new Foo () debería estar creando una nueva instancia.
Bill Yang
1
@BillYang Eso fue hace 5 años. Ni idea. No he tocado javascript desde entonces.
kikito
12

Para aclarar algunos aspectos y hacer que JSLint de Douglas Crockford no se queje de su código aquí hay algunos ejemplos de instanciación:

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

En el ejemplo 3. la expresión en (...) como valor es una función / constructor. Tiene este aspecto: new (function () {...}) (). Entonces, si omitimos los corchetes finales como en el ejemplo 2, la expresión sigue siendo una llamada de constructor válida y se parece al ejemplo 4.

JSLint de Douglas Crockford "piensa" que desea asignar la función a someObj, no a su instancia. Y después de todo, es solo una advertencia, no un error.

DUzun
fuente