¿Cuál es la palabra clave 'nueva' en JavaScript?

1744

La newpalabra clave en JavaScript puede ser bastante confusa cuando se encuentra por primera vez, ya que las personas tienden 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?
Alon Gubkin
fuente
10
Además, hilo relacionado - stackoverflow.com/questions/383402/…
Chetan Sastry
primero lean estos ejemplos, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Martian2049

Respuestas:

2145

Hace 5 cosas:

  1. Crea un nuevo objeto. El tipo de este objeto es simplemente objeto .
  2. Establece la propiedad interna, inaccesible, [[prototipo]] (es decir, __proto__ ) de este nuevo objeto para que sea el objeto prototipo externo, accesible y accesible de la función constructora (cada objeto de función tiene automáticamente una propiedad prototipo ).
  3. Hace que la thisvariable apunte al objeto recién creado.
  4. Ejecuta la función constructora, utilizando el objeto recién creado cada vez que thisse menciona.
  5. Devuelve el objeto recién creado, a menos que la función constructora devuelva una nullreferencia que no sea de objeto. En este caso, se devuelve esa referencia de objeto en su lugar.

Nota: la función constructora se refiere a la función después de la newpalabra clave, como en

new ConstructorFunction(arg1, arg2)

Una vez hecho esto, si se solicita una propiedad indefinida del nuevo objeto, el script verificará el objeto [[prototipo]] del objeto en su lugar. Así es como puede obtener algo similar a la herencia de clase 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 [[prototipo]] . Puede solamente puede establecer en tiempo de creación de objeto, ya sea con nuevo , con Object.create , o basado en el literal (funciones predeterminada para Function.prototype, números a Number.prototype, etc.). Solo se puede leer con Object.getPrototypeOf (someObject) . No hay otra forma de establecer o leer este valor.

Las funciones, además de la propiedad oculta [[prototipo]] , también tienen una propiedad llamada prototipo , y es esto a lo que puede acceder y modificar para proporcionar propiedades y métodos heredados para los objetos que crea.


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 the same
// as {}. 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 la herencia de clases porque ahora, cualquier objeto que hagas usando 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 esto se explica muy bien con buenos diagramas.

Daniel Howard
fuente
47
Solo quería agregar: De hecho, hay una manera de acceder al [[prototipo]] interno, mediante __proto__. Sin embargo, esto no es estándar y solo es compatible con navegadores relativamente nuevos (y no todos). Está surgiendo una forma estandarizada, a saber, Object.getPrototypeOf (obj), pero es Ecmascript3.1 y solo se admite en los nuevos navegadores, nuevamente. Sin embargo, generalmente se recomienda no usar esa propiedad, las cosas se complican muy rápido allí.
Blub
99
Pregunta: ¿qué sucede de manera diferente si ObjMakerse define como una función que devuelve un valor?
Jim Blackler
13
@LonelyPixel newexiste para que no tenga que escribir métodos de fábrica para construir / copiar funciones / objetos. Significa: "Copie esto, haciéndolo como su 'clase' principal; hágalo de manera eficiente y correcta; y almacene información de herencia a la que solo yo pueda acceder, JS, internamente". Para hacerlo, modifica el interno prototypede otro modo inaccesible del nuevo objeto para encapsular opacamente los miembros heredados, imitando las cadenas de herencia OO clásicas (que no son modificables en tiempo de ejecución). Puede simular esto sin new, pero la herencia será modificable en tiempo de ejecución. ¿Bueno? ¿Malo? Depende de usted.
Ingeniero
11
un pequeño punto para agregar: una llamada a un constructor, cuando está precedida por la nueva palabra clave, devuelve automáticamente el objeto creado; no es necesario devolverlo explícitamente desde el constructor.
charlie roberts
77
Hay una nota que dice Notice that this pattern is deprecated!. ¿Cuál es el patrón actualizado correcto para establecer el prototipo de una clase?
Tom Pažourek
400

Supongamos que tiene esta función:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};

Si llama a esto como una función independiente de esta manera:

Foo();

La ejecución de esta función agregará dos propiedades al windowobjeto ( Ay B). Lo agrega al windowporque windowes el objeto que llamó a la función cuando lo ejecuta así, y thisen una función está el objeto que llamó a la función. En Javascript al menos.

Ahora, llámalo así con new:

var bar = new Foo();

