Js asks from the perspective of language design: why do we design prototype
How did you design the prototype?
Think about it as a language designer. Why prototypes? Javascript designed the prototype, what problem is it trying to solve? There is no love without cause, nor hatred without cause. It’s not funny. It just happens. On no!
Brendan Eich (the father of JavaScript) designed it in 10 days, and then for marketing purposes, Java was a big name at the time, with the prefix Java, great JavaScript was born. Since then, Java and javascript have become lifelong friends. Haha… Any programming language has to solve a problem: Share/reuse, say important things 3 times, (share/reuse) x 3, same business logic, same state, behavior javascript Are you going to let me copy 3 times? Of course not. So do we borrow classes, modules, packages from other languages? Perhaps after the author’s balance, the idea of genius, prototype, thus emerged
Two key roles in prototyping?
From a language design perspective, what does [[Prototype]] or Proto solve? What is prototype and what is it mainly used for? Call Brendan Eich on this one and have a good chat. What? I can’t remember the number. Well, we understand it from implementation:
- Or [[Prototype]]proto: refers to the instance, refers to the prototype of the instance, is responsible for handling
Pointing to the relationship between
. - Prototype: an attribute on the constructor, as opposed to the constructor
Share/reuse
. The constructor’s prototype is calledA prototype object
.
What is the prototype of the instance? What is the prototype object of the constructor? It is helpful to ask questions in this way
// Specify shared attributes/methods
const customPrototype = { id: 0.refer: true.action: function(){}}function Ancestor(){}
// Specify the prototype object for the constructor
Ancestor.prototype = customPrototype
const a = new Ancestor()
const b = new Ancestor()
// Let's see what happens
Object.getPrototypeOf(a) === a.__proto__ // true
Object.getPrototypeOf(a) === customPrototype // true
a.id === 0 // true
a.id === b.id // true
b.action === a.action // true
Copy the code
To reduce the cost of understanding, the object.getProtoTypeof () method returns the Prototype of the specified Object (the value of the internal [[Prototype]] property). Format like [[Prototype]], surrounded by two brackets, indicates that it is internal to the browser.
Analyze the code
a.id === 0; a.id === b.id
: The attribute is 0 and shares the same attributeb.action === a.action
: points to the same function and shares the same methodObject.getPrototypeOf(a) === customPrototype
Example A’s prototype customPrototype is equivalent to the constructor’s prototype object
When it comes to prototypes, there should be a relative concept. Make it clear that [[Prototype]] or Proto deals with relationships. The constructor’s Prototype handles sharing/reuse
What are the ways to specify a prototype?
// Set the constructor's prototype
const customPrototype = { id: 0.refer: true }
function Ancestor(){}
Ancestor.prototype = customPrototype
const a = new Ancestor()
// Method 2: object.create
const b = Object.create(customPrototype)
// Mode 3: setPrototypeOf, will cause performance problems, interested can click the text at the end of the reference link
const c = Object.setPrototypeOf({},customPrototype)
// 方式四:重新赋值 __prototype
const d = {}
d.__proto__ = customPrototype
// Let's see what happens
Object.getPrototypeOf(a) === Object.getPrototypeOf(b) // true
Object.getPrototypeOf(b) === Object.getPrototypeOf(c) // true
Object.getPrototypeOf(c) === Object.getPrototypeOf(d) // true
Copy the code
One thing to note: Proto is not an EMCA specification and is a non-standard property implemented by the browser as the host environment, which is not necessarily provided on IE. [[Prototype]], in the browser does not support access, code can only use proto for assignment, to facilitate understanding proto and [[Prototype]] can be equivalent, not too much difference.
What happens when you change a prototype at runtime?
const customPrototype = { id: 0.refer: true }
const anothePrototype = { id: 1.refer: false }
function Ancestor(){}
Ancestor.prototype = customPrototype
const a = new Ancestor()
// Change the constructor prototype at runtime
Ancestor.prototype = anothePrototype
const b = new Ancestor()
Object.getPrototypeOf(a) === Object.getPrototypeOf(b) // false
Object.getPrototypeOf(a) === customPrototype // true
Object.getPrototypeOf(b) === anothePrototype // true
// Change it again
Object.setPrototypeOf(b,customPrototype)
Object.getPrototypeOf(a) === Object.getPrototypeOf(b) // true
Copy the code
As you can see from the above code, the prototype can be respecified at run time, and either the B instance or the constructor Ancestor can be specified dynamically. It’s just called in the new Ancestor(), using customPrototype as the prototype for instance B.
Prototype = anothePrototype; Ancestor. Prototype = anothePrototype; Ancestor. Prototype = anothePrototype; The generated instance link object becomes anothePrototype.
How does the prototype chain come about?
The Prototype of the instance is linked through [[Prototype]], which can be thought of as a linked list, with one node pointing to the last until the end. Since we already know that the prototype of an instance can be changed at run time, the prototype chain is also dynamic.
const customPrototype = { level: 0 }
function Ancestor(){}
Ancestor.prototype = customPrototype
const a = new Ancestor()
function A1() {}
A1.prototype = a
a1 = new A1()
// Test which prototype to point to
a1.__proto__ === a // true
a1.__proto__.__proto__ === customPrototype // true
// Change the prototype to which the instance points
Object.setPrototypeOf(a1,customPrototype)
a1.__proto__ === a // false
a1.__proto__ === customPrototype // true
// Iterate through the prototype chain
function prototypeChain(instance) {
// There is no prototype
if(instance === void 0 || instance === null) {
return
}
let prototype = Object.getPrototypeOf(instance)
do {
console.log(prototype)
prototype = Object.getPrototypeOf(prototype)
} while(prototype)
console.log(prototype)
}
prototypeChain(a1)
Copy the code
An assembly line of prototypes?
What happens to the new call constructor? What is the original intention of constructor design? To understand the original design API of the design language, look at Buildint, what’s the initialization like?
Where and what kind of work does constructor mainly handle?
function Ancestor(){}
const obj = new Ancestor()
// a property of the prototype constructor object
Ancestor.prototype.constructor === obj.constructor // true
Ancestor.prototype.constructor === Ancestor // true
Object.getOwnPropertyDescriptor(obj,'constructor') = = =undefined // true
Copy the code
You can see from these three equations
- Constructor: an attribute of the prototype object
- Constructor: Points to the constructor itself
- Constructor, derived by inheritance, accesses the constructor prototype object. Constructor
Constructor is essentially used to indicate which constructor is called to generate the instance
What kind of work does it do?
function Ancestor(){
console.log('Ancestor')}function Descendant(){
console.log('Descendant')
}
Ancestor.prototype = { id: 0 }
Descendant.prototype = { id: 1 }
Ancestor.prototype.constructor = Descendant
const obj = new Ancestor()
obj.constructor === Descendant // true
obj.id === 1 // false
obj.id === 0 // true
Copy the code
- Descendant not call: Ancestor prototype object. The prototype. The constructor is used to mark
- Obj. id === 0: indicates that the prototype of the instance is method.prototype
- (2) constructor === derive from the constructor of an instance
For now, the constructor tag is used to indicate the type of the instance.
function Ancestor(){}
Ancestor.prototype.constructor = 'mark type'
const obj = new Ancestor()
obj.constructor === 'mark type' // true
Copy the code
At this point, it is clear that constructor can be used as a string, not necessarily as a function. Basically, it marks the type of the instance that was generated by which constructor. [b]. [C]. [D].
What happened to New?
New calling the constructor in this way does the following:
Create a new object in memory. (2) The [[Prototype]] property inside this new object is assigned to the constructor’s Prototype property. (3) This inside the constructor is assigned to the new object (this refers to the new object). Execute the code inside the constructor (add attributes to the new object). (5) If the constructor returns a non-empty object, return that object; Otherwise, the newly created object is returned
Is the process necessarily like this? Is it affected by other factors? With these questions in mind, we verify them with code.
function Ancestor(){}
function Descendant() {
// Return an object
return { id: 1}}// Case 2: The prototype object is a basic data type: string, number, undefined, null
Ancestor.prototype = null
Descendant.prototype = { id: 0 }
const a = new Ancestor()
const b = new Descendant()
// Test the equation
Object.getPrototypeOf(a) === null // false
Object.getPrototypeOf(a) === Object.prototype // true
b.id === 0 // false
b.id === 1 // true
Copy the code
There are two details to note when calling a constructor with the new operator
-
[[Prototype]] (string,number, null, undefined, Boolean,Symbol) will not be linked. [[Prototype]] Sets the value to object.prototype
-
The return value of the constructor is also dependent. If it returns a reference data type, step (3) this is discarded and the return value is the instance value. No value is returned or a primitive data type is returned, so this is returned.
How do I detect prototype relationships?
The prototype chain can be understood as a linked list, and the search task is to recursively search up node by node: 1. 2. The last node is found and exits.
To determine if an instance was generated by a constructor, you can use instanceof, which checks if the constructor’s prototype property appears on the prototype chain of an instance object.
function Ancestor(){}
let a = new Ancestor()
/ / equation 1
a instanceof Ancestor === true // true
// Equation 2 is true: the prototype chain of instance A finds the prototype of the constructor Ancestor
Object.getPrototypeOf(a) === Ancestor.prototype // true
Ancestor.prototype = new Array()
a = new Ancestor()
a instanceof Ancestor // true
/ / equation 2
a instanceof Array // true
// Equation two is true: the prototype chain of instance A finds the prototype object of the constructor Array
a.__proto__.__proto__ === Array.prototype // true
function Descendant() {
return { id: 1}}const b = new Descendant()
// The equation is not true
The Descendant constructor returns {id: 1}, such that the Descendant. Prototype prototype object is not linked
b instanceof Descendant // false
// Instanceof operator restrictions
// Wrap the object
Object.getPrototypeOf(3) = = =Number.prototype // true
3 instanceof Number // false
// Case 2: Instanceof and multiple global objects (e.g. interaction between multiple frames or multiple Windows)
// There are still some instanceof cases
Copy the code
Built-in object prototype diagram?
What objects will be built into the browser? Object is very common, Array and Date may also be used a lot. Function may not be around that much, but it’s often seen in frameworks. Function is the core of the built-in object, so let’s see what’s special about Function.
ƒ () {[native code]}
// Yes, it is a function, that's all
typeof Function.prototype === 'function' // true
// The prototype Object is an Object
typeof Object.prototype === 'object' // true
// As an example, what is the prototype of Function?
// The prototype of a Function is its own prototype object
Object.getPrototypeOf(Function) = = =Function.prototype // true
Copy the code
Prototype: Function. Prototype: Prototype
// Function.prototype 的 [[Prototype]] 是 Object.prototype
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Like other constructors, point to itself
Function.prototype.constructor === Function // true
Copy the code
Function. Prototype is Object. Prototype. This time you can view the specification, the official link
The Function prototype object is itself a Function object (its [[Class]] is “Function”) that, when invoked, accepts any arguments and returns undefined.
The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object Prototype Object (15.2.4). The Initial value of The [[Extensible]] internal property of The Function prototype object is true.
The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.
The length property of the Function prototype object is 0.
A Function prototype object is itself a Function object (its [[Class]] is “Function”, which is callable) that, when called, takes any argument and returns undefined.
The [[Prototype]] internal properties are the standard built-in objects. Prototype, the [[Extensible]] internal properties of Function Prototype objects start with true.
The Function prototype object does not have its own valueOf attribute; However, it inherits the valueOf attribute from Object.prototype. It should be noted that valueOf is a function, which is better translated as valueOf method.
The length attribute of the Function prototype object is 0.
Based on the above, we make a summary:
- The prototype object is a Function; As a function call, it takes any argument and returns undefined
- Function prototype: refers to function. prototype, which is where the prototype chain appears
ring
The key to, is often saidAround the
- Prototype: Function. Proto. Proto === Object
Function and Object ring relation
// The prototype of Function is the prototype object of Function
Object.getPrototypeOf(Function) = = =Function.prototype // true
// The prototype of Object is Function prototype Object
Object.getPrototypeOf(Object) = = =Function.prototype // true
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Object.getPrototypeOf(Object.prototype) === null // true
Copy the code
In terms of examples, what are we talking about prototypes? From the constructor point of view, we are talking about the prototype object, so that there is no loop. From a language design point of view, they’re all there to solve some kind of problem. Let’s sort it out again. Why is it designed this way
-
Function. Prototype: Can be called more as a Function, as the ancestor of all functions, the most initial state.
-
Prototype: more as the original common property and method provider of the Object, which itself carries the responsibility of sharing/reuse
-
Null: Is the prototype of all, the purest, without any methods, attributes.
-
[[Prototype]] Object is a Function. [[Prototype]] is a Function
-
Prototype chain must have a node that points [[Prototype]] to Object. Prototype
-
What happens if function. proto points to null? Function has no way of owning the representation of the object, so it cannot be called a callable object. Object. Prototype and Function. Prototype are separated at first. To achieve this effect, there must be a crossover point. In this way, function. proto points directly to the body prototype, which is feasible and perfect. You can also add a node that points to Object. Prototype can also be added, but this is very verbose.
Function. Prototype or Object. Prototype? In fact, this problem does not exist, from the top. [[Prototype]] [[Prototype]] What comes first, what comes after, there is time itself this setting condition, time 24h itself is created by human beings, is a virtual concept. It’s like the chicken or the egg. Chickens and eggs, essentially matter, have nothing to do with time. If biotechnology were developed enough to produce a chicken and an egg separately, would you ask which came first?
conclusion
The above is some personal understanding of the prototype, if there are mistakes, welcome to point out correction. From the point of view of language design, more from the original intention of API design, use point of view, hope not to cause misunderstanding, just some personal guesses.
Participate in data
-
Object.setPrototypeOf
-
Object.getPrototypeOf
-
instanceof
-
New operator