Lyrische Einführung
Nachdem ich beim nächsten Interview erneut eine Reihe von Fragen zu Prototypen erhalten hatte, stellte ich fest, dass ich die Feinheiten des Prototyping leicht vergessen hatte, und beschloss, mein Wissen aufzufrischen. Ich stieß auf eine Reihe von Artikeln, die entweder auf Inspiration des Autors geschrieben wurden, wie er die Prototypen "fühlt", oder der Artikel befasste sich mit einem separaten Teil des Themas und gab kein vollständiges Bild davon, was geschah.
Es stellt sich heraus, dass es viele nicht offensichtliche Dinge aus den alten Tagen von ES5 und sogar ES6 gibt, von denen ich noch nichts gehört hatte. Es stellte sich auch heraus, dass die Ausgabe der Browserkonsole möglicherweise nicht der Realität entspricht.
Was ist ein Prototyp?
Das Objekt in JS hat seine eigenen und geerbten Eigenschaften, z. B. in diesem Code:
var foo = { bar: 1 };
foo.bar === 1 // true
typeof foo.toString === "function" // true
Das Objekt foohat eine eigene Eigenschaft barmit einem Wert 1, aber es hat auch andere Eigenschaften wie toString. Um zu verstehen, wie ein Objekt fooeine neue Eigenschaft erhält toString, schauen wir uns an, woraus das Objekt besteht:
Der Punkt ist, dass ein Objekt einen Verweis auf ein anderes Prototypobjekt hat. Beim Zugriff auf ein Feld foo.toStringwird eine Suche nach einer solchen Eigenschaft zuerst vom Objekt selbst und dann von seinem Prototyp, dem Prototyp seines Prototyps usw. durchgeführt, bis die Prototypkette endet. Es ist wie eine einfach verknüpfte Liste von Objekten, in der das Objekt und seine Prototypobjekte nacheinander überprüft werden. Auf diese Weise wird beispielsweise die Vererbung von Eigenschaften implementiert (fast, aber dazu später mehr). Jedes Objekt verfügt über Methoden valueOfund 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.
Es wäre naiv zu erwarten, dass ein Artikel alle Fragen beantwortet. Wenn Sie interessante Fragen, Exkursionen in die Geschichte, begründete oder unbegründete Aussagen, dass ich alles falsch gemacht habe, oder Korrekturen für Fehler haben, schreiben Sie in die Kommentare.