2012-04-26 15 views
10

Sé que esto no es la forma recomendada de hacerlo, pero si declaro las siguientes funciones, y luego les invoco como constructores, lo que será la diferencia (si la hay) entre los objetos resultantes?¿Qué diferencia hay en JavaScript entre una función de constructor y un objeto de devolución de función que se invoca como un constructor?

function Something() { 
    this.foo = "bar"; 
} 

function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
} 

var x = new Something(); 
var y = new something2(); 
var z = something2(); 

I.e. ¿Qué diferenciará entre x, y y z aquí?

¿No sería something2 una manera mucho mejor de escribir el constructor, ya que si usa new o no, no afectará el resultado de la función?

Por cierto debe ser capitalizado something2 aquí? (Supongo que no, ya que Crockford es tan firme en el uso de mayúsculas, ya que las funciones afectarán el espacio de nombres global ...)

+2

Sea un poco de dudas de Sr. Crockford. Si bien tiene muchas cosas buenas que decir, sí tiene opiniones con las que no está de acuerdo. – staticsan

Respuesta

14

En resumen:

new something2() instanceof something2 === false 

En relación con esto, si se amplía su ejemplo para utilizar la propiedad prototipo

Something.prototype.method = function() { }; 
something2.prototype.method = function() { }; 

se encuentra que el prototipo no se hereda en este último caso:

typeof (new Something()).method === "function" 
type (new something2()).method === "undefined" 

The rea La respuesta es que estás recurriendo a una maquinaria subyacente completamente diferente.Llamar con new invoca el mecanismo [[Construct]], que implica establecer la propiedad [[Prototype]] de acuerdo con la propiedad .prototype del constructor.

Pero sucede algo gracioso en los pasos 8--10 del algoritmo [[Construir]]: después de configurar un nuevo objeto vacío y luego adjuntar su [[Prototipo]], hace una [[Llamar] ] al constructor real, utilizando este nuevo objeto vacío-más-prototipo como this. Y luego, en el paso 9, si resulta que ese constructor devolvió algo --- ¡tira el objeto prototypally-bound, passed-as- this que pasó todo el tiempo configurando!

Nota: se puede acceder de un objeto [[Prototype]] (que es diferente de la de un constructor .prototype) con Object.getPrototypeOf:

Object.getPrototypeOf(new Something()) === Something.prototype // steps 5 & 6 
Object.getPrototypeOf(new something2()) === Object.prototype // default 

para responder a algunas meta preguntas:

  • No, no use mayúsculas something2, ya que es una función de fábrica y no un constructor. Si algo está en mayúscula, se espera que tenga semántica de constructor, p. new A() instanceof A.
  • Si le preocupa el peligro de dañar el espacio de nombres global, debe comenzar a usar strict mode, poniendo "use strict"; en la parte superior de sus archivos. Una de las muchas y bonitas limpiezas del modo estricto es que this está predeterminado en undefined, no el objeto global, por lo tanto, p. llamar a un constructor sin new dará como resultado errores en el momento en que el constructor intente adjuntar propiedades al undefined.
  • Las funciones de fábrica (también conocido como el "patrón de cierre") son en general un sustituto razonable para los constructores y las clases, siempre y cuando usted (a) no utilice la herencia; (b) no construye demasiadas instancias de ese objeto. Esto último se debe a que, en el patrón de cierre, adjunta una nueva instancia de cada método a cada objeto recién creado, lo que no es bueno para el uso de la memoria. El mayor beneficio, IMO, del patrón de cierre es la capacidad de usar "private" variables (que son good thing, y no permita que nadie le diga lo contrario: P).
+0

El 'nuevo' también es opcional (o de hecho no es necesario) en el segundo método, pero arruina las cosas cuando se deja afuera en la primera (esta ventana ===) – Matt

+0

@ Matt-puedes defenderte un poco contra eso constructor al ver si 'this' es el objeto global y procede en consecuencia. – RobG

+1

@Matt, @RobG --- o, simplemente ponga '' use strict '; 'en la parte superior de su archivo! Entonces 'this' será' undefined'. – Domenic

2

En el segundo caso, el objeto devuelto no hereda nada del constructor, por lo que no tiene sentido usándolo como tal.

> var x = new Something(); 
> var y = new something2(); 
> var z = something2(); 

es decir, ¿Qué será diferente entre x, y y z aquí?

x hereda de Something, wheres ni y o z Heredar del something2.

No sería something2 ser una mucho mejor manera de escribir el constructor, ya que si se utiliza nueva o no, no afectará el resultado de la función ?

no hay ningún punto en llamar something2 como constructor porque el objeto se vuelve no es el objeto de nueva construcción asignado a su this que hereda de something2.prototype, que es lo que otros podrían esperar obtener al llamar new something2().

BTW algo debería estar en mayúsculas aquí? (Yo no asumo desde Crockford es tan inflexible en la capitalización, para funciones serán Clobber el espacio de nombres global ...)

No, porque calificó como constructor es un poco inútil, por lo que la caracteriza como una sería engañoso

1

invocar una función como un constructor (es decir con el nuevo keyword) ejecuta los pasos siguientes:

  1. crear un nuevo objeto
  2. establecer el prototipo de ese objeto al objeto en la propiedad prototype de la función
  3. ejecutar la función constructora en el contexto de ese objeto (es decir, this es el nuevo objeto)
  4. devolver ese objeto (si la construcción o no tiene ninguna declaración return)

Por lo tanto, su segunda solución simplemente devolverá un objeto simple con una propiedad "foo". Pero ni y ni z son instanceof Something2 y no heredan de ese prototipo. Hay funciones como esa, sí, pero no deberían llamarse constructores (sin nombres en mayúsculas, sin invocación con new). Pertenecen al patrón de fábrica.

Si desea un constructor que puede ser ejecutado sin recursos nuevos, utilizar ese código:

function Something(params) { 
    if (! this instanceof Something) 
     return new Something(params); 
    // else use "this" as usual 
    this.foo = "bar"; 
    ... 
} 
1

yo diría que lo más importante sería el prototipo de los objetos devueltos.

function Something() { 
     this.foo = "bar"; 
    } 

    Something.prototype = { 
    // Something prototype code 
    hello: function(){ 
    //... 
    } 
    } 

    function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
    } 

    something2.prototype = { 
     // something2 prototype code 
     greetings : function() { 
     //... 
     } 
    } 

    var x = new Something(); 
    var y = new something2(); 
    var z = something2(); 

    typeof x.hello === function // should be true 
    typeof y.greetings === undefined // should be true 
    typeof z.greetings === undefined // should be true 

En otras palabras, yo diría que no estás crear instancias de objetos withe something2, que está creando puramente nuevos objetos que heredan del objeto.

  1. Algo() le dará nuevos objetos de tipo "algo" cuando se utiliza la nueva palabra clave.
  2. something2() le dará nuevos objetos de tipo "Objeto", que devolverán inmediatamente un nuevo objeto vacío.
  3. nueva something2 es ineficiente, porque va a crear un ámbito en blanco, a partir del cual se crea un nuevo objeto

    var that = {}; 
    

    lo que equivale a

    var that = new Object(); 
    
Cuestiones relacionadas