What is the[[Prototype]]
?
Objects in JavaScript have a special [[Prototype]] built-in property that is essentially a reference to other objects. Almost all objects are created with the [[Prototype]] property given a non-null value. (The [[Prototype]] property is empty, too, though not very often).
const myObj = {
a: 'hello prototype'
}
myObj.a // 'hello prototype'
Copy the code
The [[GET]] operation is triggered when we use the.reference or [] reference attribute a. The default [[GET]] operation first checks to see if the object itself has the property and if so uses it, otherwise uses the object [[Prototype]] chain.
const sourceObj = {
a: 'hello prototype'
};
// Create an object associated with sourceObj
const targetObj = Object.create(sourceObj);
targetObj.a // 'hello prototype'
Copy the code
The [[Prototype]] attribute is not available in the targetObj Object, but we use the object. create method to create the Object and associate it with sourceObj. However, if attribute A does not exist on sourceObj and [[Prototype]] is not null, the search will continue. The process continues until a matching property is found or a complete [[Prototype]] chain is found. In the latter case, the [[GET]] operation returns undefined.
Use for… Any property that is enumerable and can be accessed through the [[Prototype]] chain is enumerable. The in operator also looks for the full prototype chain (whether enumerable or not).
const sourceObj = {
a: 'hello prototype'
};
// Create an object associated with sourceObj
const targetObj = Object.create(sourceObj);
for(let key in targetObj){
console.log('key:' + key); //key: a
}
Object.defineProperty(myObj, 'a', {
enumerable: false}); ('a' in targetObj) //true
Copy the code
Object.prototype
All normal [[Prototype]] chains eventually point to the built-in object.prototype. Since all normal objects are derived from (or set to object.prototype at the top of the [[Prototype]] chain), there are some built-in features in JavaScript. For example, toString() and valueOf().
Property Settings and masking
Setting a property on an object is not just a matter of adding a new property or changing the value of an existing property. Here is the complete procedure: myobj.bar = ‘Hello prototype’
- if
myObj
Common data access attributes exist inbar
, the existing attribute value is directly modified. - if
bar
There are bothmyObj
In and inmyObj
the[[Prototype]]
Chain, attribute masking occurs,myObj
In thebar
Will block[[Prototype]]
All of the upper levels of the chainbar
Because themyObj.bar
The lowest level of the prototype chain is always selectedbar
. - if
bar
There is no inmyObj
, which does not exist with the prototype chain, will be added directly tomyObj
On. ifbar
There is no inmyObj
, but exist in the prototype chain, then there are the following situations:
- If normal data access properties exist at the upper level of the prototype chain
bar
And is not marked read-only (writable:true
), will be directly in themyObj
addbar
, which is a masked property.
const sourceObj = {
bar: "2"};const myObj = Object.create(sourceObj);
myObj.bar = "345";
console.log(myObj, sourceObj); / / '345', '2'
Copy the code
- If there is one at the top of the prototype chain
bar
And is marked read-only (writable:false
), then can not be inmyObj
addbar
In strict mode, the code throws an error. In non-strict mode, the assignment statement is skipped. In short, no shielding will occur.
const sourceObj = {
bar: "2"};Object.defineProperty(sourceObj, "bar", {
writable: false});const myObj = Object.create(sourceObj);
myObj.bar = "345";
console.log(myObj.bar, sourceObj.bar); / / '2', '2'
Copy the code
- If there is one at the top of the prototype chain
bar
, and is onesetter
, then this will be calledsetter
, will not be added inmyObj
I’m not going to redefine thissetter
, that is, shielding does not occur.
const sourceObj = {
set bar(val) {
console.log("bar setter");
this._val_ = val; }};const myObj = Object.create(sourceObj);
myObj.bar = 2; // bar setter
console.log(myObj.bar); // undefined
Copy the code
If you want to mask the bar on the prototype chain in the second and third, you can’t assign via the = operator, but instead add using object.defineProperty.
“Class”
JavaScript differs from class-oriented languages in that it does not have classes as abstract patterns for objects. JavaScript only has objects. In fact, JavaScript is the language that really should be called ‘object oriented’ because it is one of the few languages that can create objects directly without using classes.
The class function
One of the most egregious abuses in JavaScript is the practice of mimicking classes. This strange behavior takes advantage of a peculiarity of functions: all functions have a public and non-enumerable property called Prototype, which points to another object.
function Bar() {}
Bar.prototype;
Copy the code
This object is often referred to as the prototype for Bar. What exactly is this object? The most straightforward explanation is that this object is created when new Bar() is called, and will eventually be associated with the bar.prototype object.
function Bar() {}
const bar = new Bar();
Object.getPrototypeOf(bar) === Bar.prototype; //true
Copy the code
Explain what the new operator does in javascript:
- Create (or construct) an entirely new object.
- The object will be prototyped.
- This object will be bound to the function call
this
. - If the function returns no other object, then
new
A function call in an expression automatically returns this new object.
function myNew(Bar, ... args) {
const obj = {};
Object.setPrototypeOf(obj, Bar.prototype);
let result = Bar.apply(obj, args);
return result instanceof Object ? result : obj
}
Copy the code
In a class-oriented language, a class can be instantiated multiple times, just as something can be moulded. This is true because instantiating (or inheriting) a class means copying the behavior of the class into a physical object (instance), and this process is repeated for each new instance. In JavaScript, however, there is no similar replication mechanism. Instead of creating multiple instances, you can only create multiple objects whose [[Prototype]] is associated with the same object, namely bar.prototype. By default, however, it is not copied, so the objects are not completely disconnected from each other; they are related to each other.
About the name
In JavaScript, we don’t copy one object (class) to another object (instance), we just associate them. Visually, the [[Prototype]] mechanism is as follows:
This mechanism is often referred to as prototype inheritance. But I think it’s precisely because of the name that it affects people’s understanding of how JavaScript really works. Inheritance means copying operations, but JavaScript(by default) does not copy object properties. Instead, JavaScript creates an association between two objects so that one object can access the properties and functions of the other through delegates. The term delegate more accurately describes the association mechanism of objects in JavaScript.
The constructor
function Bar(){}const bar = new Bar();
bar.constructor === Bar; //true
Copy the code
The above code makes it easy to think that Bar is a constructor, because we call it with the new operator and create an object Bar. In fact, Bar is no different from any other normal function. Objects are created here only because we use the new operator, so my understanding is that constructors in JavaScript are all function calls with new. In other words, a function is not a constructor, but it becomes a constructor if and only if the new operator is used.
Bar. The constructor = = = bar is easy to misunderstand for the bar has a pointer to a constructor of the bar, but in fact is not the case, the bar. The constructor is delegated to the bar. The prototype. The constructor, It has nothing to do with construction.
Here’s an example:
function Bar() {}
Bar.prototype = {};
const bar = new Bar();
console.log(bar.constructor === Bar); //false
console.log(bar.constructor === Object); //true
Copy the code
So, just because the function Bar definition to create a Bar. The prototype, Bar. The prototype. The constructor is pointing in the direction of the Bar itself by default, again by new objects created by [[prototype]] will point Bar. The prototype, However, when the reference to bar. prototype changes, there is no guarantee that bar.constructor === Bar, even if Bar is an object derived from Bar New. Therefore, bar.constructor is an unreliable and unsafe reference.
Prototype inheritance
The following code is a typical prototype style.
function Foo(name) {
this.name = name;
}
Foo.prototype.getName = function () {
return this.name;
};
function Bar(name, label) {
Foo.call(this, name);
this.label = label;
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.getLabel = function () {
return this.label;
};
const a = new Bar("a"."obj a");
a.getLabel(); // "obj a"
a.getName(); // "a"
Copy the code
The core of this code is bar.prototype = object.create (foo.prototype), calling object.create (…) This statement creates a new object and associates [[Prototype]] with the object you specified. In other words, this statement creates a new Bar.
Note that both of these are common mistakes, and both have problems:
Bar.prototype = Foo.prototype
Bar.prototype = new Foo()
Bar.prototype = foo.prototype does not create a new object associated with bar.prototype, it simply makes bar.prototype refer directly to the foo.prototype object. So when you do something like bar.prototype. getLabel =… The assignment statement changes foo. prototype itself directly. Obviously this is not what you want, otherwise you don’t need the Bar object at all, just use Foo, and the code is much simpler. Bar.prototype = new Foo() does create a new object associated with bar.prototype. However, it uses the constructor call for Foo, and if Foo has any side effects, it could affect the descendants of Bar().
Checking class relationships
Checking an instance (object in JavaScript) for its inherited ancestors (delegate associations in JavaScript) is often referred to as internal auditing (or reflection).
function Bar(){}
Bar.prototype.name = 'Bar' ;
const bar = new Bar();
Copy the code
How do we find the delegate association of bar through internal audit? The first way is to take a class view: the left operand of the bar instanceof operator is an ordinary object, and the right operand is a function. Instanceof answers the question: is there an object pointing to bar.prototype in the entire chain of prototypes of bar?
If a hardbound function is generated using bind, the function has no Prototype property. Using instanceof on such a function, the target function’s prototype replaces the hard-bound function’s prototype.
function Bar(name) {
this.name = name;
}
const obj = {};
const Baz = Bar.bind(obj);
console.log(Baz.prototype); //undefined
Copy the code
Determine whether two objects are linked by a prototype chain: a. int prototypeof (b)
We can also get the prototype chain of an object directly. The standard method in ES5 is object.getPrototypeof (bar) to verify that the Object is what we want: object.getPrototypeof (bar) === bar.prototype // true
Most (but not all) browsers also support a non-standard way to access it: bar.__proto__ === bar.prototype
Rough implementation of.__proto__ :
Object.definePrototype(Object.prototype, '__proto__', {
get: function(){
return Object.getPrototypeOf(this);
},
set: function(o){
Object.setPrototypeOf(this, o);
returno; }})Copy the code
Object associated
[[Prototype]] is an internal link that exists in an object that references other objects. In general, this link works: if the desired property or method reference is not found on the object, the engine will continue to look on the object associated with [[Prototype]]. Similarly, if no reference is found in the latter, it will continue to look for its [[Prototype]], and so on. This chain of objects is called a prototype chain.
Create a link
const foo = {
something: function(){
console.log('tell me something... ')}};const bar = Object.create(foo);
bar.something(); // 'tell me something... '
Copy the code
Object.create(..) We create a new object bar and associate it with our designated object foo, so that we can leverage the power of the [[Prototype]] mechanism (delegate) and avoid unnecessary hassles (such as Prototype and constructor references generated with new constructor calls).
Object.create(null) creates an Object with an empty (or null)[[Prototype]] link. This Object cannot delegate. Since this object does not have a prototype chain, the instanceof operator cannot be determined, so it always returns false. These special empty [[Prototype]] objects, often referred to as dictionaries, are completely undisturbed by the Prototype chain and are therefore ideal for storing data.
The association is standby
It seems that the previous association of objects is an alternate option for dealing with missing properties or methods. There’s some truth to this, but I don’t think it’s the nature of [[Prototype]].
const foo = {
something: function(){
console.log('tell me something... ')}};const bar = Object.create(foo);
bar.something(); // 'tell me something... '
Copy the code
Although this code works fine. But if you write it so that bar can use an alternate foo if it can’t handle properties or methods, then the code will be hard to understand and maintain later on.
const foo = {
something: function(){
console.log('tell me something... ')}};const bar = Object.create(foo);
bar.something = function(){
this.something();
}
bar.something(); // 'tell me something... '
Copy the code
Here we call bar.something(), which actually exists in bar, which makes our API design clearer and less magical. Internally, our implementation follows the delegate design pattern.
Conclusion:
To access a property that does not exist in the object, the [[Get]] operation looks for the object associated with the [[Prototype]] inside the object. This association defines a chain of stereotypes that are traversed when looking for properties.
All normal objects have a built-in __proto__ that points to the top of the stereotype chain and stops if the specified attribute is not found in the stereotype chain. Some common methods exist on object.prototype, so all objects can use them.
The most common method associated with two objects is a function call with the new keyword, which creates a new object associated with other objects in the four steps of the call.
Calling a function with new associates the new object’s Prototype property with other objects. Function calls with new are often referred to as constructor calls, although they are not really the same as class constructors in traditional class-oriented languages.
While these JavaScript mechanisms are similar to class initialization and class inheritance in traditional class-oriented languages, there is a core difference in the JavaScript mechanisms. Instead of copying, objects are associated via an internal prototype chain item =.
For a variety of reasons, terms that end in inheritance and other object-oriented terms don’t help you understand the true mechanics of JavaScript.
Delegate is a more appropriate term because the relationship between objects is not a copy but a delegate.
The content of this article is for personal study notes only. The content of this article is referred to as JavaScript you Don’t know.