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.xRespuestas:
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
Ase 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.xes 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
Ase 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
newoperador 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, laxpropiedad 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
Aasigna 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.xes efectivowindow.x. Como la función no devuelve nada,Atendrá 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 laprototypepropiedad, 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?Acomo 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
thisobjeto, 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 unthisobjeto, 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.prototypees 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.prototypey 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áundefinedpor la propiedad.También puede preguntarse, ¿por qué JavaScript crea una propiedad llamada
prototypepara 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.
a1es una variable ordinaria a la que se le asignó un nuevo objeto vacío.El hecho de que haya utilizado el operador
newantes de una invocación de funciónA()hizo algo ADICIONAL en segundo plano. Lanewpalabra clave creó un nuevo objeto que ahora hace referenciaa1y 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
a1objeto vacío recién horneado . Dijimos que todos los objetos en JavaScript tienen una__proto__propiedad interna que apunta a algo (a1también lo tiene), ya sea nulo u otro objeto. Lo que hace elnewoperador es que establece esa__proto__propiedad para que apunte a laprototypepropiedad de la función . Lee eso de nuevo. Básicamente es esto:Dijimos que
A.prototypeno 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.prototypeseñ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,thiscambia aa1y obtienes esto:No cubriré por qué los
thiscambios,a1pero 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.prototypeapunta a (otro objeto vacío {})La función
A()se está ejecutando conthisset en el nuevo objeto vacío creado en el paso 1 (lea la respuesta a la que hice referencia anteriormente sobre por quéthiscambiaa1)Ahora, intentemos crear otro objeto:
Los pasos 1,2,3 se repetirán. ¿Notas algo? La palabra clave es repetir. Paso 1:
a2será un nuevo objeto vacío, paso 2: su__proto__propiedad apuntará a lo mismoA.prototypey, lo más importante, paso 3: la funciónA()se ejecutará OTRA VEZ, lo que significa quea2obtendrá laheypropiedad que contiene una función.a1ya2tiene dos propiedades SEPARATE nombradasheyque 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 layoManpropiedad 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.prototypeapunta 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
a1no contieneheyy 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
a1ya2tener una separadaheypropiedad, 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.prototypepropiedad como dos cosas diferentes.fuente
__proto__y.prototypeson 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
xesté 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
thislugar deprototypees 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
prototypepropiedad 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
xa cada instancia creada con laAfunción.En el segundo ejemplo, está agregando una propiedad al objeto prototipo al que
Aapuntan 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
thisversió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
prototypeestructura 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
printmétodo en Chrome. Estamos almacenando cada objeto en una matriz. Ponerseprintel 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
Cares una función ( también objeto de pensamiento ) que se puede invocar como solía hacerlo. Tiene una propiedadmethods(que es un objeto con unamovefunción). CuandoCarse invocaextend, se llama a la función, que hace algo de magia, y extiende laCarfunció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
movefunció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
prototypeobjeto 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
Carvincula alprototypeobjeto, que se vinculaconstructoraCarsí mismo,Car.prototype.constructores decir, es élCarmismo. Esto le permite descubrir qué función constructora construyó un determinado objeto.amy.constructorLa búsqueda falla y, por lo tanto, se delega aCar.prototype, que tiene la propiedad de constructor. Y asiamy.constructoresCar.Además,
amyes uninstanceofCar. Elinstanceofoperador funciona al ver si el objeto prototipo (Car) del operando derecho se puede encontrar en cualquier lugar de laamycadena prototipo ( ) del operando izquierdo .Algunos desarrolladores pueden confundirse al principio. Vea el siguiente ejemplo:
El
instanceofoperador regresafalse, porqueDogel prototipo no se puede encontrar en ninguna parte defidola cadena de prototipos.fidoes 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
newconstructor.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í (
locvariable 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
initializeyx methods do not refer to the_instance_var` en unaAinstancia, pero a una global. Úselothis._instance_varsi desea utilizar la_instance_varpropiedad de unaAinstancia.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
prototypeestáticas y las cosas enthisestán relacionadas con la instancia.fuente