Cuál es la diferencia entre
var A = function () {
this.x = function () {
//do something
};
};
y
var A = function () { };
A.prototype.x = function () {
//do something
};
javascript
prototype
this
sw234
fuente
fuente
a1.x !== a2.x
:; en prototipo:a1.x === a2.x
Respuestas:
Los ejemplos tienen resultados muy diferentes.
Antes de mirar las diferencias, se debe tener en cuenta lo siguiente:
[[Prototype]]
propiedad privada de la instancia .myObj.method()
), esto dentro del método hace referencia al objeto. Cuando esto no se establece mediante la llamada o mediante el uso de bind , el valor predeterminado es el objeto global (ventana en un navegador) o en modo estricto, permanece indefinido.Así que aquí están los fragmentos en cuestión:
En este caso, a la variable
A
se le asigna un valor que es una referencia a una función. Cuando esta función se llama usandoA()
, la función es esto no está establecido por la llamada por lo que por defecto es el objeto global y la expresiónthis.x
es efectivawindow.x
. El resultado es que se asigna una referencia a la expresión de función en el lado derechowindow.x
.En el caso de:
ocurre algo muy diferente. En la primera línea, a la variable
A
se le asigna una referencia a una función. En JavaScript, todos los objetos de funciones tienen una propiedad prototipo por defecto, por lo que no hay un código separado para crear un objeto A.prototype .En la segunda línea, a A.prototype.x se le asigna una referencia a una función. Esto creará una propiedad x si no existe, o asignará un nuevo valor si lo hace. Entonces, la diferencia con el primer ejemplo en el que la propiedad x del objeto está involucrada en la expresión.
Otro ejemplo está abajo. Es similar al primero (y tal vez lo que querías preguntar):
En este ejemplo, el
new
operador se ha agregado antes de la expresión de la función para que la función se llame como un constructor. Cuando se llama connew
, la función this está configurada para hacer referencia a un nuevo Objeto cuya[[Prototype]]
propiedad privada está configurada para hacer referencia al prototipo público del constructor . Entonces, en la declaración de asignación, lax
propiedad se creará en este nuevo objeto. Cuando se llama como constructor, una función devuelve su este objeto por defecto, así que no hay necesidad de una separadareturn this;
comunicado.Para verificar que A tiene una propiedad x :
Este es un uso poco común de new ya que la única forma de hacer referencia al constructor es a través de A.constructor . Sería mucho más común hacer:
Otra forma de lograr un resultado similar es usar una expresión de función invocada inmediatamente:
En este caso, se le
A
asigna el valor de retorno de llamar a la función en el lado derecho. Aquí nuevamente, dado que esto no se establece en la llamada, hará referencia al objeto global ythis.x
es efectivowindow.x
. Como la función no devuelve nada,A
tendrá un valor deundefined
.Estas diferencias entre los dos enfoques también se manifiestan si está serializando y des serializando sus objetos Javascript a / desde JSON. Los métodos definidos en el prototipo de un objeto no se serializan cuando serializa el objeto, lo que puede ser conveniente cuando, por ejemplo, desea serializar solo las porciones de datos de un objeto, pero no sus métodos:
Preguntas relacionadas :
Nota al margen: puede que no haya ahorros significativos de memoria entre los dos enfoques, sin embargo, usar el prototipo para compartir métodos y propiedades probablemente usará menos memoria que cada instancia que tenga su propia copia.
JavaScript no es un lenguaje de bajo nivel. Puede no ser muy valioso pensar en la creación de prototipos u otros patrones de herencia como una forma de cambiar explícitamente la forma en que se asigna la memoria.
fuente
null
), pero esto es muy diferente de laprototype
propiedad, que está en funciones y para la cual se establece el prototipo de todas las instancias cuando se construyennew
. No puedo creer que esto realmente haya recibido 87 votos a favor :-("The language is functional"
¿Estás seguro de que esto es lo que significa funcional?A
como una función, y la otra mitad se trata de formas oscuras y poco ortodoxas de hacer Algo sencillo.Como otros han dicho en la primera versión, el uso de "esto" da como resultado que cada instancia de la clase A tenga su propia copia independiente del método de función "x". Mientras que usar "prototipo" significará que cada instancia de la clase A usará la misma copia del método "x".
Aquí hay un código para mostrar esta sutil diferencia:
Como otros han mencionado, hay varias razones para elegir un método u otro. Mi muestra solo pretende demostrar claramente la diferencia.
fuente
this
objeto, que es el propietario del método. es decir, el método no tiene ningún objeto que sea su propietario. En este caso hay unthis
objeto, como se muestra en la clase A en el ejemplo.Tome estos 2 ejemplos:
vs.
La mayoría de las personas aquí (especialmente las respuestas mejor calificadas) trataron de explicar cómo son diferentes sin explicar POR QUÉ. Creo que esto está mal y si entiendes los fundamentos primero, la diferencia será obvia. Intentemos explicar los fundamentos primero ...
a) Una función es un objeto en JavaScript. CADA objeto en JavaScript obtiene una propiedad interna (es decir, no puede acceder a ella como otras propiedades, excepto tal vez en navegadores como Chrome), a menudo referidos como
__proto__
(en realidad puede escribiranyObject.__proto__
Chrome para ver a qué se refiere. Esto es solo eso , una propiedad, nada más. Una propiedad en JavaScript = una variable dentro de un objeto, nada más. ¿Qué hacen las variables? Señalan cosas.Entonces, ¿a qué
__proto__
apunta esta propiedad? Bueno, generalmente otro objeto (explicaremos por qué más adelante). La única forma de forzar JavaScript para que la__proto__
propiedad NO apunte a otro objeto es usarlovar newObj = Object.create(null)
. Incluso si hace esto, la__proto__
propiedad TODAVÍA existe como una propiedad del objeto, simplemente no apunta a otro objeto, sino a élnull
.Aquí es donde la mayoría de las personas se confunden:
Cuando crea una nueva función en JavaScript (que también es un objeto, ¿recuerda?), En el momento en que se define, JavaScript crea automáticamente una nueva propiedad en esa función llamada
prototype
. Intentalo:A.prototype
es TOTALMENTE DIFERENTE de la__proto__
propiedad. En nuestro ejemplo, 'A' ahora tiene DOS propiedades llamadas 'prototipo' y__proto__
. Esta es una gran confusión para las personas.prototype
y las__proto__
propiedades no están relacionadas de ninguna manera, son cosas separadas que apuntan a valores separados.Te preguntarás: ¿por qué JavaScript tiene
__proto__
propiedades creadas en cada objeto? Bueno, una palabra: delegación . Cuando llama a una propiedad de un objeto y el objeto no la tiene, entonces JavaScript busca el objeto al que hace referencia__proto__
para ver si tal vez lo tiene. Si no lo tiene, entonces mira la__proto__
propiedad de ese objeto y así sucesivamente ... hasta que la cadena termina. De ahí el nombre de prototipo de cadena . Por supuesto, si__proto__
no apunta a un objeto y, en cambio, apunta anull
, buena suerte, JavaScript se da cuenta de eso y lo devolveráundefined
por la propiedad.También puede preguntarse, ¿por qué JavaScript crea una propiedad llamada
prototype
para una función cuando define la función? Porque trata de engañarte, sí, te engaña que funciona como lenguajes basados en clases.Continuemos con nuestro ejemplo y creemos un "objeto" a partir de
A
:Hay algo sucediendo en el fondo cuando sucedió esto.
a1
es una variable ordinaria a la que se le asignó un nuevo objeto vacío.El hecho de que haya utilizado el operador
new
antes de una invocación de funciónA()
hizo algo ADICIONAL en segundo plano. Lanew
palabra clave creó un nuevo objeto que ahora hace referenciaa1
y ese objeto está vacío. Esto es lo que sucede además:Dijimos que en cada definición de función hay una nueva propiedad creada llamada
prototype
(a la que puede acceder, a diferencia de la__proto__
propiedad) creada. Bueno, esa propiedad se está utilizando ahora.Así que ahora estamos en el punto donde tenemos un
a1
objeto vacío recién horneado . Dijimos que todos los objetos en JavaScript tienen una__proto__
propiedad interna que apunta a algo (a1
también lo tiene), ya sea nulo u otro objeto. Lo que hace elnew
operador es que establece esa__proto__
propiedad para que apunte a laprototype
propiedad de la función . Lee eso de nuevo. Básicamente es esto:Dijimos que
A.prototype
no es más que un objeto vacío (a menos que lo cambiemos a otra cosa antes de definirloa1
). Así que ahora básicamentea1.__proto__
apunta a lo mismo queA.prototype
señala, que es ese objeto vacío. Ambos apuntan al mismo objeto que se creó cuando sucedió esta línea:Ahora, sucede otra cosa cuando
var a1 = new A()
se procesa la declaración. BásicamenteA()
se ejecuta y si A es algo como esto:Todo lo que hay dentro
function() { }
se ejecutará. Cuando llegas a lathis.hey..
línea,this
cambia aa1
y obtienes esto:No cubriré por qué los
this
cambios,a1
pero esta es una gran respuesta para aprender más.Para resumir, cuando lo hace,
var a1 = new A()
hay 3 cosas que suceden en segundo plano:a1
.a1 = {}
a1.__proto__
la propiedad se asigna para apuntar a lo mismo queA.prototype
apunta a (otro objeto vacío {})La función
A()
se está ejecutando conthis
set en el nuevo objeto vacío creado en el paso 1 (lea la respuesta a la que hice referencia anteriormente sobre por quéthis
cambiaa1
)Ahora, intentemos crear otro objeto:
Los pasos 1,2,3 se repetirán. ¿Notas algo? La palabra clave es repetir. Paso 1:
a2
será un nuevo objeto vacío, paso 2: su__proto__
propiedad apuntará a lo mismoA.prototype
y, lo más importante, paso 3: la funciónA()
se ejecutará OTRA VEZ, lo que significa quea2
obtendrá lahey
propiedad que contiene una función.a1
ya2
tiene dos propiedades SEPARATE nombradashey
que apuntan a 2 funciones SEPARATE! Ahora tenemos funciones duplicadas en los mismos dos objetos diferentes que hacen lo mismo, oops ... Puede imaginar las implicaciones de memoria de esto si tenemos 1000 objetos creadosnew A
, después de que todas las declaraciones de funciones toman más memoria que algo como el número 2. Entonces ¿Cómo evitamos esto?¿Recuerdas por qué
__proto__
existe la propiedad en cada objeto? De modo que si recupera layoMan
propiedad ena1
(que no existe),__proto__
se consultará su propiedad, que si es un objeto (y en la mayoría de los casos lo es), verificará si contieneyoMan
, y si no lo hace, consultará el objeto,__proto__
etc. Si lo hace, tomará el valor de esa propiedad y se lo mostrará.Entonces, alguien decidió usar este hecho + el hecho de que cuando crea
a1
, su__proto__
propiedad apunta al mismo objeto (vacío)A.prototype
apunta y hace esto:¡Frio! Ahora, cuando crea
a1
, nuevamente pasa por los 3 pasos anteriores, y en el paso 3, no hace nada, ya quefunction A()
no tiene nada que ejecutar. Y si lo hacemos:Verá que
a1
no contienehey
y comprobará su__proto__
objeto de propiedad para ver si lo tiene, que es el caso.Con este enfoque eliminamos la parte del paso 3 donde las funciones se duplican en la creación de cada nuevo objeto. En lugar de
a1
ya2
tener una separadahey
propiedad, ahora ninguno de ellos tiene. Lo cual, supongo, ya te habrás dado cuenta. Eso es lo bueno ... si entiendes__proto__
yFunction.prototype
, preguntas como estas serán bastante obvias.NOTA: Algunas personas tienden a no llamar a la propiedad Prototype interna, ya
__proto__
que he usado este nombre en la publicación para distinguirlo claramente de laFunctional.prototype
propiedad como dos cosas diferentes.fuente
__proto__
y.prototype
son cosas totalmente diferentes.En la mayoría de los casos, son esencialmente lo mismo, pero la segunda versión ahorra memoria porque solo hay una instancia de la función en lugar de una función separada para cada objeto.
Una razón para usar el primer formulario es acceder a "miembros privados". Por ejemplo:
Debido a las reglas de alcance de javascript, private_var está disponible para la función asignada a this.x, pero no fuera del objeto.
fuente
El primer ejemplo cambia la interfaz solo para ese objeto. El segundo ejemplo cambia la interfaz para todos los objetos de esa clase.
fuente
x
esté disponible para todos los objetos cuyo prototipo tenga asignada una nueva instancia de A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
El último problema con el uso en
this
lugar deprototype
es que al anular un método, el constructor de la clase base todavía se referirá al método anulado. Considera esto:versus:
Si cree que esto no es un problema, entonces depende de si puede vivir sin variables privadas y de si tiene la experiencia suficiente para saber una fuga cuando la vea. Además, tener que poner la lógica del constructor después de las definiciones del método es inconveniente.
versus:
fuente
Cada objeto está vinculado a un objeto prototipo. Al intentar acceder a una propiedad que no existe, JavaScript buscará en el objeto prototipo del objeto esa propiedad y la devolverá si existe.
La
prototype
propiedad de un constructor de funciones se refiere al objeto prototipo de todas las instancias creadas con esa función cuando se usanew
.En su primer ejemplo, está agregando una propiedad
x
a cada instancia creada con laA
función.En el segundo ejemplo, está agregando una propiedad al objeto prototipo al que
A
apuntan todas las instancias creadas .En conclusión, en el primer ejemplo se asigna una copia de la función a cada instancia . En el segundo ejemplo, todas las instancias comparten una única copia de la función .
fuente
¿Cual es la diferencia? => Mucho.
Creo que la
this
versión se utiliza para habilitar la encapsulación, es decir, la ocultación de datos. Ayuda a manipular variables privadas.Veamos el siguiente ejemplo:
Ahora, la
prototype
estructura se puede aplicar de la siguiente manera:Diferentes adultos tienen diferentes edades, pero todos los adultos tienen los mismos derechos.
Entonces, lo agregamos usando un prototipo, en lugar de esto.
Veamos la implementación ahora.
Espero que esto ayude.
fuente
El prototipo es la plantilla de la clase; que se aplica a todas las instancias futuras de la misma. Mientras que esta es la instancia particular del objeto.
fuente
Sé que esto ha sido respondido a muerte, pero me gustaría mostrar un ejemplo real de las diferencias de velocidad.
Aquí estamos creando 2,000,000 de objetos nuevos con un
print
método en Chrome. Estamos almacenando cada objeto en una matriz. Ponerseprint
el prototipo lleva aproximadamente la mitad del tiempo.fuente
Permítame darle una respuesta más completa que aprendí durante un curso de capacitación de JavaScript.
La mayoría de las respuestas ya mencionaron la diferencia, es decir, cuando la creación de prototipos de la función se comparte con todas las instancias (futuras). Mientras que declarar la función en la clase creará una copia para cada instancia.
En general, no hay correcto o incorrecto, es más una cuestión de gusto o una decisión de diseño según sus requisitos. Sin embargo, el prototipo es la técnica que se utiliza para desarrollar de manera orientada a objetos, como espero que veas al final de esta respuesta.
Mostraste dos patrones en tu pregunta. Trataré de explicar dos más y trataré de explicar las diferencias si es relevante. Siéntase libre de editar / ampliar. En todos los ejemplos, se trata de un objeto de automóvil que tiene una ubicación y puede moverse.
Patrón de decorador de objetos
No estoy seguro si este patrón sigue siendo relevante hoy en día, pero existe. Y es bueno saberlo. Simplemente pasa un objeto y una propiedad a la función decoradora. El decorador devuelve el objeto con propiedad y método.
Clases Funcionales
Una función en JavaScript es un objeto especializado. Además de ser invocada, una función puede almacenar propiedades como cualquier otro objeto.
En este caso
Car
es una función ( también objeto de pensamiento ) que se puede invocar como solía hacerlo. Tiene una propiedadmethods
(que es un objeto con unamove
función). CuandoCar
se invocaextend
, se llama a la función, que hace algo de magia, y extiende laCar
función (objeto de pensamiento) con los métodos definidos dentromethods
.Este ejemplo, aunque diferente, se acerca más al primer ejemplo de la pregunta.
Clases de prototipos
Los primeros dos patrones permiten una discusión sobre el uso de técnicas para definir métodos compartidos o el uso de métodos que se definen en línea en el cuerpo del constructor. En ambos casos, cada instancia tiene su propia
move
función.El patrón prototípico no se presta bien para el mismo examen, porque el intercambio de funciones a través de una delegación prototipo es el objetivo mismo del patrón prototípico. Como otros señalaron, se espera que tenga una mejor huella de memoria.
Sin embargo, hay un punto interesante que debe saber: cada
prototype
objeto tiene una propiedad de convenienciaconstructor
, que apunta de nuevo a la función (objeto de pensamiento) al que vino adjunto.Con respecto a las últimas tres líneas:
En este ejemplo, se
Car
vincula alprototype
objeto, que se vinculaconstructor
aCar
sí mismo,Car.prototype.constructor
es decir, es élCar
mismo. Esto le permite descubrir qué función constructora construyó un determinado objeto.amy.constructor
La búsqueda falla y, por lo tanto, se delega aCar.prototype
, que tiene la propiedad de constructor. Y asiamy.constructor
esCar
.Además,
amy
es uninstanceof
Car
. Elinstanceof
operador funciona al ver si el objeto prototipo (Car
) del operando derecho se puede encontrar en cualquier lugar de laamy
cadena prototipo ( ) del operando izquierdo .Algunos desarrolladores pueden confundirse al principio. Vea el siguiente ejemplo:
El
instanceof
operador regresafalse
, porqueDog
el prototipo no se puede encontrar en ninguna parte defido
la cadena de prototipos.fido
es un objeto simple que se crea con un objeto literal, es decir, simplemente delega aObject.prototype
.Patrones pseudoclásicos
Esta es realmente solo otra forma del patrón prototípico en forma simplificada y más familiar para aquellos que programan en Java, por ejemplo, ya que usa el
new
constructor.Realmente hace lo mismo que en el patrón prototípico, es simplemente una capa de azúcar sintáctica del patrón prototípico.
Sin embargo, la principal diferencia es que hay optimizaciones implementadas en los motores de JavaScript que solo se aplican cuando se usa el patrón pseudoclásico. Piense en el patrón pseudoclásico, una versión probablemente más rápida del patrón prototípico; Las relaciones de objeto en ambos ejemplos son las mismas.
Finalmente, no debería ser demasiado difícil darse cuenta de cómo se puede hacer la programación orientada a objetos. Hay dos secciones
Una sección que define propiedades / métodos comunes en el prototipo (cadena).
Y otra sección donde pones las definiciones que distinguen los objetos entre sí (
loc
variable en los ejemplos).Esto es lo que nos permite aplicar conceptos como superclase o subclase en JavaScript.
Siéntase libre de agregar o editar. Una vez más completo, podría hacer que este sea un wiki comunitario.
fuente
Creo que @Matthew Crumley tiene razón. Son funcionalmente , si no estructuralmente, equivalentes. Si usa Firebug para mirar los objetos que se crean
new
, puede ver que son los mismos. Sin embargo, mi preferencia sería la siguiente. Supongo que se parece más a lo que estoy acostumbrado en C # / Java. Es decir, definir la clase, definir los campos, el constructor y los métodos.EDITAR No quería decir que el alcance de la variable fuera privado, solo estaba tratando de ilustrar cómo defino mis clases en javascript. El nombre de la variable se ha cambiado para reflejar esto.
fuente
initialize
yx methods do not refer to the
_instance_var` en unaA
instancia, pero a una global. Úselothis._instance_var
si desea utilizar la_instance_var
propiedad de unaA
instancia.Como se discutió en otras respuestas, es realmente una consideración de rendimiento porque la función en el prototipo se comparte con todas las instancias, en lugar de la función que se crea para cada instanciación.
Puse un jsperf para mostrar esto. Hay una diferencia dramática en el tiempo que lleva crear una instancia de la clase, aunque en realidad solo es relevante si está haciendo muchas instancias.
http://jsperf.com/functions-in-constructor-vs-prototype
fuente
Piense en el lenguaje de tipo estático, las cosas en
prototype
estáticas y las cosas enthis
están relacionadas con la instancia.fuente