At the end of last article, we mentioned that hasOwnProperty is used to check whether a property is private to the current instance. We also wrote hasPubProperty to check whether a property is public to the current instance. Private methods, described above, are methods that are private to the instance itself and exist in the current instance; So what are public methods, and where are they? That’s the prototype and the prototype chain that we’re going to talk about today.

Let’s take arrays as an example:

Each Array is an instance of the built-in Array class Array;

let arr1 = [10.20];
let arr2 = [30.40];

console.log(arr1 instanceof Array); //=>true
console.log(arr1.hasOwnProperty('push')); //=>false
console.log(arr1.push === arr2.push); //=>true
arr1.push(100); PUSH is a property of ARR1 and is a public property (common methods in other arrays are also public properties of array instances).
Copy the code

Where are the methods of push and so on?

Let’s remember three sentences: very important, very important, very important

  • 1, Every function is born with a property: prototype, whose property value is an object (the browser assigns a heap to it by default)

    • The properties and methods stored on the prototype object are the public properties and methods that are called by the current class instance.
  • A class is called a constructor because its constructor value is the current class itself

  • 3. Every object is born with a __proto__ attribute whose value is the prototype of the class to which the current instance belongs

Mind mapping

One, foreword

Again, take the Array built-in class

Classes (custom or JS built-in) are function data types

We can open the console output:

1. Since each is a reference data type, a heap memory is created for storing code strings. As shown in figure

Array is a built-in class, so the stored code is native code. In order to protect the native code, the browser does not allow us to view the specific content, so the output is [native code]. If it is a custom class created by ourselves, it can be seen.

2. At this point we create two arrays

Let’s remember one thing here

All instances are object datatype values (with the real exception of base datatype values)

let arr1 = [10, 20];
let arr2 = [30, 40];
Copy the code

Array arR1 0:10, 1:20, length:2 are private attributes of the arR1 instance; Contents in ARR1 and ARR2 do not conflict (for reasons explained in singleton mode);

For each instance object, the properties stored in its heap memory are private

Either ARR1 or ARR2 can call array push… And so on method, but these methods are not private in, then how to come out of these methods?

Prototype and prototype chain

Here we use the above three sentences:

1. There is one on every functionprototypeProperties; An attribute value is an object

Every function (including 👇) is born with a property: prototype, whose value is an object (the browser allocates a heap for it by default)

  • Common function
  • Classes are also values of function types

The properties and methods stored on the prototype object are the public properties and methods that are invoked by the current class instance

2, inprototypeIn the prototype object, one exists by defaultconstructorProperty, the value of which is the current function itself

The prototype object of a class has a built-in property called constructor by default. The value of the property is the current class itself, so we call a class a constructor

dir(Array)
prototype
constructor
Array

3. Each object has one__proto__Property whose value is of the class to which the current instance belongsprototypeThe prototype

Every object is born with a __proto__ attribute, whose value is the prototype of the class to which the current instance belongs

  • Object data type value
    • Ordinary objects
    • The array object
    • Regular object
    • .
  • Instances are also object type values (except base values)
  • Of the classprototypeStereotype property values are also objects
  • Functions also have the characteristics of objects (one of their identities is the object type)
  • .

Now we print:

  • 1.arr1.lengthorarr1[0] ;
    • => gets the private property value of the current instance;
  • 2,arr1.push() ;
    • First find their own private property, private property has, call is private property
    • If not, the default is based on__proto__Prototype chain property, find the owning classprototypeCommon properties and methods on the stereotype
    • (This lookup mechanism is called prototype chain lookup)
  • 3,arr1.push() === arr2.push();
    • findarr1.pushIn the findarr2.push
    • It’s all the same method on the prototype, it’s all pointing to the same spatial address, so zerotrue;

So we say that the stereotype holds the common properties and methods of the instance;

Three, prototype chain search

  • 4,arr1.proto.push
    • Go straight to the class stereotypepushMethod, something like thatArrary.prototype.pushSo find
    • arr1.__proto__.push === arr2.push === Array.prototype.push

Arr1.__proto__=== array. prototype

Whose instance is the array. prototype object?

  • 1. Definitely notArrayHe isArrayArray is the prototypeArrayAn instance of the
  • He is himself an object;

All Object datatype values are instances of the built-in Object class

