¿Cuál es la palabra clave "nueva" en JavaScript?
La new
palabra clave en JavaScript puede resultar bastante confusa cuando se encuentra por primera vez, ya que la gente tiende a pensar que JavaScript no es un lenguaje de programación orientado a objetos.
- ¿Qué es?
- ¿Qué problemas resuelve?
- ¿Cuándo es apropiado y cuándo no?
Hace 5 cosas:
- Crea un nuevo objeto. El tipo de este objeto es simplemente objeto .
- Establece la propiedad interna, inaccesible
[[prototype]]
(es decir__proto__
) de este nuevo objeto para que sea elprototype
objeto externo y accesible de la función constructora (cada objeto de función tiene automáticamente unaprototype
propiedad). - Hace que la
this
variable apunte al objeto recién creado. - Ejecuta la función constructora, utilizando el objeto recién creado cada vez que
this
se menciona. - Devuelve el objeto recién creado, a menos que la función constructora devuelva una
null
referencia que no sea un objeto. En este caso, se devuelve esa referencia de objeto.
Nota: la función constructora se refiere a la función después de la new
palabra clave, como en
new ConstructorFunction(arg1, arg2)
Una vez hecho esto, si se solicita una propiedad indefinida del nuevo objeto, el script verificará la [[prototype]]
propiedad en el objeto del objeto. Así es como puedes obtener algo similar a la herencia de clases tradicional en JavaScript.
La parte más difícil de esto es el punto número 2. Cada objeto (incluidas las funciones) tiene esta propiedad interna llamada [[prototype]]
. Solo se puede configurar en el momento de la creación del objeto, ya sea con new
, con Object.create
o en función del literal (las funciones predeterminadas son Function.prototype
, los números son Number.prototype
, etc.). Sólo se puede leer con Object.getPrototypeOf(someObject)
. No hay otra forma de obtener o establecer este valor.
Las funciones, además de la [[prototype]]
propiedad oculta, también tienen una propiedad llamada prototipo , y es a esta a la que puedes acceder y modificar para proporcionar propiedades y métodos heredados para los objetos que creas.
Aquí hay un ejemplo:
ObjMaker = function() { this.a = 'first'; };
// `ObjMaker` is just a function, there's nothing special about it
// that makes it a constructor.
ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible `prototype` property that
// we can alter. I just added a property called 'b' to it. Like
// all objects, ObjMaker also has an inaccessible `[[prototype]]` property
// that we can't do anything with
obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called `obj1`. At first `obj1`
// was just `{}`. The `[[prototype]]` property of `obj1` was then set to the current
// object value of the `ObjMaker.prototype` (if `ObjMaker.prototype` is later
// assigned a new object value, `obj1`'s `[[prototype]]` will not change, but you
// can alter the properties of `ObjMaker.prototype` to add to both the
// `prototype` and `[[prototype]]`). The `ObjMaker` function was executed, with
// `obj1` in place of `this`... so `obj1.a` was set to 'first'.
obj1.a;
// returns 'first'
obj1.b;
// `obj1` doesn't have a property called 'b', so JavaScript checks
// its `[[prototype]]`. Its `[[prototype]]` is the same as `ObjMaker.prototype`
// `ObjMaker.prototype` has a property called 'b' with value 'second'
// returns 'second'
Es como herencia de clases porque ahora, cualquier objeto que crees new ObjMaker()
también parecerá haber heredado la propiedad 'b'.
Si quieres algo como una subclase, entonces haz esto:
SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);
SubObjMaker.prototype.c = 'third';
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype
obj2.c;
// returns 'third', from SubObjMaker.prototype
obj2.b;
// returns 'second', from ObjMaker.prototype
obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype
// was created with the ObjMaker function, which assigned a for us
Leí un montón de basura sobre este tema antes de finalmente encontrar esta página , donde se explica muy bien con bonitos diagramas.
Supongamos que tiene esta función:
var Foo = function(){
this.A = 1;
this.B = 2;
};
Si llama a esto como una función independiente así:
Foo();
La ejecución de esta función agregará dos propiedades al window
objeto ( A
y B
). Lo agrega al window
porque window
es el objeto que llamó a la función cuando la ejecutas así, y this
en una función está el objeto que llamó a la función. Al menos en JavaScript.
Ahora, llámalo así con new
:
var bar = new Foo();
Cuando agrega new
a una llamada de función, se crea un nuevo objeto (solo var bar = new Object()
) y this
dentro de la función apunta al nuevo Object
que acaba de crear, en lugar del objeto que llamó a la función. Ahora lo bar
es un objeto con las propiedades A
y B
. Cualquier función puede ser un constructor; simplemente no siempre tiene sentido.
Además de la respuesta de Daniel Howard , esto es lo que new
hace (o al menos parece hacer):
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
Mientras
var obj = New(A, 1, 2);
es equivalente a
var obj = new A(1, 2);