Take up the basics of object-oriented knowledge – prototype and prototype chain basics
What is a prototype and how does the prototype chain mechanism work
Let’s talk about the concept from two perspectives
fromFunction data typeperspective
Most function data types have values that have the Prototype property, which is itself an object. By default, browsers create a heap of memory for it to store common properties and methods that can be called by instances of the current class. In the browser’s default heap (prototype object), there is a default property constructor, whose value is the current function/class itself.
Function data type classification
- Normal functions (real-name or anonymous functions)
- Arrow function
- Constructor/Class “Built-in class/Custom class”
- Generator function
Generator
- .
Do not haveprototype
The function of
-
Arrow function const fn=()=>{}
-
Es6-based quick operation to assign a function value to a member of an object
let obj = { fn1: function () { // The normal method has the prototype attribute }, fn2() { // The shortcut does not have the prototype attribute}};class Fn { fn() {} // This has no prototype property }; Copy the code
fromObject data type valueperspective
The value of each object data type has an attribute __proto__ (stereotype chain/implicit stereotype) that points to the prototype of its own class.
Ps: Forget function type objects here
Object data type Value classification
- Ordinary objects
- Special objects: array, re, date,
Math
,Error
. - The function object
- Instance objects
- Constructor. prototype
The bottom line of object orientation is based on two perspectives: classes (constructors) and instance objects
conclusion
Remember these two most important concepts:
- Most of theFunction data typeHas the values of
prototype
(Stereotype/explicit stereotype) property, the value of which is itself an object. By default, browsers create a heap of memory for it to store common properties and methods that can be called by instances of the current class. There is a default property in the heap memory (the prototype object) that the browser opens by defaultconstructor
Constructor/constructor, the property value is the current function/class itself - eachObject data typeEach of the values of
__proto__
(Prototype chain/implicit prototype), the property value points toThe stereotype of the class to which it belongsprototype
.
Understand concepts by example
Use a built-in class (Array and examples) to understand the above concepts
Function data type perspective
We know that any function exists in memory: scope, code string, attribute key-value pair, so let’s look at Array
-
scope
Because it is built-in, the scope is not clear, but it is scoped
-
Code string
[native code] is a string of code, probably written for C++
-
Each function of the propToType property in the property key-value pair has a propToType by nature, so it is also present in Array
And the PropToType object has a constructor property pointing to the function itself, and contains many of the Array’s common properties (concat,forEach, etc.), as shown:
Conclusion:
The same goes for the Object function
Object data type perspective
Ps: Function is also a pair of objects, about the function of the various roles of the problem is not discussed here for the time being, later write an article to supplement
Instance objects, prototype objects, and function objects are all objects. All objects have a __proto__ attribute that points to the prototype object of their class.
The pointing relationship is as follows
Watch out for confusing points:
The stereotype object is not an instance of the constructor to which it belongs, for exampleArray.prototype
notArray
An instance of. Since, as a stereotype, it is the object that all instances share properties and methods with, it cannot be an instance of one of them. onlynew
The resulting constructor is an instance.Array
The prototype object is created by default in the browser, not hereArray
Constructor instance.
__proto__ points to Object. proptoType, so array. prototype is an instance of Object
Note the more special object.prototype. __proto__, pointing to null. Prototype is itself an Object. Object.prototype.__proto__ refers to itself, object.prototype. that doesn’t make sense. That makes more sense.
So after the definition of the prototype chain mechanism above is clear, how does it usually work? For example:
Execute arr.length to see the length of the arr property, or execute arr.push, or execute arr.hasOwnProperty:
- First access their own private attributes, if private attributes exist, then directly use.
arr.length
That’s it. - If the accessed member is not in the private property, the default is based on
__proto__
To find the owning classprototype
Properties/methods on.arr.push
That’s it. - Private no,
Array.prototype
So it’s going to be based onArray.prototype.__proto__
Keep looking up.arr.hasOwnProperty
That’s it. FinallyObject.prototype
To find the method. - If you can’t find it again, return
undefined
.
We call this member access lookup mechanism the prototype chain mechanism
The diagram below:
These are the most common member access mechanisms, but there are a few other ways to access members:
For example, arr.foreach (), in addition to being used directly, can also be:
- You can use it directly
arr.__proto__.forEach()
This allows you to skip the private property search and use the method on the prototype object. (This method is usually not handled manually because Internet Explorer cannot use this method. Internet Explorer has protected it from access.) - Can also be
Array.prototype.forEach()
Or you can use the methods directly on the prototype object
So what’s the difference between these methods? ForEach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach = array.prototype.foreach Who called the method, whatever came before the dot, this is whatever. This is arr,arr__proto__, and Array. prototype
Here’s an example:
let arr = [10.20.30];
console.log(arr.hasOwnProperty('forEach')); //->false
Copy the code
In a nutshell, the above code verifies that forEach is a private property of an ARR object
So the whole execution process is actually:
Arr in accordance with the prototype chain lookup mechanism, is found in the Object. The prototype. The hasOwnProperty method “@”, and to find @ A execution, note:
- At sign A method
this
It should bearr
“The object we want to operate on” - The argument passed should be
'forEach'
The property we want to validate, the @A method,Verify that the argument is currentthis
A private property of, then the final execution mode of the same effect is: ===>Object.prototype.hasOwnProperty.call(arr,'forEach')
The “opposite” of public and private properties
Another point to note: Public and private properties are “opposite”.
For example, in contrast to the arR instance object, the array. prototype push and other methods are public attributes of each ARR instance. Array.prototype is an object itself, and push is its own private method, so whether the properties on the object are public or private depends on the relative. A public property is relative to an instance of the class, and a private property is relative to itself
So there is no strict concept of public and private attributes, but only relatively speaking, when different roles have different functions, and finally the distinction between public and private.
When writing code for a String interception, for example, we can check string. prototype to see if there are any public methods that can be called, and then follow __proto__ to find public methods. Any method that appears on the prototype chain can be used.
For example, we go all the way up to the document.body instance method and find that it has event-related methods
So we can use the document. The body. The addEventListener, such as dom manipulation have again classList method, used to operate the class
You can find ways to do it
Therefore, the whole JS is based on the idea of object-oriented construction.
Examples of deeper understanding
function Fn() {
this.x = 100;
this.y = 200;
this.getX = function () {
console.log(this.x);
};
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.prototype.getY = function () {
console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);//false
console.log(f1.getY === f2.getY);//true
console.log(f1.__proto__.getY === Fn.prototype.getY);//true
console.log(f1.__proto__.getX === f2.getX);//false
console.log(f1.getX === Fn.prototype.getX);//false
console.log(f1.constructor);//Fn
console.log(Fn.prototype.__proto__.constructor);//Object
f1.getX();/ / 100
f1.__proto__.getX();//undefined
f2.getY();/ / 200
Fn.prototype.getY();//undefined
Copy the code
Note: New Fn() has the same effect as new Fn, except that the former has a priority of 20 and can pass parameters, while the latter has a priority of 19 and does not pass parameters
Analysis:
F1 and F2 are both as follows. The trick is that they both have a getX function on their private property and their prototype. The two private properties have different addresses and return false, while the prototype is the same object where the getX function is the same address
__proto__ returns the prototype
Rewrite the built-innew
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function () {
console.log('wangwang');
}
Dog.prototype.sayName = function () {
console.log('my name is ' + this.name);
}
/* let sanmao = new Dog(' sanmao '); sanmao.sayName(); sanmao.bark(); * /
function _new() {
Finish your code
}
let sanmao = _new(Dog, 'three hairs');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=> < span style = "max-width: 100%; clear: both;
console.log(sanmao instanceof Dog); //=>true
Copy the code
What does the new keyword do:
- Create an instance object of Ctor (Create an instance object and place the instance object’s
__proto__
Point to theCtor.prototype
) - Execute the constructor as if it were a normal function and let the
this
Point to instance object - Verify the return value of the method execution. If no value is returned or the original value is returned, let it return the instance object by default.
Note:
-
Ctor -> constructor abbreviation
-
Params -> all argument information passed to Ctor later
function _new(Ctor, ... params) {
// 1. Create an instance of Ctor
/ / instance. __proto__ = = = Ctor. Prototype
let obj = {};// Only one instance of Object is created here
obj.__proto__ = Ctor.prototype;
// 2. Execute the constructor as a normal function "let THIS-> instance object in the method"
letresult = Ctor.call(obj, ... params);// 3. Verify the return value of the method execution. "If there is no return value or the original value is returned, let it default to return the instance object."
if(result ! = =null && /^(object|function)$/.test(typeof result)) return result;
return obj;
}
Copy the code
Create an instance object of Ctor if used
let obj = {};// Only one instance of Object is created here
obj.__proto__ = Ctor.prototype;
Copy the code
That’s not a good one, so here’s a new one
Object.create()
Object.create([obj]) : Creates an empty Object and makes the empty Object. __proto__ point to obj.
Official explanation: Use OBJ as the prototype for the new instance object.
Another interpretation: Create a new object based on OBj
Note:
- parameter
obj
It could be an object or it could benull
, but it cannot be any other value Object.create(null)
Create one that does not have__proto__
Property (not an instance of any class)
rewriteObject.create()
Object._create = function create(prototype) {
if(prototype ! = =null && typeofprototype ! = ="object") throw new TypeError('Object prototype may only be an Object or null');
var Proxy = function Proxy() {}
Proxy.prototype = prototype;
return new Proxy;
};
Copy the code
Effect:
Note that __proto__ cannot be eliminated when null is passed to _create
__proto__ is unerasable, cannot be deleted, and refers directly to Object
Built-in browser:
Allows the use ofObject.create()
When rewritingnew
Use object.create () instead of rewriting the first step of new
Pre-declare variables, be more formal, and pay attention to some validation rules
function _new(Ctor, ... params) {
let obj,
result,
proto = Ctor.prototype,
ct = typeof Ctor;
// Check rules for the constructor
// The premise cannot be Symbol or BigInt, then Ctor is not a function or proto does not exist (arrow function) throws a type error
if (Ctor === Symbol || Ctor === BigInt|| ct ! = ='function'| |! proto)throw new TypeError(`${Ctor}is not a constuctor! `);
// Follow the logic
/ / 1.
obj = Object.create(Ctor.prototype);
/ / 2.result = Ctor.call(obj, ... params);/ / 3.
if(result ! = =null && /^(object|function)$/.test(typeof result)) return result;
return obj;
}
Copy the code
Prototype redirection
__proto__=== object.prototype
Arrays, for example, are not pure objects because [].__proto__=== array. prototype and array.prototype. __proto__=== object.prototype
Examples understand prototype redirection
function fun() {
this.a = 0;
this.b = function () {
alert(this.a);
}
}
fun.prototype = {
b: function () {
this.a = 20;
alert(this.a);
},
c: function () {
this.a = 30;
alert(this.a)
}
}
var my_fun = new fun();
my_fun.b();
my_fun.c();
Copy the code
The simpler example above prints 0,30
fun.prototype = {… } This operation is called prototype redirection and after redirection the original constructor prototype is released by the browser when idle.
Application scenario: Stereotype redirection can be used to bulk extend properties/methods to the stereotype of the constructor
let proto = fun.prototype;
proto.b = function () {};
// ...
proto.c = function () {};
Copy the code
We can also extend properties and methods to the prototype object one by one, but there are some disadvantages:
- Trouble:
fun.prototype
It’s a little tricky to operate (you can use a nickname) - Out-of-focus: Code that extends methods to prototypes may be spread out, which is not maintainable
Redirection solves these problems, with a little help from the singleton idea
fun.prototype = {
b: function () {},
c: function () {}};Copy the code
This can also be problematic, as the prototype object created by the original browser may be released after the redirect. This results in properties and methods on the original prototype object that may be cleared (including constructor)
Solutions:
- If we know that there are no properties and methods on the original prototype object other than Constructor, we simply set Constructor manually on the redirected prototype object ourselves
- If we are sure or not sure whether the remaining properties and methods exist on the original object, we copy the contents of the original prototype object and reassign them to the new prototype object (involving the merging of the two objects).
useObject.assign
Extend the prototype
Object.assign([obj1],[obj2],…) Note: instead of returning a completely new merged object, the value returned is the same heap of obj1, just merging the contents of obj2 into obj1
let obj1 = {
x: 10.y: 20.getX: function () {}};let obj2 = {
x: 100.getY: function () {}};console.log(Object.assign(obj1, obj2));
console.log(Object.assign(obj1, obj2)===obj1);
console.log(Object.assign({}, obj1, obj2))
console.log(Object.assign({}, obj1, obj2)===obj1)
Copy the code
function fun() {}
fun.prototype.x = 100;
fun.prototype.getX = function () {};
fun.prototype = Object.assign(fun.prototype, {
b: function () {},
c: function () {}});Copy the code
This does not redirect prototype pointing and extends fun.Prototype
Built-in class prototype extension
In order to avoid the built-in property methods being overridden, the built-in class prototype does not allow redirection. Array.prototype = {} does not return an error, but has no effect
Redirects are not allowed, but some built-in methods on built-in prototypes can be overridden singly. For example array.prototype. push = function (){console.log(‘ haha ‘)} would override push
Although the prototype of built-in class provides many properties and methods for example fetching, it may not fully meet our needs. At this time, we need to extend methods to the prototype of built-in class by ourselves
- Benefits:
- Easy to use.”
Instance. Method
“; 4 - You can implement chained writing “core: if the return value of a function execution is an instance of a class, you can directly continue to call other methods on that class prototype”;
- Easy to use.”
- Disadvantages: Self-written methods tend to overwrite built-in methods “so it is best to set the prefix for the name, such as: myXxx”
toArray.prototype
To expand the array to remove the duplicate method
Array.prototype.unique = function unique() {
// this -> arr is usually the current instance of the operation "no need to pass the array to be processed, because this stores the value"
return Array.from(new Set(this));
};
let arr = [1.2.3.4.2.3.4.5.6.2.3.4.5.6.7.2.2.3.4.6.7];
console.log(arr.unique());
let result = arr.unique()
console.log(result);
console.log(arr); // The original array is unchanged
Copy the code
You can also do chain calls
let result = arr.unique().sort((a, b) = > b - a);
console.log(result);
Copy the code
Where this is usually the instance being operated on, there is no need to pass the array to be processed, because the value this stores is the instance’s custom method passed into the ARR
const unique = function unique(arr) {
return Array.from(new Set(arr));
};
console.log(unique(arr));
Copy the code
toNumber.prototype
Extension methods
(function (proto) {
const verification = function verification(num) {
num = +num;// Convert it to a numeric type
return isNaN(num) ? 0 : num;// Determine if it is NaN
};
const plus = function plus(num) {
// This -> the number to operate on {object type}
let self = this;
num = verification(num);
return self + num;
};
const minus = function minus(num) {
// This -> the number to operate on {object type}
let self = this;
num = verification(num);
returnself - num; }; proto.plus = plus; proto.minus = minus; }) (Number.prototype);
let n = 10;
let m = n.plus(10).minus(5);
console.log(m); / / = > 15 (10 + 10-5)
Copy the code
Let m = n.plus(10).minus(5); The browser will ‘box’ the original value of type Number into an object value, and then call the method on the prototype