The prototype of Object is also an Object, each Object has a __proto__ attribute pointing to the prototype of the current owning class, and all Object data types are instances of Object;

We find that he finally turns on himself; Object. Prototype. __proto__ === null

Object is the base class of all objects. The __proto__ attribute on its prototype, if present, points to its own prototype, which makes no sense, so its __proto__ attribute value is null

The entire prototype chain for ARR1(array instance object) :

  • Look for the private ones first. No private ones
  • Based on the__proto__Find the stereotype of the owning classArray.prototype; If you haven’t already
  • Based on theArray.prototypethe__proto__findObject.prototype; If you haven’t already
  • It’s gone

This series is our prototype chain search;

Prototype chain lookup mechanism: find the prototype of the class based on the instance’s __proto__

  • => Private property method of the instance
  • => Public properties and methods of the instance

This lookup mechanism helps us realize that instances have both private and public properties and methods

This is also at the heart of the whole object orientation.

We can open the console and print dir[10,20]):

  • When we usearr1.hasOwnPropertywhen
    • Actually find =>arr1.__proto__.__proto__ => Object.prototype
  • arr1.hasOwnProperty("push") => false
  • Array.prototype.hasOwnProperty("push") => true

Push is the public method of an arR1 instance; But array. prototype is private

HasOwnProperty is the “public property method” for arR1 instances

  • Object’s private properties: stored in their own heap, not based on__proto__Look it up
  • Object public property: does not exist in its own heap and needs to be based on__proto__Looking for aprototypeOn the

Prototype.js all values, finally based on the __proto__ prototype chain, can find Object. Prototype, which is the instance of the Object class, which is the Object.

Four, an example

As always, let’s do a problem

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);
console.log(f1.getY === f2.getY);
console.log(f1.__proto__.getY === Fn.prototype.getY);
console.log(f1.__proto__.getX === f2.getX);
console.log(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX();
f1.__proto__.getX();
f2.getY();
Fn.prototype.getY();
Copy the code

There’s no better way to do a prototype problem than by drawing

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); //=> The constructor of an Fn instance usually refers to the class to which it belongs
console.log(Fn.prototype.__proto__.constructor); //=> Objectf1.getX() ; Private getX =>function () {console.log(this.x); } methodthis=> the f1 code is executedconsole.log(this.x);  => f1.x  => 100f1.__proto__.getX() ; Execute public getX => on the prototypefunction () {console.log(this.x); }; In the methodthis=> f1.__proto__ Code executionconsole.log(this.x);  => f1.__proto__.x => undefinedf2.getY() ; Execute public getY on prototype =>function () {console.log(this.y); }; In the methodthis=> F2 code executionconsole.log(this.y)  => f2.y =>200Fn.prototype.getY() ; Execute getY on prototype =>function () {console.log(this.y); }; In the methodthis=> fn. prototype code executesconsole.log(this.y)  => Fn.prototype.y => undefined
Copy the code

Extend properties or methods on the prototype of a class

(Public properties and methods for instantiation)

1.Fn.prototype.xxx = xxx(common)

  • Add attribute methods to the default heap memory
  • Disadvantages: If you need to set up a lot of property methods, the operation of the more troublesome (small tips, toFn.prototypeSet alias)
  • These methods are characterized by default heap extension property methods, default heap memory existsconstructorThis attribute
let prop = Fn.prototype;
    prop.A = 100;
    prop.B = 200;
    prop.C = 300;
Copy the code

2,Object.prototype.xxx = xxx(not often used)

  • Extend methods on built-in class stereotypes

3,f1.__proto__.xxx = xxx(Hardly)

  • That’s fine because it’s instance-based__proto__You find the stereotype of the class you belong to, which is equivalent to extending property methods on the stereotype
  • Weakness: only this way we basically do not use, becauseIEIn the browser, in order to prevent malicious tampering of the prototype chain, we are forbidden to operate ourselves__proto__Property of (IE not allowed to use__proto__)

Prototype redirectionFn.prototype = {... }(common)

  • We manually create a heap of memory assigned toFn.prototype
  • Disadvantage 1: there is no heap memory in your own heapconstructorOf this property; So in real projects, in order to protect the rigor of the structure, we need to set it up manuallyconstructor
  • Disadvantage 2: If we set some property methods in the default prototype heap before the redirect, the property methods are lost after the redirect.
  • Solution: Use merge objectsObject.assign(original Object, new Object)
    • In the process of merging, the conflicts are mainly new ones, and the remaining ones that do not conflict are merged together
    • Return a new merged object for redirection, we are looking at a problem 👇