Lo que sucede cuando agrega newa una llamada de función es que se crea un nuevo objeto (solo var bar = new Object()) y que thisdentro de la función apunta al nuevo Objectque acaba de crear, en lugar de al objeto que llamó a la función. Entonces, barahora es un objeto con las propiedades Ay B. Cualquier función puede ser un constructor, simplemente no siempre tiene sentido.

JulianR
fuente
77
Depende del contexto de ejecución. En mi caso (secuencia de comandos Qt) es solo un objeto global.
Maxym
2
¿Esto causará más uso de memoria?
Jürgen Paul
2
porque ventana es el objeto que llamó a la función , debe ser: porque ventana es el objeto que contiene la función.
Dávid Horváth
1
@Taurus En un navegador web, una función sin método será un método windowimplícito. Incluso en un cierre, incluso si es anónimo. Sin embargo, en el ejemplo es una invocación de método simple en la ventana: Foo();=> [default context].Foo();=> window.Foo();. En esta expresión windowestá el contexto (no solo la persona que llama , lo que no importa).
Dávid Horváth
1
@Taurus Básicamente sí. Sin embargo, en ECMA 6 y 7 las cosas son más complejas (ver lambdas, clases, etc.).
Dávid Horváth
164

Además de la respuesta de Daniel Howard, esto es lo que newhace (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);
basilikum
fuente
73
Descubrí que javascript es más fácil de entender que el inglés: v
damphat
Excelente respuesta Tengo una pequeña pregunta: ¿cómo puede ser posible func.prototypeser null? ¿Podría por favor explicar un poco sobre eso?
Tom Pažourek
66
@tomp, puede anular la propiedad del prototipo, simplemente escribiendo A.prototype = null;En ese caso se new A()obtendrá un objeto, esos puntos prototipo internos al Objectobjeto: jsfiddle.net/Mk42Z
basilikum
2
El tipo de verificación puede ser incorrecto porque un objeto host podría producir algo diferente a "objeto" o "función". Para probar si algo es un objeto, prefiero Object(ret) === ret.
Oriol
2
@Oriol gracias por el comentario. Es cierto lo que dice y cualquier prueba real debe hacerse de manera más sólida. Sin embargo, creo que para esta respuesta conceptual, la typeofprueba simplemente hace que sea más fácil entender lo que está sucediendo detrás de escena.
basilikum
109

Para que los principiantes lo entiendan mejor

pruebe el siguiente código en la consola del navegador.

function Foo() { 
    return this; 
}

var a = Foo();       //returns window object
var b = new Foo();   //returns empty object of foo

a instanceof Window;  // true
a instanceof Foo;     // false

b instanceof Window;  // false
b instanceof Foo;     // true

Ahora puedes leer la respuesta wiki de la comunidad :)

Anulal S
fuente
44
Buena respuesta. Además, omitir return this;produce la misma salida.
Nelu
37

así que probablemente no sea para crear instancias de objetos

Se usa exactamente para eso. Define un constructor de funciones así:

function Person(name) {
    this.name = name;
}

var john = new Person('John');

Sin embargo, el beneficio adicional que tiene ECMAScript es que puede ampliar con la .prototypepropiedad, para que podamos hacer algo como ...

Person.prototype.getName = function() { return this.name; }

Todos los objetos creados a partir de este constructor ahora tendrán un getNamedebido a la cadena de prototipo a la que tienen acceso.

meder omuraliev
fuente
66
los constructores de funciones se usan como clases, no hay classpalabras clave pero puedes hacer lo mismo.
meder omuraliev
Hay una palabra clave de clase - la clase está reservada para uso futuro
Greg
11
Por cierto que es por eso que utiliza .className no .class para establecer una clase CSS
Greg
23
Se debe capitalizar Persona por convención.
eomeroff
27

JavaScript es un lenguaje de programación orientado a objetos y se usa exactamente para crear instancias. Está basado en prototipos, más que en clases, pero eso no significa que no esté orientado a objetos.

Miguel
fuente
66
Me gusta decir que JavaScript parece estar aún más orientado a objetos que todos esos lenguajes basados ​​en clases. En JavaScript, todo lo que escribe se convierte inmediatamente en un objeto, pero en los lenguajes basados ​​en clases primero escribe declaraciones y solo más tarde crea instancias específicas (objetos) de clases. Y el prototipo de JavaScript parece recordar vagamente todo ese material VTABLE para lenguajes basados ​​en clases.
JustAMartin
14

Javascript es un lenguaje de programación dinámico que admite el paradigma de programación orientado a objetos, y se utiliza para crear nuevas instancias de objetos.

Las clases no son necesarias para los objetos: Javascript es un lenguaje basado en prototipos .

