Pregunta ¿Cómo funciona JavaScript .prototype?


No me gustan los lenguajes de programación dinámicos, pero he escrito mi parte justa de código JavaScript. Nunca entendí esta programación basada en prototipos, ¿alguien sabe cómo funciona esto?

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

Recuerdo una gran cantidad de discusiones que tuve con gente hace un tiempo (no estoy exactamente seguro de lo que estoy haciendo) pero, según tengo entendido, no existe el concepto de una 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 esto .prototype propiedad en JavaScript? ¿Cómo se relaciona con la instanciación de objetos?


Editar

Estas diapositivas realmente ayudó mucho a entender este tema.


1856
2018-02-21 12:31


origen


Respuestas:


Cada objeto JavaScript tiene una propiedad interna llamada [[Prototipo]]. Si busca una propiedad a través de obj.propName o obj['propName'] y el objeto no tiene esa propiedad, que se puede verificar a través de obj.hasOwnProperty('propName') - el tiempo de ejecución busca la propiedad en el objeto referenciado por [[Prototype]]. Si el prototipo-objeto tampoco tiene esa propiedad, su prototipo se verifica a su vez, caminando por el objeto original cadena de prototipos hasta que se encuentre una coincidencia o se llegue a su fin.

Algunas implementaciones de JavaScript permiten el acceso directo a la propiedad [[Prototype]], por ejemplo, a través de una propiedad no estándar llamada __proto__. En general, solo es posible establecer el prototipo de un objeto durante la creación del objeto: si creas un nuevo objeto a través de new Func(), la propiedad del objeto [[Prototype]] se establecerá en el objeto al que hace referencia Func.prototype.

Esto permite simular clases en JavaScript, aunque el sistema de herencia de JavaScript es, como hemos visto, prototípico y no basado en clases:

Solo piense en las funciones de constructor como clases y las propiedades del prototipo (es decir, del objeto al que hace referencia la función del constructor prototype propiedad) 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 normalmente se agregan métodos al prototipo, mientras que los campos de un objeto son específicos de la instancia y, por lo tanto, se agregan al objeto durante la construcción.


925
2018-02-21 13:33



En un lenguaje que implementa herencia clásica como Java, C # o C ++, comienzas creando una clase, un diseño para tus objetos, y luego puedes crear nuevos objetos a partir de esa clase o puedes extender la clase, definiendo una nueva clase que aumente. la clase original.