function Fn(num) {
    this.x = this.y = num;
}
Fn.prototype = {
    x: 20.sum: function () {
        console.log(this.x + this.y); }};let f = new Fn(10);
console.log(f.sum === Fn.prototype.sum); //true
f.sum();/ / 20
Fn.prototype.sum();//NaN
console.log(f.constructor);//Object
Copy the code

Extend properties and methods on the prototype of the built-in class

There are a lot of built-in classes in JS, and there are a lot of built-in attributes and methods on the prototype of the built-in class. Although there are a lot of methods on the prototype of the built-in class, it is not necessarily fully enough for project development, so in real projects, we need to extend the method to the built-in class prototype to achieve more functional operations

1.Array.prototype.xxx = xxx(Take arrays as an example)

  • Disadvantages: There is a risk that our own property names will overwrite the built-in properties

So we generally extend our own methods on the built-in class prototype, set the property name to do a good prefix

The browser does not allow us to redirect the built-in class prototype to protect methods on the built-in class prototype (strict mode causes an error).

2. Practice examples

Requirement 1: Emulate the built-in PUSH method

  • A method written on a prototype of a class that lets the method execute, we typically do this:Instance.method (), so in the methodTHISIt’s generally the case that we’re going to operate on, which we’re based onTHISAn operation is an operation on this instance
  • Implementation approach
/* * array.prototype: Array => arr.push() : /* * array.prototype: Array => arr.push() : Arr uses the __proto__ prototype chain to find the array. prototype push method and execute the push method. The push method performs an array instance that appends a new value to the end of arR (THIS) * returns the length of the new array * * extends the method to the built-in class prototype: * array.prototype. XXX = XXX * => Prototype ={array.prototype ={array.prototype ={... } * => The browser, in order to protect the methods on the built-in class stereotype, does not allow us to redirect the built-in class stereotype (in strict mode an error is reported) */

Array.prototype.myPush = function () {
	console.log('Own PUSH');
};
let arr = [10.20];
arr.myPush(100); 
Copy the code
  • Implementation code:
Array.prototype.myPush = function myPush(value) {
	// this: Array arR instance to operate on
	this[this.length] = value;
	return this.length;
};
let arr = [10.20];
console.log(arr.myPush(100), arr);

let arr2 = [];
console.log(arr2.myPush('Little Sesame'), arr2);
Copy the code

Requirement 2: The prototype of the array has a SORT method, but there is no method to implement the array to duplicate, we next extend the built-in prototype method myUnique, arr. MyUnique implementation can duplicate the array

Array.prototype.myUnique = function myUnique() {
	// this: the current array instance to operate on
	let obj = {};
	for (let i = 0; i < this.length; i++) {
		let item = this[i];
		if (typeofobj[item] ! = ="undefined") {
			this[i] = this[this.length - 1];
			this.length--;
			i--;
			continue;
		}
		obj[item] = item;
	}
	obj = null;

	// For the chaining method
	return this;
};

let arr = [12.23.13.23.12.12.2.3.1.2.3.2.1.2.3];
arr.myUnique().sort((a, b) = > a - b);
console.log(arr);
Copy the code

3, chain writing: execute the last method, then call the next method to execute

  • arrThe reason why we can callmyUniqueorsortAnd so on array prototype method, becausearrisArrayAn instance of the
  • So the idea of chaining is simple: just make the last method still return an instance of the current class, and you can immediately call the other methods on the class prototype
arr.myUnique().sort((a, b) = > a - b).map(item= > {
	return '@ @' + item;
}).push('Little Sesame').shift();
//Uncaught TypeError: arr.myUnique(...) .sort(...) .map(...) .push(...) .shift is not a function
//=> Because push returns the length of the new array, which is a number, it is no longer an array, so it cannot continue to call the array method
Copy the code

7. This question about constructors

Executed based on new, this in the constructor – function body is an instance of the current class

The private or public methods that extend the instance, depending on whether the “this” is preceded by the “dot”

Again, we did the above example