2009-12-30 13 views
83

¿Cuáles son las circunstancias exactas para las cuales una declaración de devolución en Javascript puede devolver un valor distinto de this cuando se invoca un constructor con la palabra clave new?¿Qué valores puede devolver un constructor para evitar devolver esto?

Ejemplo:

function Foo() { 
    return something; 
} 

var foo = new Foo(); 

Si no me equivoco, si something es un no-función primitiva, serán devueltos this. De lo contrario, se devuelve something. ¿Es esto correcto?

IOW, ¿qué valores puede something llevar a causar (new Foo() instanceof Foo) === false?

+0

relacionada: [? Lo que se devuelve desde un constructor] (http://stackoverflow.com/q/3350215/1048572) – Bergi

Respuesta

150

la condición exacta se describe en la propiedad [[Construct]] interna, que se utiliza por el operador new:

Desde la tercera ECMA-262. Especificación Edición:

13.2.2[[Construct]]

Cuando la propiedad de un objeto [[Construct]]FFunction es llamada, se toman las siguientes medidas:

  1. Crear un nuevo objeto ECMAScript nativa.
  2. Establezca la propiedad [[Class]] de Result(1) en "Object".
  3. Obtenga el valor de la propiedad prototipo de F.
  4. Si Result(3) es un objeto, establezca la propiedad [[Prototype]] de Result(1) en Result(3).
  5. Si Result(3) no es un objeto, establezca la propiedad de [[Prototype]]Result(1) al objeto Object prototipo original como se describe en 15.2.3.1.
  6. invocar la propiedad [[Call]] de F, proporcionando Result(1) como el valor this y proporcionar la lista de argumentos pasado a [[Construct]] como los valores de los argumentos .
  7. Si Type (Result(6)) es Object luego devuelva Result(6).
  8. Vuelta Result(1).

mirada a los pasos 7 y 8, se devolverá el objeto nuevo sólo si el tipo de Result(6) (el valor devuelto por la función F constructor ) es no un objeto.

+5

+1 gran uso de referencia –

+12

Nota adicional: typeof (nulo) es "objeto", sin embargo, devolver null no * desencadena el paso 7 –

+3

@BT: Probando en mi instalación de node.js, parece que '' Type (x) 'is' Object' "en la especificación como se citó arriba significa que" 'x instanceof Object' es' true' ", * not *" 'typeof (x) === 'object'' is' true' ". Por ejemplo, 'instancia nula de Object' se evalúa como false (y de hecho no se puede devolver' null' de un constructor), y 'new String (" asdf ") instanceof Object' se evalúa como verdadero (y de hecho se puede devolver un String del constructor ...) –

-1

Cuando usa la palabra clave new, se crea un objeto. Luego se llama a la función para inicializar el objeto.

No hay nada que la función pueda hacer para evitar que se cree el objeto, como se hace antes de llamar a la función.

+2

No estoy tomando al respecto. Sé que puedes hacer lo que yo digo (lo he intentado), pero no conozco los casos definitivos para los que ocurre. En mi ejemplo, es posible terminar con '(foo instanceof Foo) === false'. –

+1

@trin - respuesta corta no – JonH

+0

¿Por qué el voto a favor? Si no explica qué es lo que cree que está mal, no puede mejorar la respuesta. – Guffa

2

No he podido encontrar ninguna documentación al respecto, pero creo que estás en lo cierto. Por ejemplo, puede devolver new Number(5) desde un constructor, pero no el literal 5 (que se ignora y se devuelve this en su lugar).

+0

Por cierto: esto sucede porque 'new Number (5)' (o incluso 'Number (5)', ya que está destinado a funcionar igual si se llama como una función) crea un número * object *, por ejemplo esto significa que while '' '== false', '!! new String (' ') == true'. –

+0

Dos años después y listo para responder para corregir mi propio comentario, esto funciona: 'function One() {return new Number (1)}' pero esto no: 'function One() {return Number (1)}' –

0

Como nota al margen, el valor de retorno this es solo una parte de la ecuación.

Por ejemplo, considere esto:

function Two() { return new Number(2); } 
var two = new Two; 
two + 2; // 4 
two.valueOf = function() { return 3; } 
two + 2; // 5 
two.valueOf = function() { return '2'; } 
two + 2; // '22' 

Como se puede ver, .valueOf() se utiliza internamente y puede ser explotado para la diversión y el beneficio. Incluso puede crear efectos secundarios, por ejemplo:

function AutoIncrementingNumber(start) { 
    var n = new Number, val = start || 0; 
    n.valueOf = function() { return val++; }; 
    return n; 
} 
var auto = new AutoIncrementingNumber(42); 
auto + 1; // 43 
auto + 1; // 44 
auto + 1; // 45 

me puedo imaginar esto debe tener algún tipo de aplicación práctica. Y no tiene por qué ser explícitamente un Number o bien, si se agrega .valueOf a cualquier objeto que puede comportarse como un número:

({valueOf: function() { return Math.random(); }}) + 1; // 1.6451723610516638 

Puede aprovechar esto para hacer que un objeto que siempre devuelve un nuevo GUID, por ejemplo.

2

Ejemplos concretos http://jsbin.com/zivivucahi/1/edit?html,js,console,output

/* 
ECMA 262 v 5 
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf 
"4.3.2 
primitive value 
member of one of the types Undefined, Null, Boolean, Number, Symbol, or String as defined in clause 6" 
*/ 

var Person = function(x){ 
    return x; 

}; 


console.log(Person.constructor); 
console.log(Person.prototype.constructor); 
console.log(typeof(Person)); 
console.log(typeof(Person.prototype)); 

function log(x){ 
    console.log(x instanceof Person); 
    console.log(typeof x); 
    console.log(typeof x.prototype); 
} 

log(new Person(undefined)); 
log(new Person(null)); 
log(new Person(true)); 
log(new Person(2)); 
log(new Person("")); 

//returns a function not an object 
log(new Person(function(){})); 


//implementation? 
//log(new Person(Symbol('%'))); 
Cuestiones relacionadas