Greg
fuente
12

Ya hay algunas respuestas muy buenas, pero estoy publicando una nueva para enfatizar mi observación en el caso III a continuación sobre lo que sucede cuando tiene una declaración de retorno explícita en una función que está newactivando. Echa un vistazo a los siguientes casos:

Caso I :

var Foo = function(){
  this.A = 1; 
  this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

Arriba hay un caso simple de llamar a la función anónima señalada por Foo. Cuando llama a esta función, vuelve undefined. Como no hay una declaración de retorno explícita, el intérprete de JavaScript inserta con fuerza una return undefined;declaración al final de la función. Aquí ventana es el objeto de invocación (contextual this) que obtiene nueva Ay Bpropiedades.

Caso II :

var Foo = function(){
  this.A = 1;
  this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1

Aquí, el intérprete de JavaScript que ve la newpalabra clave crea un nuevo objeto que actúa como el objeto de invocación (contextual this) de la función anónima señalada por Foo. En este caso Ay Bpropiedades convertido en el objeto recién creado (en lugar del objeto de la ventana). Como no tiene ninguna declaración de retorno explícita, el intérprete de JavaScript inserta a la fuerza una declaración de retorno para devolver el nuevo objeto creado debido al uso de la newpalabra clave.

Caso III :

var Foo = function(){
  this.A = 1;
  this.B = 2;
  return {C:20,D:30}; 
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

Aquí nuevamente el intérprete de JavaScript al ver la newpalabra clave crea un nuevo objeto que actúa como el objeto de invocación (contextual this) de la función anónima señalada por Foo. Nuevamente, Ay Bconviértase en propiedades en el objeto recién creado. Pero esta vez tiene una declaración de retorno explícita, por lo que el intérprete de JavaScript no hará nada por sí mismo.

Lo que hay que tener en cuenta en el caso III es que el objeto que se está creando debido a la newpalabra clave se perdió de su radar. baren realidad apunta a un objeto completamente diferente que no es el que el intérprete de JavaScript creó debido a la newpalabra clave.

Citando a David Flanagan de JavaScripit: The Definitive Guide (6th Edition), cap. 4, página # 62:

Cuando se evalúa una expresión de creación de objeto, JavaScript primero crea un nuevo objeto vacío, como el creado por el inicializador de objeto {}. A continuación, invoca la función especificada con los argumentos especificados, pasando el nuevo objeto como el valor de esta palabra clave. La función puede usar esto para inicializar las propiedades del objeto recién creado. Las funciones escritas para su uso como constructores no devuelven un valor, y el valor de la expresión de creación del objeto es el objeto recién creado e inicializado. Si un constructor devuelve un valor de objeto, ese valor se convierte en el valor de la expresión de creación del objeto y el objeto recién creado se descarta.

--- Información adicional ---

Las funciones utilizadas en el fragmento de código de los casos anteriores tienen nombres especiales en el mundo JS de la siguiente manera:

Caso I y II - Función de constructor

Caso III - Función de fábrica. Las funciones de fábrica no deben usarse con la newpalabra clave que he hecho para explicar el concepto en el hilo actual.

Puedes leer sobre la diferencia entre ellos en este hilo.

RBT
fuente
su caso 3, es una observación gr8
appu
5

a veces el código es más fácil que las palabras:

var func1 = function (x) { this.x = x; }                    // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; }   // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;

A1 = new func1(1);      // has A1.x  AND  A1.y
A2 =     func1(1);      // undefined ('this' refers to 'window')
B1 = new func2(2);      // has B1.x  ONLY
B2 =     func2(2);      // has B2.x  ONLY

Para mí, siempre que no prototipo, uso el estilo de func2, ya que me da un poco más de flexibilidad dentro y fuera de la función.

rsbkk
fuente
3
B1 = new func2(2); <- ¿Por qué esto no tendrá B1.y ?
sunny_dev
@sunny_dev No soy un experto en JS, pero probablemente porque func2 está devolviendo directamente un valor (objeto z), en lugar de trabajar / regresar con valores internos (esto)
Eagle
5

La newpalabra clave cambia el contexto en el que se ejecuta la función y devuelve un puntero a ese contexto.

Cuando no usa la newpalabra clave, el contexto en el que se Vehicle()ejecuta la función es el mismo contexto desde el que está llamando a la Vehiclefunción. La thispalabra clave se referirá al mismo contexto. Cuando lo usa new Vehicle(), se crea un nuevo contexto para que la palabra clave thisdentro de la función se refiera al nuevo contexto. Lo que obtienes a cambio es el contexto recién creado.

Juzer Ali
fuente
Esa es una respuesta muy perspicaz en términos de alcance. Gr8 además de la respuesta.
appu
3

La newpalabra clave es para crear nuevas instancias de objeto. Y sí, JavaScript es un lenguaje de programación dinámico, que admite el paradigma de programación orientado a objetos. La convención sobre la denominación de objetos es, siempre use letras mayúsculas para los objetos que se supone que deben ser instanciados por la nueva palabra clave.

obj = new Element();
Erenon
fuente
2

Resumen:

La newpalabra clave se usa en JavaScript para crear un objeto a partir de una función constructora. La newpalabra clave debe colocarse antes de la llamada a la función del constructor y hará lo siguiente:

  1. Crea un nuevo objeto.
  2. Establece el prototipo de este objeto en la propiedad prototipo de la función constructora.
  3. Vincula la thispalabra clave al objeto recién creado y ejecuta la función constructora
  4. Devuelve el objeto recién creado

Ejemplo:

function Dog (age) {
  this.age = age;
}

const doggie = new Dog(12);

console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true

Qué sucede exactamente:

  1. const doggie dice: Necesitamos memoria para declarar una variable.
  2. El operador de asignación =dice: Vamos a inicializar esta variable con la expresión después de=
  3. La expresión es new Dog(12). El motor JS ve la nueva palabra clave, crea un nuevo objeto y establece el prototipo en Dog.prototype
  4. La función constructora se ejecuta con el thisvalor establecido para el nuevo objeto. En este paso es donde se asigna la edad al nuevo objeto perrito creado.
  5. El objeto recién creado se devuelve y se asigna a la variable doggie.
Willem van der Veen
fuente
1

La newpalabra clave crea instancias de objetos utilizando funciones como constructor. Por ejemplo:

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

Las instancias heredan de la prototypefunción constructora. Entonces, dado el ejemplo anterior ...

foo.bar; // 'bar'
sin párpados
fuente
2
La nueva palabra clave básicamente asocia la función como el constructor ya; No necesitas devolver nada. Simplemente puede hacer: function foo (x) {this.bar = x; } var obj = nuevo foo (10); alerta (obj.bar);
reko_t
No necesita devolver objetos de la función de constructor a menos que lo desee específicamente, para un propósito. Por ejemplo, si tiene que devolver una instancia de objeto específica en lugar de crear un nuevo objeto cada vez (por cualquier razón). En su ejemplo, sin embargo, es totalmente innecesario.
Chetan Sastry
Bueno, fue un ejemplo. Usted puede devolver un objeto. Hay muchos patrones utilizados en este escenario, proporcioné uno como "por ejemplo", de ahí mis palabras "por ejemplo".
párpado
1

Bueno, JavaScript per si puede diferir mucho de una plataforma a otra, ya que siempre es una implementación de la especificación original EcmaScript.

En cualquier caso, independientemente de la implementación, todas las implementaciones de JavaScript que sigan la especificación EcmaScript correcta le proporcionarán un lenguaje orientado a objetos. De acuerdo con el estándar ES:

ECMAScript es un lenguaje de programación orientado a objetos para realizar cálculos y manipular objetos computacionales dentro de un entorno host.

Entonces, ahora que hemos acordado que JavaScript es una implementación de EcmaScript y, por lo tanto, es un lenguaje orientado a objetos. La definición de la newoperación en cualquier lenguaje orientado a objetos, dice que dicha palabra clave se utiliza para crear una instancia de objeto de una clase de cierto tipo (incluidos los tipos anónimos, en casos como C #).

En EcmaScript no usamos clases, como puede leer en las especificaciones:

ECMAScript no utiliza clases como las de C ++, Smalltalk o Java. En cambio, los objetos se pueden crear de varias maneras, incluso a través de una notación literal o mediante constructores que crean objetos y luego ejecutan código que inicializa todos o parte de ellos mediante la asignación de valores iniciales a sus propiedades. Cada constructor es una función que tiene una propiedad llamada - prototipo ‖ que se usa para implementar la herencia basada en prototipos y las propiedades compartidas. Los objetos se crean
utilizando constructores en nuevas expresiones; por ejemplo, nueva Fecha (2009,11) crea un nuevo objeto Fecha. Invocar un constructor sin usar nuevo tiene consecuencias que dependen del constructor. Por ejemplo, Date () produce una representación de cadena de la fecha y hora actuales en lugar de un objeto.

João Pinho
fuente