En JavaScript, primero crea un objeto (no hay ningún 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:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Hasta ahora he estado ampliando el objeto base, ahora creo otro objeto y luego heredé de Persona.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Mientras que como dije no puedo llamar a setAmountDue (), getAmountDue () en una persona.

//The following statement generates an error.
john.setAmountDue(1000);

1755
2018-01-24 03:42



Tengo un papel como profesor de JavaScript y el concepto de prototipo siempre ha sido un tema controvertido para cubrir cuando enseño. Me tomó un tiempo encontrar un buen método para aclarar el concepto, y ahora en este texto voy a tratar de explicar cómo funciona el JavaScript .prototype.


Este es un modelo de objeto basado en un prototipo muy simple que se consideraría como una muestra durante la explicación, sin comentarios 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 debemos considerar antes de pasar por el concepto de prototipo.

1- Cómo funcionan en realidad las funciones de JavaScript:

Para dar el primer paso, tenemos que descubrir cómo funcionan realmente las funciones de JavaScript, como una clase que usa funciones this palabra clave en él 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 intentar hacer lo mismo sin usar prototype y new palabra clave.

Entonces en este paso functions, objects y this palabra clave, son todo lo que tenemos.

La primera pregunta sería cómo this palabra clave podría ser útil sin usar 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 uso new palabra clave cómo podríamos usar estas funciones. Entonces JavaScript tiene 3 formas diferentes de hacerlo:

a. La primera forma es simplemente 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 global window objeto en el navegador o GLOBAL en Node.js. Significa que tendríamos, window.name en el navegador o GLOBAL.name en Node.js, con "George" como su valor.

segundo. Podemos adjuntar a un objeto, como sus propiedades

-La forma más fácil hacer esto es modificar el vacío person objeto, como:

person.Person = Person;
person.getName = getName;

de esta manera podemos llamarlos así:

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 está utilizando el prototype de ese objeto que se puede encontrar en cualquier objeto de JavaScript con el nombre de __proto__, y he tratado de explicarlo un poco en la parte resumida. Para que podamos obtener el resultado similar haciendo:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Pero De esta manera, lo que realmente estamos haciendo es modificar el Object.prototype, porque cada vez que creamos un objeto JavaScript usando literales ({ ... }), se crea en base a Object.prototype, lo que significa que se adjunta al objeto recién creado como un atributo llamado __proto__ , así que si lo cambiamos, como lo hemos hecho en nuestro fragmento de código anterior, todos los objetos de JavaScript 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 objeto creado (var person = {};) luego cámbialo como:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

lo que hace es crear un nuevo JavaScript Object y adjunte el propertiesObject al __proto__ atributo. Para asegurarse de que puede hacer:

console.log(person.__proto__===propertiesObject); //true

Pero el punto complicado aquí es que tienes acceso a todas las propiedades definidas en __proto__ en el primer nivel de la person objeto (lea la parte del resumen para más detalles).


como ves usando cualquiera de estos dos sentidos this señalaría exactamente a la person objeto.

do. JavaScript tiene otra forma de proporcionar la función thisque está usando llamada o aplicar para invocar la función.

El método apply () llama a una función con un valor dado y dado   argumentos proporcionados como una matriz (o un objeto parecido a una matriz).

y

El método call () llama a una función con un valor dado y dado   argumentos proporcionados individualmente.

De esta manera, que es mi favorito, 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 el new la palabra clave funciona?

este es el segundo paso para entender el .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 intentar tomar todos los pasos que toma JavaScript, sin usar el new palabra clave y prototypecuando usas new palabra clave. entonces cuando lo hacemos new Person("George"), 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 = {};

segundo. el siguiente paso que toma JavaScript es adjuntar todos los prototipos se oponen al objeto recién creado

tenemos my_person_prototype aquí es 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 del prototipo.


a. & b. En lugar de estos dos pasos, puede obtener exactamente el mismo resultado:

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 al getName función en nuestro my_person_prototype:

newObject.getName();

do. luego 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 esta dentro de ese constructor está el objeto que se acaba de crear.

ahora el resultado final antes de simular los otros pasos:     Objeto {nombre: "George"}


Resumen:

Básicamente, cuando usas el nuevo palabra clave en una función, está llamando sobre eso y esa función sirve como un constructor, por lo que cuando diga:

new FunctionName()

JavaScript hace internamente un objeto, un hash vacío y luego le da ese objeto al constructor, luego el constructor puede hacer lo que quiera, porque esta dentro de ese constructor está el objeto que se acaba de crear y luego le da ese objeto, por supuesto, si no ha usado la declaración de devolución en su función o si ha puesto un return undefined; al final de tu cuerpo de 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 generalmente lo tenemos como __proto__ y esa propiedad es lo que JavaScript mira a continuación. Y cuando mira a través de __proto__, en cuanto a que es otro objeto JavaScript, tiene su propio __proto__atributo, sube y sube hasta que llega al punto donde el próximo __proto__ es nulo. El punto es el único objeto en JavaScript que es __proto__ atributo es nulo es Object.prototype objeto:

console.log(Object.prototype.__proto__===null);//true

y así es como funciona la herencia en JavaScript.

The prototype chain

En otras palabras, cuando tienes una propiedad prototipo en una función y llamas a una nueva sobre eso, después de que JavaScript termina de mirar ese objeto recién creado para las propiedades, irá a la función de .prototype y también es posible que este objeto tenga su propio prototipo interno. y así.


162
2018-02-13 19:32



prototype te permite hacer clases si no usas prototype entonces se convierte en una estática.

Aquí hay un pequeño ejemplo.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

En el caso anterior, tiene una prueba de llamada funcation estática. A esta función solo se puede acceder mediante obj.test donde se puede imaginar que obj es una clase.

donde como en el código a continuación

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

El obj se ha convertido en una clase que ahora se puede instanciar. Pueden existir varias instancias de obj y todas tienen el test función.

Lo de arriba es mi entendimiento. Lo estoy convirtiendo en una wiki comunitaria, para que la gente pueda corregirme si me equivoco.


66
2017-11-07 09:48



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 *[[protytype]]* and <code>prototype</code> property of function objects

es un gráfico claro para mostrar la herencia de JavaScript por cadena de prototipos

y

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

este contiene un ejemplo con código y varios diagramas agradables.

La cadena de prototipos finalmente vuelve a Object.prototype.

La cadena de prototipos se puede extender técnicamente el tiempo que desee, cada vez configurando el prototipo de la subclase igual a un objeto de la clase principal.

Espero que también te sea útil comprender la cadena de prototipos de JavaScript.


59
2018-05-26 20:40



Los siete Koans del prototipo

Cuando Ciro San descendió de Mount Fire Fox después de una profunda meditación, su mente era clara y pacífica.

Su mano, sin embargo, estaba inquieta, y solo tomó un pincel y anotó las siguientes notas.


0) Dos cosas diferentes se pueden llamar "prototipo":

  • el prototipo de propiedad, como en obj.prototype

  • el prototipo de propiedad interna, denotado como [[Prototype]]  en ES5.

    Se puede recuperar a través de ES5 Object.getPrototypeOf().

    Firefox lo hace accesible a través de __proto__ propiedad como una extensión. ES6 ahora menciona algunos requisitos opcionales para __proto__.


