Introduction lyrique
Ayant à nouveau reçu un tas de questions sur les prototypes lors de la prochaine interview, je me suis rendu compte que j'avais un peu oublié les subtilités du prototypage, et j'ai décidé de rafraîchir mes connaissances. Je suis tombé sur un tas d'articles qui ont été écrits soit sur l'inspiration de l'auteur, comment il "ressent" les prototypes, soit l'article portait sur une partie distincte du sujet et ne donnait pas une image complète de ce qui se passait.
Il s'avère qu'il y a beaucoup de choses non évidentes de l'ancien temps d'ES5 et même d'ES6 dont je n'avais pas entendu parler. Il s'est également avéré que la sortie de la console du navigateur pouvait ne pas correspondre à la réalité.
Qu'est-ce qu'un prototype
L'objet dans JS a ses propres propriétés héritées, par exemple, dans ce code:
var foo = { bar: 1 };
foo.bar === 1 // true
typeof foo.toString === "function" // true
l'objet foo
a sa propre propriété bar
avec une valeur 1
, mais il a également d'autres propriétés telles que toString
. Pour comprendre comment un objet foo
obtient une nouvelle propriété toString
, voyons en quoi consiste l'objet:
Le fait est qu'un objet a une référence à un autre objet prototype. Lors de l'accès à un champ foo.toString
, une recherche d'une telle propriété est d'abord effectuée à partir de l'objet lui-même, puis à partir de son prototype, le prototype de son prototype, et ainsi de suite jusqu'à la fin de la chaîne de prototypes. C'est comme une liste d'objets liés individuellement, où l'objet et ses objets prototypes sont vérifiés tour à tour. C'est ainsi que l'héritage des propriétés est implémenté, par exemple (presque, mais plus à ce sujet plus tard) tout objet a des méthodes valueOf
et toString
.
, constructor
__proto__
. constructor
-, , __proto__
( null, ). .
, .
constructor
constructor
– , :
const a = {};
a.constructor === Object // true
, , :
object.constructor(object.arg)
, , , . constructor
, writable , , , .
, , JS . , , [[SlotName]]
. [[Prototype]]
- ( null
, ).
- , [[Prototype]]
JS , . , __proto__
, , JS .
,
__proto__
[[Prototype]]
Object.prototype
:
- __proto__
. __proto__
, . __proto__
:
const foo = {};
foo.toString(); // toString() Object.prototype '[object Object]',
foo.__proto__ = null; // null
foo.toString(); // TypeError: foo.toString is not a function
foo.__proto__ = Object.prototype; //
foo.toString(); // , TypeError: foo.toString is not a function
? , __proto__
– Object.prototype
, foo
. - Object.prototype
, __proto__
.
. :
var baz = { test: "test" };
var foo = { bar: 1 };
foo.__proto__ = baz;
Chrome foo :
baz
Object.prototype
:
baz.__proto__ = null;
Chrome :
Object.prototype
baz
__proto__
undefined
foo
, Chrome __proto__
. [[Prototype]]
, __proto__
, , .
: .
: __proto__
Object.setPrototypeOf
.
var myProto = { name: "Jake" };
var foo = {};
Object.setPrototypeOf(foo, myProto);
foo.__proto__ = myProto;
, , .
[[Extensible]]
, . , false : Object.freeze
, Object.seal
, Object.preventExtensions
. :
const obj = {};
Object.preventExtensions(obj);
Object.setPrototypeOf(obj, Function.prototype); // TypeError: #<Object> is not extensible
. .
:
const foo = Object.create(myPrototype);
Object.create
, __proto__
:
const foo = { __proto__: myPrototype };
:
const f = function () {}
f.prototype = myPrototype;
const foo = new f();
new
, . , new
prototype
, .. [[Prototype]]
, .
.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const user = new Person('John', 'Doe');
Person , :
Person.prototype
? , prototype
(note 3), prototype
, . , :
Person.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
}
user.fullName()
"John Doe".
new
new . new :
- self
- prototype self
- self this
- self ,
, new :
function custom_new(constructor, args) {
// https://stackoverflow.com/questions/31538010/test-if-a-variable-is-a-primitive-rather-than-an-object
function isPrimitive(val) {
return val !== Object(val);
}
const self = Object.create({});
const constructorValue = constructor.apply(self, args) || self;
return isPrimitive(constructorValue) ? self : constructorValue;
}
custom_new(Person, ['John', 'Doe'])
ES6 new new.target, , new, :
function Foo() {
console.log(new.target === Foo);
}
Foo(); // false
new Foo(); // true
new.target
undefined , new
;
, Student
Person
.
- Student Person
- `Student.prototype` `Person`
- `Student.prototype`
function Student(firstName, lastName, grade) {
Person.call(this, firstName, lastName);
this.grade = grade;
}
// 1
Student.prototype = Object.create(Person.prototype, {
constructor: {
value:Student,
enumerable: false,
writable: true
}
});
// 2
Object.setPrototypeOf(Student.prototype, Person.prototype);
Student.prototype.isGraduated = function() {
return this.grade === 0;
}
const student = new Student('Judy', 'Doe', 7);
( , .. this ), ( )
1 , .. Object.setPrototypeOf
.
, , Person Student:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
class Student extends Person {
constructor(firstName, lastName, grade) {
super(firstName, lastName);
this.grade = grade;
}
isGraduated() {
return this.grade === 0;
}
}
, :
- , new
-
prototype .
P.S.
Il serait naïf d'attendre qu'un article réponde à toutes les questions. Si vous avez des questions intéressantes, des excursions dans l'histoire, des déclarations raisonnées ou sans fondement selon lesquelles j'ai tout fait de mal, ou des corrections d'erreurs, écrivez dans les commentaires.