2012-02-16 9 views
10

he encontrado algo muy extraño hoy en día: Si crea objetos con una función constructora y la palabra clave new, pero return una función desde el constructor, se comporta de esta manera:El uso de 'retorno' al crear objetos con 'nuevo'

  1. El "objeto" creado recientemente es una función.
  2. Esa nueva función puede ser invocada como normal, sin embargo ...
  3. Si mantiene una referencia a this en la función constructora, this referencia a un objeto que se ha creado correctamente desde el constructor. Es lo que espera que se devuelva desde new.

He aquí un ejemplo:

function Constructor() { 
    var self = this; 

    this.name = 'instance'; 
    return function() { 
    return self; 
    } 
} 

Así que si instanciado como esto: var instance = new Constructor() A continuación se traduciría:

typeof instance //returns "function" 
typeof instance() //returns "object" 
instance()   //returns { name: 'instance' } 

así que supongo que tengo tres preguntas:

  1. ¿Es esto legal y funciona? k cross-browser? Es realmente increíble y creo que se puede usar de muchas maneras, pero ¿es confiable este comportamiento?
  2. ¿Qué sucede en el fondo que causa este comportamiento?
  3. (quizás responda por 2, pero ...) ¿Está el nuevo objeto (el que se menciona con 'this') dentro de la nueva instancia, de modo que sea todo independiente y el recolector de basura lo limpie correctamente?
+0

Es un error 'devolver' cualquier cosa de un constructor, lo último que escuché. –

Respuesta

7
  1. Sí, mientras que un constructor por defecto devuelve el nuevo objeto se construye (que se hace referencia por this), puede sustituir ese valor de retorno , siempre y cuando regresa un objeto. Como una función es un objeto, puede devolverla tal como está en su ejemplo. El objeto recién creado es no una función en sí, pero su función devuelta hace referencia al objeto recién creado en su ámbito de variable.

  2. Ver # 1

  3. Esto es porque una función crea un cierre, por lo que sigue para hacer referencia a la variable self, que pasa a hacer referencia al objeto real que se está construyendo. Así que no diría que está "adentro" de nada, sino que simplemente es parte del alcance variable de la función.

Lo que hay que entender es que su función no es diferente de cualquier otra función. Al igual que si en su lugar hubiera devuelto una matriz, solo tendría una matriz regular, que podría hacer referencia al nuevo objeto.

function Constructor() { 

    this.name = 'instance'; 
    return [ this ]; // Instead return an Array that references the new object 
} 
+0

¡Ya veo! Entonces, ¿la variable "instancia" en mi ejemplo es un * cierre *, no una función normal? Y ese cierre contiene tanto la función devuelta como el objeto instanciado, a los que se puede acceder con "this"? –

+2

@KevinMcTigue: Bueno, todas las funciones son cierres. Es simplemente parte de la implementación interna de funciones en JavaScript. Lo que hace que una función sea un cierre es que tiene una referencia permanente al ámbito de la variable original donde se creó. Por lo tanto, solo está devolviendo una función antigua simple, que se comporta como cualquier otra función, ya que no pierde de vista su alcance variable * (que incluye la variable 'self', que hace referencia a su nuevo objeto) *. –

2

Bueno, esa es una muy buena pregunta y, como habrás adivinado, no se responde fácilmente.

Para decirlo simplemente:
1) Sí y Sí; esta es una de las características increíbles que no encuentras en los lenguajes de programación "tradicionales".
2) lea sobre el cierre (enlaces más abajo)
3) Sí (por favor leer más)

Usted debe leer más sobre Javascript Cierres: http://jibbering.com/faq/notes/closures/
http://www.javascriptkit.com/javatutors/closures.shtml (aquí tienes algunos ejemplos de buen funcionamiento)

y, más particularmente, el modelo de herencia parasitaria:
http://blog.higher-order.net/2008/02/21/javascript-parasitic-inheritance-power-constructors-and-instanceof/

Espero que esto ayude

+0

Gracias por los enlaces. El segundo es muy interesante. Hoy aprendí un nuevo término: Power Constructor –

1

esto es lo que se llama un closure

lo que hace es crear un entorno de código de auto-contenido (comúnmente conocido como un objeto)

typeof instance //returns "function" - since it's not "fired" or called. just returns the function declaration (correct me if i'm wrong) 
typeof instance() //returns "object" - it returns an object since you called it 
instance()   //returns an object also - you called it, but you didn't store it 

un ejemplo de un objeto construido usando un cierre:

function Constructor() { 
    var privateProperty = 'private'; 
    var privateMethod = function(){ 
     alert('called from public method'); 
    }; 

    //return only what's to be seen in public 
    return { 
     publicProperty: 'im public', 
     publicMethod: function(){ 
      alert('called from public method'); 
     } 
     getter: privateMethod //assign to call the private method 
    } 
} 

var myObj = Constructor(); 
var pubProp = myObj.publicProperty; // pubProp = 'im public' 
myObj.publicMethod()    //alert: 'called from public method'; 
myObj.getter()      //alert: 'called from public method'; 

//cannot access since "private": 
myObj.privateProperty 
myObj.privateMethod 
+1

_ "lo que hace es crear un entorno de código independiente (comúnmente conocido como un objeto)" _ - Comúnmente conocido como "cierre". No es un "objeto", al menos no en el sentido de ser un objeto JS. Además, si su función devuelve explícitamente un objeto, no es una buena práctica llamarlo con 'new' porque eso es engañoso; si usa' new', esperaría que el resultado fuera una instancia de 'Constructor'. – nnnnnn

+0

De acuerdo con la página MDN sobre cierres: * "Un cierre es un tipo especial de objeto que combina dos cosas: una función y el entorno en el que se creó esa función." * –

+1

@nnnnnn Acabo de explicarlo en términos simples. aunque no es un objeto, es común usarlo para emular un objeto (que tiene propiedades privadas y públicas). en cuanto a lo 'nuevo' ... no sabía de eso. – Joseph

Cuestiones relacionadas