1) Esos conceptos existen para responder la pregunta:

Cuando lo hago obj.property, ¿dónde busca JS? .property?

Intuitivamente, la herencia clásica debería afectar la búsqueda de propiedades.


2)

  • __proto__ se usa para el punto . búsqueda de propiedades como en obj.property.
  • .prototype es no utilizado para buscar directamente, solo de forma indirecta, ya que determina __proto__ en la creación de objetos con new.

La orden de búsqueda es:

  • obj propiedades agregadas con obj.p = ... o Object.defineProperty(obj, ...)
  • propiedades de obj.__proto__
  • propiedades de obj.__proto__.__proto__, y así
  • si algun __proto__ es null, regreso undefined.

Este es el llamado cadena de prototipos.

Puedes evitar . búsqueda con obj.hasOwnProperty('key') y Object.getOwnPropertyNames(f)


3) Hay dos formas principales de establecer obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()
    

    entonces new ha establecido:

    f.__proto__ === F.prototype
    

    Esta es donde .prototype se acostumbra

  • Object.create:

     f = Object.create(proto)
    

    conjuntos:

    f.__proto__ === proto
    

4) El código:

var F = function() {}
var f = new F()

Corresponde al siguiente diagrama:

(Function)       (  F  )                                      (f)
 |  ^             | | ^                                        |
 |  |             | | |                                        |
 |  |             | | +-------------------------+              |
 |  |constructor  | |                           |              |
 |  |             | +--------------+            |              |
 |  |             |                |            |              |
 |  |             |                |            |              |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |
 |  |             |                |            |              |
 |  |             |                | +----------+              |
 |  |             |                | |                         |
 |  |             |                | | +-----------------------+
 |  |             |                | | |
 v  |             v                v | v
(Function.prototype)              (F.prototype)
 |                                 |
 |                                 |
 |[[Prototype]]                    |[[Prototype]]
 |                                 |
 |                                 |
 | +-------------------------------+
 | |
 v v
(Object.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Este diagrama muestra muchos nodos de objetos predefinidos en el lenguaje: null, Object, Object.prototype, Function y Function.prototype. Nuestras 2 líneas de código solo crearon f, F y F.prototype.


5)  .constructor normalmente viene de F.prototype a través de . buscar:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Cuando escribimos f.constructor, JavaScript hace . buscar como:

  • f no tiene .constructor
  • f.__proto__ === F.prototype tiene .constructor === F, así que tómalo

El resultado f.constructor == F es intuitivamente correcto, ya que F se usa para construir f, p.ej. establecer campos, al igual que en los lenguajes OOP clásicos.


6) La sintaxis de herencia clásica se puede lograr manipulando cadenas de prototipos.

ES6 agrega el class y extends palabras clave, que son simplemente azúcar de sintaxis para la locura de manipulación de prototipos previamente posible.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Diagrama simplificado sin todos los objetos predefinidos:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

57
2018-06-18 19:48



Cada objeto tiene una propiedad interna, [[Prototype]], vinculándolo a otro objeto:

object [[Prototype]] -> anotherObject

En javascript tradicional, el objeto vinculado es el prototype propiedad de una función:

object [[Prototype]] -> aFunction.prototype

Algunos entornos exponen [[Prototype]] como __proto__:

anObject.__proto__ === anotherObject

Crea el enlace [[Prototype]] al crear un objeto.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Entonces estas declaraciones son equivalentes:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

UN new La declaración no muestra el destino del enlace (Object.prototype) sí mismo; en cambio, el objetivo está implícito en el constructor (Object)

Recuerda:

  • Cada objeto tiene un enlace, [[Prototype]], a veces expuesto como __proto__.
  • Cada función tiene un prototype propiedad.
  • Objetos creados con new están vinculados a la prototype propiedad de su constructor.
  • Si una función nunca se usa como un constructor, es prototype la propiedad no se usará.
  • Si no necesita un constructor, use Object.create en lugar de new.

33
2018-02-21 12:41