Este 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:
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person("George");
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 utilizar prototype
y new
palabra clave .
Entonces, en este paso functions
, objects
y this
palabra clave, es todo lo que tenemos.
La primera pregunta sería cómo la this
palabra clave podría ser útil sin usar la new
palabra clave .
Entonces, para responder eso, digamos que tenemos un objeto vacío y dos funciones como:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
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:
Person("George");
getName();//would print the "George" in the console
en este caso, este sería el objeto de contexto actual, que generalmente es el window
objeto global en el navegador o GLOBAL
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:
person.Person = Person;
person.getName = getName;
de esta manera podemos llamarlos como:
person.Person("George");
person.getName();// -->"George"
y ahora el person
objeto es como:
Object {Person: function, getName: function, name: "George"}
- 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:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
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 de Object.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:
person.__proto__ = {
Person: Person,
getName: getName
};
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:
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
lo que hace es crear un nuevo JavaScript Object
y adjuntarlo propertiesObject
al __proto__
atributo. Entonces, para asegurarse de que puede hacer:
console.log(person.__proto__===propertiesObject); //true
Pero el punto difícil aquí es que tiene acceso a todas las propiedades definidas en __proto__
el primer nivel del person
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 al person
objeto.
C. JavaScript tiene otra forma de proporcionar la función this
, que es llamar o aplicar para invocar la función.
El método apply () llama a una función con un valor dado y argumentos proporcionados como una matriz (o un objeto similar a una matriz).
y
El método call () llama a una función con un valor dado y argumentos proporcionados individualmente.
de esta manera, mi favorita, podemos llamar fácilmente a nuestras funciones como:
Person.call(person, "George");
o
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
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:
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
en esta parte voy a tratar de seguir todos los pasos que toma JavaScript, sin usar la new
palabra clave y prototype
, cuando use la new
palabra clave. así que cuando lo hacemos new Person("George")
, la Person
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:
var newObject = {};
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.
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
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:
var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
ahora podemos llamar a la getName
función en nuestro my_person_prototype
:
newObject.getName();
C. entonces le da ese objeto al constructor,
podemos hacer esto con nuestra muestra como:
Person.call(newObject, "George");
o
Person.apply(newObject, ["George"]);
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:
new FunctionName()
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 el Object.prototype
objeto:
console.log(Object.prototype.__proto__===null);//true
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í.