No estoy tan interesado en los lenguajes de programación dinámicos, pero he escrito mi parte justa de código JavaScript. Nunca entendí realmente esta programación basada en prototipos, ¿alguien sabe cómo funciona?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
Recuerdo muchas conversaciones que tuve con la gente hace un tiempo (no estoy exactamente seguro de lo que estoy haciendo) pero, según tengo entendido, no hay un concepto de clase. Es solo un objeto, y las instancias de esos objetos son clones del original, ¿verdad?
Pero, ¿cuál es el propósito exacto de esta propiedad ".prototype" en JavaScript? ¿Cómo se relaciona con la creación de instancias de objetos?
Actualización: forma correcta
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
Además, estas diapositivas realmente ayudaron mucho.
javascript
dynamic-languages
prototype-oriented
John Leidegren
fuente
fuente
Respuestas:
Cada objeto de JavaScript tiene una "ranura" interna llamada
[[Prototype]]
cuyo valor es unonull
o unobject
. Puede pensar en una ranura como una propiedad de un objeto, interna al motor de JavaScript, oculta del código que escribe. Los corchetes alrededor[[Prototype]]
son deliberados y son una convención de especificación ECMAScript para denotar ranuras internas.El valor señalado por el
[[Prototype]]
de un objeto, se conoce coloquialmente como "el prototipo de ese objeto".Si accede a una propiedad a través de la notación dot (
obj.propName
) o corchete (obj['propName']
), y el objeto no tiene directamente dicha propiedad (es decir, una propiedad propia , comprobable medianteobj.hasOwnProperty('propName')
), el tiempo de ejecución busca una propiedad con ese nombre en el objeto al que se hace referencia por el[[Prototype]]
contrario. Si el[[Prototype]]
también no tiene una propiedad tal, su[[Prototype]]
se comprueba a su vez, y así sucesivamente. De esta manera, la cadena prototipo del objeto original se camina hasta que se encuentra una coincidencia o se alcanza su final. En la parte superior de la cadena de prototipos está elnull
valor.Las implementaciones modernas de JavaScript permiten el acceso de lectura y / o escritura a
[[Prototype]]
las siguientes formas:new
operador (configura la cadena de prototipo en el objeto predeterminado devuelto por una función de constructor),extends
palabra clave (configura la cadena de prototipo cuando se usa la sintaxis de clase),Object.create
establecerá el argumento proporcionado como el[[Prototype]]
del objeto resultante,Object.getPrototypeOf
yObject.setPrototypeOf
(obtener / establecer la creación[[Prototype]]
posterior del objeto), y__proto__
(similar a 4.)Object.getPrototypeOf
yObject.setPrototypeOf
son preferibles__proto__
, en parte porque el comportamiento deo.__proto__
es inusual cuando un objeto tiene un prototipo denull
.Un objeto
[[Prototype]]
se establece inicialmente durante la creación del objeto.Si crea un nuevo objeto mediante
new Func()
, el objeto[[Prototype]]
, por defecto, se establecerá en el objeto al que hace referenciaFunc.prototype
.Tenga en cuenta que, por lo tanto, todas las clases y todas las funciones que se pueden usar con el
new
operador tienen una propiedad nombrada.prototype
además de su propia[[Prototype]]
ranura interna. Este uso dual de la palabra "prototipo" es la fuente de confusión interminable entre los recién llegados al idioma.El uso
new
con funciones de constructor nos permite simular la herencia clásica en JavaScript; aunque el sistema de herencia de JavaScript es, como hemos visto, prototípico y no basado en clases.Antes de la introducción de la sintaxis de clase a JavaScript, las funciones de constructor eran la única forma de simular clases. Podemos pensar en las propiedades del objeto referenciado por la
.prototype
propiedad de la función constructora como miembros compartidos; es decir. miembros que son iguales para cada instancia. En los sistemas basados en clases, los métodos se implementan de la misma manera para cada instancia, por lo que los métodos se agregan conceptualmente a la.prototype
propiedad; Sin embargo, los campos de un objeto son específicos de la instancia y, por lo tanto, se agregan al objeto mismo durante la construcción.Sin la sintaxis de clase, los desarrolladores tuvieron que configurar manualmente la cadena de prototipos para lograr una funcionalidad similar a la herencia clásica. Esto condujo a una preponderancia de diferentes formas de lograr esto.
Aquí hay una manera:
... y aquí hay otra forma:
La sintaxis de clase introducida en ES2015 simplifica las cosas, al proporcionar
extends
como la "única forma verdadera" de configurar la cadena de prototipos para simular la herencia clásica en JavaScript.Entonces, similar al código anterior, si usa la sintaxis de clase para crear un nuevo objeto de esta manera:
... el objeto resultante
[[Prototype]]
se establecerá en una instancia deParent
, que[[Prototype]]
, a su vez, esParent.prototype
.Finalmente, si crea un nuevo objeto vía
Object.create(foo)
, el objeto resultante[[Prototype]]
se establecerá enfoo
.fuente
En un lenguaje que implementa herencia clásica como Java, C # o C ++, comienza creando una clase, un plano para sus objetos, y luego puede crear nuevos objetos a partir de esa clase o puede extender la clase, definiendo una nueva clase que aumente La clase original.
En JavaScript, primero crea un objeto (no hay un concepto de clase), luego puede aumentar su propio objeto o crear nuevos objetos a partir de él. No es difícil, pero un poco extraño y difícil de metabolizar para alguien acostumbrado a la forma clásica.
Ejemplo:
Hasta ahora he estado extendiendo el objeto base, ahora creo otro objeto y luego heredo de Person.
Mostrar fragmento de código
Mientras que como dije no puedo llamar a setAmountDue (), getAmountDue () en una Persona.
fuente
Customer.prototype = new Person();
línea, MDN muestra un ejemplo usandoCustomer.prototype = Object.create(Person.prototype)
, y afirma que 'Un error común aquí es usar "new Person ()"' . fuenteEste es un modelo de objeto basado en un prototipo muy simple que se consideraría como una muestra durante la explicación, sin ningún comentario todavía:
Hay algunos puntos cruciales que tenemos que considerar antes de pasar por el concepto del prototipo.
1- Cómo funcionan realmente las funciones de JavaScript:
Para dar el primer paso, tenemos que descubrir cómo funcionan realmente las funciones de JavaScript, como una clase como función usando
this
palabra clave o simplemente como una función regular con sus argumentos, qué hace y qué devuelve.Digamos que queremos crear un
Person
modelo de objeto. pero en este paso voy a estar tratando de hacer exactamente lo mismo sin utilizarprototype
ynew
palabra clave .Entonces, en este paso
functions
,objects
ythis
palabra clave, es todo lo que tenemos.La primera pregunta sería cómo la
this
palabra clave podría ser útil sin usar lanew
palabra clave .Entonces, para responder eso, digamos que tenemos un objeto vacío y dos funciones como:
y ahora sin usar
new
palabras clave, cómo podríamos usar estas funciones. Entonces JavaScript tiene 3 formas diferentes de hacer eso:a. La primera forma es llamar a la función como una función regular:
en este caso, este sería el objeto de contexto actual, que generalmente es el
window
objeto global en el navegador oGLOBAL
enNode.js
. Significa que tendríamos window.name en el navegador o GLOBAL.name en Node.js, con "George" como su valor.si. Podemos adjuntarlos a un objeto, como sus propiedades
- La forma más fácil de hacer esto es modificar el
person
objeto vacío , como:de esta manera podemos llamarlos como:
y ahora el
person
objeto es como:- La otra forma de adjuntar una propiedad a un objeto es usar
prototype
ese objeto que se puede encontrar en cualquier objeto JavaScript con el nombre de__proto__
, y he tratado de explicarlo un poco en la parte de resumen. Entonces podríamos obtener el resultado similar haciendo:Pero de esta manera lo que estamos haciendo es modificar el
Object.prototype
, porque cada vez que creamos un objeto JavaScript usando literales ({ ... }
), se crea en función deObject.prototype
, lo que significa que se adjunta al objeto recién creado como un atributo llamado__proto__
, así que si lo cambiamos , como hemos hecho en nuestro fragmento de código anterior, todos los objetos de JavaScript se cambiarían, no es una buena práctica. Entonces, ¿cuál podría ser la mejor práctica ahora:y ahora otros objetos están en paz, pero todavía no parece ser una buena práctica. Así que todavía tenemos una solución más, pero para usar esta solución, debemos volver a esa línea de código donde
person
se creó el objeto (var person = {};
) y luego cambiarlo como:lo que hace es crear un nuevo JavaScript
Object
y adjuntarlopropertiesObject
al__proto__
atributo. Entonces, para asegurarse de que puede hacer:Pero el punto difícil aquí es que tiene acceso a todas las propiedades definidas en
__proto__
el primer nivel delperson
objeto (lea la parte de resumen para obtener más detalles).Como puede ver, el uso de cualquiera de estas dos vías
this
apuntaría exactamente alperson
objeto.C. JavaScript tiene otra forma de proporcionar la función
this
, que es llamar o aplicar para invocar la función.y
de esta manera, mi favorita, podemos llamar fácilmente a nuestras funciones como:
o
Estos 3 métodos son los pasos iniciales importantes para descubrir la funcionalidad .prototype.
2- ¿Cómo funciona la
new
palabra clave?Este es el segundo paso para comprender la
.prototype
funcionalidad. Esto es lo que uso para simular el proceso:en esta parte voy a tratar de seguir todos los pasos que toma JavaScript, sin usar la
new
palabra clave yprototype
, cuando use lanew
palabra clave. así que cuando lo hacemosnew Person("George")
, laPerson
función sirve como un constructor. Esto es lo que hace JavaScript, uno por uno:a. En primer lugar, crea un objeto vacío, básicamente un hash vacío como:
si. el siguiente paso que toma JavaScript es adjuntar todos los objetos prototipo al objeto recién creado
Tenemos
my_person_prototype
aquí similar al objeto prototipo.No es la forma en que JavaScript realmente une las propiedades que se definen en el prototipo. La forma real está relacionada con el concepto de cadena prototipo.
a. y b. En lugar de estos dos pasos, puede obtener exactamente el mismo resultado haciendo:
ahora podemos llamar a la
getName
función en nuestromy_person_prototype
:C. entonces le da ese objeto al constructor,
podemos hacer esto con nuestra muestra como:
o
entonces el constructor puede hacer lo que quiera, porque este dentro de ese constructor es el objeto que se acaba de crear.
ahora el resultado final antes de simular los otros pasos: Object {name: "George"}
Resumen:
Básicamente, cuando usa la nueva palabra clave en una función, está invocando eso y esa función sirve como un constructor, por lo que cuando dice:
JavaScript crea internamente un objeto, un hash vacío y luego le da ese objeto al constructor, luego el constructor puede hacer lo que quiera, porque esto dentro de ese constructor es el objeto que acaba de crear y luego le da ese objeto, por supuesto. si no ha usado la declaración return en su función o si ha puesto un
return undefined;
al final del cuerpo de su función.Entonces, cuando JavaScript busca una propiedad en un objeto, lo primero que hace es buscarlo en ese objeto. Y luego hay una propiedad secreta
[[prototype]]
que usualmente tenemos__proto__
y esa propiedad es lo que JavaScript ve a continuación. Y cuando mira a través de__proto__
, en la medida en que es de nuevo otro objeto JavaScript, tiene su propio__proto__
atributo, sube y sube hasta llegar al punto donde el siguiente__proto__
es nulo. El punto es que el único objeto en JavaScript que su__proto__
atributo es nulo es elObject.prototype
objeto:y así es como funciona la herencia en JavaScript.
En otras palabras, cuando tiene una propiedad prototipo en una función y llama a una nueva sobre eso, después de que JavaScript termina de buscar propiedades en ese objeto recién creado, verá las funciones de la función
.prototype
y también es posible que este objeto tenga su Propio prototipo interno. y así.fuente
__proto__
, primero eliminará todas las propiedades de prototipo de nivel superior y luego tendrá una nueva base de prototipos que ya no se comparte a menos que la comparta.Los siete Koans del prototipo
Mientras Ciro San descendía del Monte Fire Fox después de una profunda meditación, su mente estaba clara y pacífica.
Sin embargo, su mano estaba inquieta, y por sí misma agarró un pincel y anotó las siguientes notas.
0) Dos cosas diferentes se pueden llamar "prototipo":
la propiedad prototipo, como en
obj.prototype
la propiedad interna del prototipo, denotada como
[[Prototype]]
en ES5 .Se puede recuperar a través del ES5
Object.getPrototypeOf()
.Firefox lo hace accesible a través de la
__proto__
propiedad como una extensión. ES6 ahora menciona algunos requisitos opcionales para__proto__
.1) Esos conceptos existen para responder la pregunta:
Intuitivamente, la herencia clásica debería afectar la búsqueda de propiedades.
2)
__proto__
se usa para la.
búsqueda de propiedades de puntos como enobj.property
..prototype
se no se utiliza para la búsqueda directamente, sólo de manera indirecta, ya que determina__proto__
en la creación de objetos connew
.El orden de búsqueda es:
obj
propiedades agregadas conobj.p = ...
oObject.defineProperty(obj, ...)
obj.__proto__
obj.__proto__.__proto__
, etc.__proto__
esnull
, regreseundefined
.Esta es la llamada cadena prototipo .
Puede evitar la
.
búsqueda conobj.hasOwnProperty('key')
yObject.getOwnPropertyNames(f)
3) Hay dos formas principales de configurar
obj.__proto__
:new
:luego
new
ha establecido:Aquí es donde
.prototype
se acostumbra.Object.create
:establece:
4) El código:
Corresponde al siguiente diagrama (
Number
se omiten algunas cosas):Este diagrama muestra muchos nodos de objeto predefinidos de lenguaje:
null
Object
Object.prototype
Function
Function.prototype
1
Number.prototype
(se puede encontrar con(1).__proto__
paréntesis obligatorio para satisfacer la sintaxis)Nuestras 2 líneas de código solo crearon los siguientes objetos nuevos:
f
F
F.prototype
i
ahora es una propiedad def
porque cuando lo haces:se evalúa
F
conthis
ser el valor quenew
va a volver, que luego se asigna af
.5)
.constructor
normalmente proviene deF.prototype
la.
búsqueda:Cuando escribimos
f.constructor
, JavaScript realiza la.
búsqueda como:f
no tiene.constructor
f.__proto__ === F.prototype
tiene.constructor === F
, así que tómaloEl resultado
f.constructor == F
es intuitivamente correcto, ya queF
se utiliza para construirf
, por ejemplo, establecer campos, como en los lenguajes clásicos de OOP.6) La sintaxis de herencia clásica se puede lograr manipulando cadenas de prototipos.
ES6 agrega las palabras clave
class
yextends
, que son principalmente azúcar de sintaxis para la locura de manipulación de prototipos previamente posible.Diagrama simplificado sin todos los objetos predefinidos:
Tomemos un momento para estudiar cómo funciona lo siguiente:
Los primeros conjuntos de línea
c.i
a1
como se explica en "4)".En la segunda línea, cuando hacemos:
.inc
se encuentra a través de la[[Prototype]]
cadena:c
->C
->C.prototype
->inc
X.Y()
, ¡JavaScript se establece automáticamentethis
en igualX
dentro de laY()
llamada a la función!La misma lógica exacta también explica
d.inc
yd.inc2
.Este artículo https://javascript.info/class#not-just-a-syntax-sugar menciona otros efectos que
class
vale la pena conocer. Algunos de ellos pueden no ser alcanzables sin laclass
palabra clave (TODO verifique cuál):[[FunctionKind]]:"classConstructor"
, que obliga al constructor a llamarse con new: ¿Cuál es la razón por la cual los constructores de clase ES6 no pueden llamarse como funciones normales?Object.defineProperty
.use strict
. Se puede hacer con un explícitouse strict
para cada función, lo cual es ciertamente tedioso.fuente
.
búsqueda (y cuántas copias de los datos se hacen) . Así que me propuse entender ese punto. El resto es Google + publicaciones de blog + un intérprete Js a la mano. :)sets f.constructor = F
parte era descaradamente incorrecta y contradecía con otras secciones:.constructor
se encuentra a través de la.
búsqueda en la cadena de prototipos. Lo arregló ahora.f
el prototipo se estableceF
solo en el momento de la construcción;f
no lo sabrá ni le importaráF.prototype
en ningún momento después de su primera construcción.prototype
te permite hacer clases. si no lo usaprototype
, se convierte en estático.Aquí hay un breve ejemplo.
En el caso anterior, tiene una prueba de llamada de función estática. A esta función solo se puede acceder mediante obj.test, donde puede imaginar que obj sea una clase.
donde como en el siguiente código
El obj se ha convertido en una clase que ahora se puede instanciar. Pueden existir múltiples instancias de obj y todas tienen la
test
función.Lo anterior es mi entendimiento. Lo estoy haciendo un wiki comunitario, para que la gente pueda corregirme si me equivoco.
fuente
prototype
es una propiedad de las funciones de constructor, no de instancias, es decir, ¡su código está equivocado! Tal vez usted significó la propiedad de no-estándar__proto__
de los objetos, pero eso es una bestia completamente diferente ...Después de leer este hilo, me siento confundido con JavaScript Prototype Chain, luego encontré estos gráficos
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
es un gráfico claro para mostrar la herencia de JavaScript por cadena de prototipo
y
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
Este contiene un ejemplo con código y varios diagramas agradables.
Espero que también sea útil para que entiendas la cadena de prototipos de JavaScript.
fuente
[[Prototype]]
significa?Cada objeto tiene una propiedad interna, [[Prototipo]] , que lo vincula a otro objeto:
En javascript tradicional, el objeto vinculado es
prototype
propiedad de una función:Algunos entornos exponen [[Prototipo]] como
__proto__
:Usted crea el enlace [[Prototipo]] cuando crea un objeto.
Entonces estas declaraciones son equivalentes:
En realidad no puede ver el destino del enlace (
Object.prototype
) en una nueva declaración; en cambio, el objetivo está implícito por el constructor (Object
).Recuerda:
prototype
propiedad, inicialmente sosteniendo un objeto vacío.prototype
propiedad de su constructor.prototype
propiedad no se usará.new
.fuente
Object.create()
documentos , @sam. Enlaces__proto__
yObject.prototype
serían buenas mejoras. Y me gustaron tus ejemplos de cómo funcionan los prototipos con los constructores yObject.create()
, pero probablemente fueron la parte larga y menos relevante de la que querías deshacerte.Javascript no tiene herencia en el sentido habitual, pero tiene la cadena de prototipo.
cadena prototipo
Si no se puede encontrar un miembro de un objeto en el objeto, lo busca en la cadena de prototipos. La cadena consta de otros objetos. Se puede acceder al prototipo de una instancia dada con la
__proto__
variable. Cada objeto tiene uno, ya que no hay diferencia entre clases e instancias en javascript.La ventaja de agregar una función / variable al prototipo es que tiene que estar en la memoria solo una vez, no para cada instancia.
También es útil para la herencia, porque la cadena del prototipo puede consistir en muchos otros objetos.
fuente
Este artículo es largo. Pero estoy seguro de que eliminará la mayoría de sus consultas sobre la naturaleza "prototípica" de la herencia de JavaScript. Y aun más. Por favor lea el artículo completo.
JavaScript básicamente tiene dos tipos de tipos de datos
No objetos
Los siguientes son el no objeto tipos de datos
Estos tipos de datos devuelven los siguientes cuando utiliza el operador typeof
typeof "string literal" (o una variable que contiene string literal) === 'string'
typeof 5 (o cualquier literal numérico o una variable que contenga literal numérico o NaN o Infynity ) === 'número'
tipo de verdadero (o falso o una variable que contiene verdadero o falso ) === 'boolean'
tipo de indefinido (o una variable indefinida o una variable que contiene indefinido ) === 'undefined'
Los tipos de datos de cadena , número y booleanos se pueden representar como objetos y no objetos . Cuando se representan como objetos, su tipo de siempre es === 'objeto'. Volveremos a esto una vez que comprendamos los tipos de datos del objeto.
Objetos
Los tipos de datos del objeto se pueden dividir en dos tipos.
Los objetos de tipo Function son los que devuelven la cadena 'function' con el operador typeof . Todas las funciones definidas por el usuario y todos los objetos integrados de JavaScript que pueden crear nuevos objetos mediante el uso de un nuevo operador entran en esta categoría. Por ej.
Entonces, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === 'función'
Todos los objetos de tipo Function son en realidad instancias de la función de objeto JavaScript incorporada (incluido el objeto Function, es decir, se define de forma recursiva). Es como si estos objetos se hubieran definido de la siguiente manera
Como se mencionó, los objetos de tipo Función pueden crear nuevos objetos usando el nuevo operador . Por ejemplo, un objeto de tipo Object , String , Number , Boolean , Array , RegExp Or UserDefinedFunction se puede crear utilizando
Los objetos así creados son todos los objetos que no son de tipo de funciones y devuelven su typeof === 'objeto' . En todos estos casos, el objeto "a" no puede crear más objetos usando el operador new. Entonces lo siguiente está mal
El objeto incorporado Math es typeof === 'objeto' . Por lo tanto, un nuevo objeto de tipo Math no puede ser creado por un nuevo operador.
Observe también que las funciones Object , Array y RegExp pueden crear un nuevo objeto sin siquiera usar el operador new . Sin embargo, los siguientes no lo hacen.
Las funciones definidas por el usuario son casos especiales.
Como los objetos de tipo Función pueden crear nuevos objetos, también se denominan Constructores .
Cada Constructor / Función (ya sea integrado o definido por el usuario) cuando se define automáticamente tiene una propiedad llamada "prototipo" cuyo valor por defecto se establece como un objeto. Este objeto tiene una propiedad llamada "constructor" que por defecto hace referencia al Constructor / Función .
Por ejemplo cuando definimos una función
siguiente sucede automáticamente
Esta propiedad "prototipo" solo está presente en los objetos de tipo Función (y nunca en los objetos de tipo No Función ).
Esto se debe a que cuando se crea un nuevo objeto (usando un nuevo operador) hereda todas las propiedades y métodos del objeto prototipo actual de la función Constructor, es decir, se crea una referencia interna en el objeto recién creado que hace referencia al objeto referenciado por el objeto prototipo actual de la función Constructor.
Esta "referencia interna" que se crea en el objeto para hacer referencia a propiedades heredadas se conoce como el prototipo del objeto (que hace referencia al objeto al que hace referencia la propiedad "prototipo" del Constructor pero es diferente de él). Para cualquier objeto (Función o No Función) esto se puede recuperar utilizando el método Object.getPrototypeOf () . Usando este método, se puede rastrear la cadena prototipo de un objeto.
Además, cada objeto que se crea ( tipo de función o tipo sin función ) tiene una propiedad "constructor" que se hereda del objeto al que hace referencia la propiedad prototipo de la función Constructor. Por defecto, esta propiedad "constructor" hace referencia a la función Constructor que la creó (si el "prototipo" predeterminado de la función Constructor no se modifica).
Para todos los objetos de tipo Function, la función constructora siempre es function Function () {}
Para los objetos de tipo Sin función (por ejemplo, Objeto matemático incorporado Javascript), la función constructora es la función que lo creó. Para el objeto Math es la función Object () {} .
Todo el concepto explicado anteriormente puede ser un poco desalentador de entender sin ningún código de soporte. Siga el siguiente código línea por línea para comprender el concepto. Intenta ejecutarlo para tener una mejor comprensión.
La cadena de prototipos de cada objeto finalmente se remonta a Object.prototype (que en sí mismo no tiene ningún objeto prototipo). El siguiente código se puede usar para rastrear la cadena prototipo de un objeto
La cadena de prototipos para varios objetos funciona de la siguiente manera.
Para crear un objeto sin ningún prototipo, use lo siguiente:
Uno podría pensar que establecer la propiedad prototipo del Constructor en nulo creará un objeto con un prototipo nulo. Sin embargo, en tales casos, el prototipo del objeto recién creado se establece en Object.prototype y su constructor se establece en función Object. Esto se demuestra con el siguiente código
Siguiendo en el resumen de este artículo
Solo los objetos de tipo Función pueden crear un nuevo objeto utilizando el operador new . Los objetos así creados son objetos de tipo No Función . Los objetos de tipo Non Function no pueden crear más un objeto usando el operador new .
Todos los objetos de tipo Función tienen por defecto una propiedad "prototipo" . Esta propiedad "prototipo" hace referencia a un objeto que tiene una propiedad "constructora" que por defecto hace referencia al objeto Tipo de función en sí.
Todos los objetos ( tipo de función y tipo sin función ) tienen una propiedad "constructor" que por defecto hace referencia al objeto de tipo de función / constructor que lo creó.
Cada objeto que se crea internamente hace referencia al objeto al que hace referencia la propiedad "prototipo" del Constructor que lo creó. Este objeto se conoce como el prototipo del objeto creado (que es diferente de la propiedad "prototipo" de los objetos de tipo Function a la que hace referencia). De esta forma, el objeto creado puede acceder directamente a los métodos y propiedades definidos en el objeto referenciado por la propiedad "prototipo" del Constructor (en el momento de la creación del objeto).
El prototipo de un objeto (y, por lo tanto, sus nombres de propiedad heredados) se puede recuperar utilizando el método Object.getPrototypeOf () . De hecho, este método se puede utilizar para navegar por toda la cadena de prototipos del objeto.
La cadena de prototipo de cada objeto finalmente se remonta a Object.prototype (a menos que el objeto se cree usando Object.create (nulo) en cuyo caso el objeto no tiene prototipo).
typeof (new Array ()) === 'objeto' es por diseño de lenguaje y no un error como lo señala Douglas Crockford
Establecer la propiedad del prototipo del Constructor en nulo (o indefinido, número, verdadero, falso, cadena) no creará un objeto con un prototipo nulo. En tales casos, el prototipo del objeto recién creado se establece en Object.prototype y su constructor se establece en la función Object.
Espero que esto ayude.
fuente
El concepto de
prototypal
herencia es uno de los más complicados para muchos desarrolladores. Tratemos de entender la raíz del problema para entenderprototypal inheritance
mejor. Comencemos con unaplain
función.Si usamos un
new
operador en elTree function
, lo llamamos como unaconstructor
función.Cada
JavaScript
función tiene unprototype
. Cuando inicias sesiónTree.prototype
, obtienes ...Si observa el
console.log()
resultado anterior , podría ver una propiedad de constructorTree.prototype
y una__proto__
propiedad también. El__proto__
representa el hecho deprototype
que estofunction
se basa, y dado que esto es simplemente un planoJavaScript function
sininheritance
configuración aún, se refiere alObject prototype
que es algo que se ha incorporado a JavaScript ...https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
Esto tiene cosas como
.toString, .toValue, .hasOwnProperty
etc.__proto__
que fue traído mi mozilla está en desuso y se reemplaza por elObject.getPrototypeOf
método para obtener elobject's prototype
.Agreguemos un método a nuestro
Tree
prototype
.Hemos modificado
Root
y agregado unafunction
rama.Eso significa que cuando crea un
instance
deTree
, puede llamar a subranch
método.También podemos agregar
primitives
oobjects
a nuestroPrototype
.Agreguemos un
child-tree
a nuestroTree
.Aquí
Child
heredaprototype
de Tree, lo que estamos haciendo aquí es usar unObject.create()
método para crear un nuevo objeto basado en lo que pasas, aquí estáTree.prototype
. En este caso, lo que estamos haciendo es configurar el prototipo de Child en un nuevo objeto que se ve idéntico alTree
prototipo. A continuación, estamos configurando elChild's constructor to Child
, si no lo hacemos, apuntaríaTree()
.Child
ahora tiene el suyoprototype
, sus__proto__
puntosTree
yTree's prototype
puntos a la baseObject
.Ahora crea un
instance
ofChild
y callbranch
que originalmente está disponible enTree
. En realidad no hemos definido nuestrobranch
en elChild prototype
. PERO, en elRoot prototype
que el niño hereda.En JS no todo es un objeto, todo puede actuar como un objeto.
Javascript
tiene primitivas comostrings, number, booleans, undefined, null.
No sonobject(i.e reference types)
, pero ciertamente pueden actuar como unobject
. Veamos un ejemplo aquí.En la primera línea de este listado,
primitive
se asigna un valor de cadena al nombre. La segunda línea trata el nombre como unobject
y llamacharAt(0)
usando la notación de puntos.Esto es lo que sucede detrás de escena: // lo que hace el
JavaScript
motorEl
String object
existe sólo para una declaración antes de que sea destruido (un proceso llamadoautoboxing
). Volvamos de nuevo a nuestroprototypal
inheritance
.Javascript
admite herencia víadelegation
basada enprototypes
.Function
tiene unaprototype
propiedad, que se refiere a otro objeto.properties/functions
se miran desdeobject
sí mismos o medianteprototype
cadena si no existeA
prototype
en JS es un objeto queyields
usted es el padre de otroobject
. [es decir, delegación]Delegation
significa que si no puede hacer algo, le dirá a otra persona que lo haga por usted.https://jsfiddle.net/say0tzpL/1/
Si busca el violín anterior, el perro tiene acceso al
toString
método, pero no está disponible en él, pero está disponible a través de la cadena prototipo que delega aObject.prototype
Si observa el siguiente, estamos tratando de acceder al
call
método que está disponible en todosfunction
.https://jsfiddle.net/rknffckc/
Si busca el violín anterior,
Profile
Function tiene acceso alcall
método, pero no está disponible en él, pero está disponible a través de la cadena de prototipos que delega aFunction.prototype
Nota:
prototype
es una propiedad del constructor de funciones, mientras que__proto__
es una propiedad de los objetos construidos a partir del constructor de funciones. Cada función viene con unaprototype
propiedad cuyo valor es un vacíoobject
. Cuando creamos una instancia de la función, obtenemos una propiedad interna[[Prototype]]
o__proto__
cuya referencia es el prototipo de la Funciónconstructor
.El diagrama anterior parece un poco complicado, pero muestra la imagen completa de cómo
prototype chaining
funciona. Pasemos por esto lentamente:Hay dos instancias
b1
yb2
, cuyo constructor esBar
y padre es Foo y tiene dos métodos de cadena de prototipoidentify
yspeak
víaBar
yFoo
https://jsfiddle.net/kbp7jr7n/
Si busca el código anterior, tenemos un
Foo
constructor que tiene el métodoidentify()
y elBar
constructor que tiene elspeak
método. Creamos dosBar
instanciasb1
yb2
cuyo tipo principal esFoo
. Ahora, mientras llamamos alspeak
método deBar
, somos capaces de identificar quién llama al hablar porprototype
cadena.Bar
ahora tiene todos los métodos de losFoo
cuales se definen en suprototype
. Vamos a profundizar más en la comprensión de laObject.prototype
yFunction.prototype
, y cómo se relacionan. Si buscas el constructor deFoo
,Bar
yObject
sonFunction constructor
.El
prototype
deBar
esFoo
,prototype
deFoo
esObject
y si miras de cerca elprototype
deFoo
está relacionadoObject.prototype
.Antes de cerrar esto, terminemos con un pequeño código aquí para resumir todo lo anterior . Estamos utilizando el
instanceof
operador aquí para verificar si unobject
tiene en suprototype
cadena laprototype
propiedad de unconstructor
que a continuación resume todo el diagrama grande.Espero que esto agregue algo de información, sé que esto podría ser algo grande de entender ... en palabras simples , ¡son solo objetos vinculados a objetos!
fuente
La interfaz con las clases estándar se vuelve extensible. Por ejemplo, está utilizando la
Array
clase y también necesita agregar un serializador personalizado para todos sus objetos de matriz. ¿Pasaría tiempo codificando una subclase, o usaría composición o ... La propiedad prototipo resuelve esto al permitir que los usuarios controlen el conjunto exacto de miembros / métodos disponibles para una clase.Piense en los prototipos como un puntero vtable adicional. Cuando faltan algunos miembros de la clase original, el prototipo se busca en tiempo de ejecución.
fuente
Puede ser útil clasificar las cadenas de prototipos en dos categorías.
Considere el constructor:
El valor de
Object.getPrototypeOf(Person)
es una función. De hecho lo esFunction.prototype
. ComoPerson
se creó como una función, comparte el mismo objeto de función prototipo que tienen todas las funciones. Es lo mismo quePerson.__proto__
, pero esa propiedad no debe usarse. De todos modos, conObject.getPrototypeOf(Person)
usted efectivamente sube la escalera de lo que se llama la cadena prototipo.La cadena en dirección ascendente se ve así:
Person
→Function.prototype
→Object.prototype
(punto final)Importante es que esta cadena de prototipos tiene poco que ver con los objetos que
Person
pueden construir . Esos objetos construidos tienen su propia cadena de prototipo, y esta cadena potencialmente no puede tener un ancestro cercano en común con el mencionado anteriormente.Tome por ejemplo este objeto:
p no tiene una relación directa de cadena de prototipo con Person . Su relación es diferente. El objeto p tiene su propia cadena prototipo. Usando
Object.getPrototypeOf
, encontrará que la cadena es la siguiente:p
→Person.prototype
→Object.prototype
(punto final)No hay ningún objeto de función en esta cadena (aunque eso podría ser).
Por lo tanto,
Person
parece estar relacionado con dos tipos de cadenas, que viven sus propias vidas. Para "saltar" de una cadena a la otra, usa:.prototype
: salta de la cadena del constructor a la cadena del objeto creado. Por lo tanto, esta propiedad solo se define para objetos de función (yanew
que solo se puede usar en funciones)..constructor
: salta de la cadena del objeto creado a la cadena del constructor.Aquí hay una presentación visual de las dos cadenas prototipo involucradas, representadas como columnas:
Resumir:
No sorprende que el nombre de la propiedad
prototype
pueda generar confusión. Quizás hubiera sido más claro si esta propiedad hubiera sido nombradaprototypeOfConstructedInstances
o algo así.Puede saltar de un lado a otro entre las dos cadenas prototipo:
Esta simetría se puede romper asignando explícitamente un objeto diferente a la
prototype
propiedad (más sobre eso más adelante).Crear una función, obtener dos objetos
Person.prototype
es un objeto que se creó al mismo tiempo que se creó la funciónPerson
. TienePerson
como constructor, a pesar de que ese constructor aún no se ejecutó. Entonces se crean dos objetos al mismo tiempo:Person
síAmbos son objetos, pero tienen diferentes roles: el objeto de la función construye , mientras que el otro objeto representa el prototipo de cualquier objeto que la función construya. El objeto prototipo se convertirá en el padre del objeto construido en su cadena prototipo.
Dado que una función también es un objeto, también tiene su propio padre en su propia cadena de prototipo, pero recuerde que estas dos cadenas son sobre cosas diferentes.
Aquí hay algunas igualdades que podrían ayudar a comprender el problema: todas estas se imprimen
true
:Agregar niveles a la cadena prototipo
Aunque se crea un objeto prototipo cuando crea una función de constructor, puede ignorar ese objeto y asignar otro objeto que debería usarse como prototipo para cualquier instancia posterior creada por ese constructor.
Por ejemplo:
Ahora la cadena prototipo de t es un paso más larga que la de p :
t
→p
→Person.prototype
→Object.prototype
(punto final)La otra cadena de prototipos ya no es:
Thief
yPerson
son hermanos que comparten el mismo padre en su cadena de prototipos:Person
}Thief
} →Function.prototype
→Object.prototype
(punto final)El gráfico presentado anteriormente se puede extender a esto (el original
Thief.prototype
se omite):Las líneas azules representan cadenas prototipo, las otras líneas de colores representan otras relaciones:
fuente
La guía definitiva para JavaScript orientado a objetos : una explicación en video muy concisa y clara de ~ 30 minutos de la pregunta formulada (el tema de herencia prototípica comienza a partir de las 5:45 , aunque prefiero escuchar el video completo). El autor de este video también creó el sitio web del visualizador de objetos JavaScript http://www.objectplayground.com/ .
fuente
Me pareció útil explicar la "cadena de prototipo" como convención recursiva cuando
obj_n.prop_X
se hace referencia a ella:si
obj_n.prop_X
no existe, verifiqueobj_n+1.prop_X
dóndeobj_n+1 = obj_n.[[prototype]]
Si
prop_X
finalmente se encuentra en el objeto prototipo k-ésimo, entoncesobj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
Puede encontrar un gráfico de la relación de los objetos Javascript por sus propiedades aquí:
http://jsobjects.org
fuente
Cuando un constructor crea un objeto, ese objeto hace referencia implícita a la propiedad "prototipo" del constructor con el fin de resolver referencias de propiedad. La expresión de programa constructor.prototype puede hacer referencia a la propiedad del "prototipo" del constructor, y todos los objetos que comparten el prototipo comparten las propiedades agregadas al prototipo de un objeto, a través de la herencia.
fuente
Aquí hay dos entidades distintas pero relacionadas que deben explicarse:
.prototype
propiedad de las funciones.[[Prototype]]
[1] de todos los objetos [2] .Estas son dos cosas diferentes.
La
[[Prototype]]
propiedad:Esta es una propiedad que existe en todos los objetos [2] .
Lo que se almacena aquí es otro objeto, que, como un objeto en sí, tiene uno
[[Prototype]]
propio que apunta a otro objeto. Ese otro objeto tiene uno[[Prototype]]
propio. Esta historia continúa hasta llegar al objeto prototípico que proporciona métodos accesibles en todos los objetos (como.toString
).La
[[Prototype]]
propiedad es parte de lo que forma la[[Prototype]]
cadena. Esta cadena de[[Prototype]]
objetos es lo que se examina cuando, por ejemplo,[[Get]]
o[[Set]]
se realizan operaciones en un objeto:La
.prototype
propiedad:Esta es una propiedad que solo se encuentra en las funciones. Usando una función muy simple:
La
.prototype
propiedad contiene un objeto al que se le asignaráb.[[Prototype]]
cuando lo hagavar b = new Bar
. Puedes examinar esto fácilmente:Uno de los más importantes
.prototype
es el de laObject
función . Este prototipo contiene el objeto prototípico que[[Prototype]]
contienen todas las cadenas. En él, se definen todos los métodos disponibles para nuevos objetos:Ahora, como
.prototype
es un objeto, tiene una[[Prototype]]
propiedad. Cuando no realiza ninguna asignación aFunction.prototype
, el.prototype
's[[Prototype]]
apunta al objeto prototípico (Object.prototype
). Esto se realiza automáticamente cada vez que crea una nueva función.De esta manera, cada vez que haga
new Bar;
la cadena de prototipos está configurada para usted, obtendrá todo definidoBar.prototype
y todo definido enObject.prototype
:Cuando haces asignaciones a
Function.prototype
todo lo que estás haciendo es extender la cadena del prototipo para incluir otro objeto. Es como una inserción en una lista individualmente vinculada.Básicamente, esto altera la
[[Prototype]]
cadena permitiendo que las propiedades definidas en el objeto asignadoFunction.prototype
sean vistas por cualquier objeto creado por la función.[1: Eso no confundirá a nadie; disponible a través de la
__proto__
propiedad en muchas implementaciones.[2]: Todos excepto
null
.fuente
Déjame decirte mi comprensión de los prototipos. No voy a comparar la herencia aquí con otros idiomas. Desearía que la gente dejara de comparar idiomas y simplemente entendiera el idioma como tal. Comprender los prototipos y la herencia de prototipos es tan simple, como te mostraré a continuación.
El prototipo es como un modelo, en función del cual crea un producto. El punto crucial a entender es que cuando crea un objeto utilizando otro objeto como prototipo, el vínculo entre el prototipo y el producto es duradero. Por ejemplo:
Cada objeto contiene una propiedad interna llamada [[prototipo]], a la que puede acceder el
Object.getPrototypeOf()
función.Object.create(model)
crea un nuevo objeto y establece su propiedad [[prototipo]] en el modelo de objeto . Por lo tanto, cuando lo hagaObject.getPrototypeOf(product)
, obtendrá el modelo de objeto .Las propiedades en el producto se manejan de la siguiente manera:
Tal vinculación de objetos usando la propiedad prototipo se llama herencia prototípica. Ahí, es tan simple, ¿de acuerdo?
fuente
Otro intento de explicar la herencia basada en prototipos de JavaScript con mejores imágenes
fuente
Considere el siguiente
keyValueStore
objeto:Puedo crear una nueva instancia de este objeto haciendo esto:
Cada instancia de este objeto tendría las siguientes propiedades públicas:
data
get
set
delete
getLength
Ahora, supongamos que creamos 100 instancias de este
keyValueStore
objeto. A pesar de queget
,set
,delete
,getLength
va a hacer exactamente lo mismo para cada uno de estos 100 casos, cada caso tiene su propia copia de esta función.Ahora, imagínese si usted podría tener una sola
get
,set
,delete
ygetLength
copia, y cada instancia haría referencia a esa misma función. Esto sería mejor para el rendimiento y requeriría menos memoria.Ahí es donde entran los prototipos. Un prototipo es un "plano" de propiedades que se hereda pero no se copia por instancias. Esto significa que existe solo una vez en la memoria para todas las instancias de un objeto y es compartido por todas esas instancias.
Ahora, considere el
keyValueStore
objeto nuevamente. Podría reescribirlo así:Esto hace EXACTAMENTE lo mismo que la versión anterior del
keyValueStore
objeto, excepto que todos sus métodos ahora se colocan en un prototipo. Lo que esto significa es que las 100 instancias ahora comparten estos cuatro métodos en lugar de que cada uno tenga su propia copia.fuente
Resumen:
new
palabra clave, el objeto obtiene el prototipo. Se puede encontrar una referencia a este prototipo en la__proto__
propiedad del objeto recién creado.__proto__
propiedad se refiere a laprototype
propiedad de la función constructora.Ejemplo:
¿Por qué es útil esto?
Javascript tiene un mecanismo al buscar propiedades en Objetos que se llama 'herencia prototípica' , esto es lo que básicamente hace:
Por ejemplo:
Actualizar:
La
__proto__
propiedad ha quedado en desuso, aunque se implementa en la mayoría de los navegadores modernos, una mejor manera de obtener la referencia del objeto prototipo sería:Object.getPrototypeOf()
fuente
Siempre me gustan las analogías cuando se trata de entender este tipo de cosas. La 'herencia prototípica' es bastante confusa en comparación con la herencia de graves de clase en mi opinión, a pesar de que los prototipos son un paradigma mucho más simple. De hecho, con los prototipos, realmente no hay herencia, por lo que el nombre en sí mismo es engañoso, es más un tipo de "delegación".
Imagina esto ....
Estás en la secundaria, estás en clase y tienes un examen que vence hoy, pero no tienes un bolígrafo para completar tus respuestas. Doh!
Estás sentado al lado de tu amigo Finnius, quien podría tener un bolígrafo. Usted pregunta, y él mira alrededor de su escritorio sin éxito, pero en lugar de decir "No tengo un bolígrafo", es un buen amigo que consulta con su otro amigo Derp si tiene un bolígrafo. De hecho, Derp tiene una lapicera de repuesto y se la pasa a Finnius, quien se la pasa a usted para completar su cuestionario. Derp le ha confiado la pluma a Finnius, quien le ha delegado la pluma para que la use.
Lo importante aquí es que Derp no te da la pluma, ya que no tienes una relación directa con él.
Este es un ejemplo simplificado de cómo funcionan los prototipos, donde se busca en un árbol de datos lo que está buscando.
fuente
otro esquema que muestra las relaciones __proto__ , prototipo y constructor :
fuente
Es solo que ya tienes un objeto
Object.new
pero aún no tienes un objeto cuando usas la sintaxis del constructor.fuente
Referencia: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
fuente
El prototipo crea un nuevo objeto clonando un objeto existente . Entonces, cuando pensamos en el prototipo, realmente podemos pensar en clonar o hacer una copia de algo en lugar de inventarlo.
fuente