JavaScript prototype chain and inheritance

I Prototype chain mechanism

1. Essence of prototype chain

As long as it is an object, there must be a prototype object, that is, as long as it is an object, there must be a proto attribute. (wrong)

We think the prototype object of the instance is also an object, so we urgently want to see the prototype object of this prototype object If there is no problem with this theory, the prototype object should also have proto pointing to its prototype object

function People(){
    
}
var xiaoming = new People();
// Prototype object of prototype object
console.log(xiaoming.__proto__.__proto__);
// Who is the constructor of the prototype object
console.log(xiaoming.__proto__.__proto__.constructor); //Object

// Let's see if we can look up the prototype object
console.log(xiaoming.__proto__.__proto__.__proto__);
// The result is null

Through the above example, it is found that the first sentence is actually wrong

In the world of JS, there is only one object without prototype object, which is object prototype.

Now you can figure out something. An object is born with some properties and methods

such as

function People(){
    
}
var xiaoming = new People();
// Xiaobai can immediately call the toString() method
console.log(xiaoming.toString());
// Print "[object]"

// This method is the method on the prototype object of Xiaoming prototype object. We can have a look
console.log(xiaoming.__proto__.__proto__);

Object is a function and a built-in constructor of the system, which is used to create objects. Object.prototype is the end of the prototype chain of all objects.

Therefore, when we call a method on an object, the system will look for its definition along the prototype chain and always find the object prototype.

   function AA(){
     console.log('I'm a function');
   }
   var aa = new AA;
   console.log(aa.__proto__);   //{}
   console.log(aa.__proto__.__proto__);   //Last object in prototype chain {}
   console.log(aa.__proto__.__proto__.__proto__); //The prototype of the last object in the prototype chain is null
   console.log(aa.toString());//aa there is no such method. aa prototype has toString method [object Object] 
   console.log(aa.__proto__.__proto__.constructor);//Constructor for creating objects
   console.log(aa.__proto__.__proto__.constructor.prototype) //End of prototype chain for all objects

Object.prototype is the end of the prototype chain of all objects. Now I force an attribute to it

Object.prototype.sayHello = function(){
    alert("Hello")
}
// Now Xiao Ming can say hello
xiaoming.sayHello();

// Not only xiaoming can sayHello, but also arrays can sayHello
var arr = [1,2,3];
arr.sayHello();

// Then everything in the world can say hello
"kiss you".sayHello();

Object.prototype is the end of the prototype chain of all objects, so we directly give it to object If a method is added to prototype, all objects in the world can call this method:

2. Constructor of reference type

All reference type values have built-in constructors. such as

new Object()

new Array()

new Function()

new RegExp()

new Date()

Let's take a look at the array:

// Now it's an array literal
var arr = [66,4343,23];
// arr.haha = 23;
// console.log(arr.haha); //23

console.log(arr.__proto__);   // Never mind, just look at the constructor
console.log(arr.__proto__.constructor);

// Find the end point of the prototype chain and find that it is object prototype
console.log(arr.__proto__.__proto__.constructor)

Functions are also objects. In JavaScript, functions are first-class citizens and functions are objects. Functions are also objects, but they can be executed by themselves ().

function People(){
    
}
People.haha = 25;
//People.haha++;
//People.haha++;
console.log(People.haha);   //27

var xiaoming = new People();
console.log(xiaoming.haha);    // undefined

console.log(People.__proto__);   // Never mind, just look at the constructor
console.log(People.__proto__.constructor);
// Did we say that before__ proto__, Only prototype and only instance of constructor are used__ proto__, That's to make you distinguish between categories and examples
// Now the function itself is an object

Example:

We can use the mechanism of prototype chain to add methods to array objects

//array
var arr = [45,34,23,45,65,76,45];

//We can use the mechanism of prototype chain to add methods to array objects
Array.prototype.max = function(){
    var max = -Infinity;
    for(var i = 0 ; i < this.length ; i++){
        if(this[i] > max){
            max = this[i];
        }
    }
    return max;
}

//Any array can call this method:
console.log(arr.max());
3. Basic types of packaging

Basic type value, but also packaging type. The so-called wrapper type is its constructor.

new Number()

new String()

new Boolean()

character string

var str = new String("How do you do");
console.log(str);        //String {"hello"}, class array
console.log(str[0])
console.log(str.toString()); //"Hello"


// This is insane. It's better to create you literally. No one uses new String()
// Anyway, I'm just telling you today that any string literal is not an object, but it has a wrapper class

var str = "How do you do"
// It has an internal mechanism that comes out in the above way

// But js is screwing bar
console.log(str.__proto__.constructor);
console.log(str.__proto__.__proto__.constructor);

// The string has a wrapper class. You can understand that "hello" comes from the wrapper class new, which has no practical significance
// But you can't say string object, but it does__ proto__


var str = "wuwei";
console.log(str.length)

Only when the string is converted into a wrapper class can methods and properties be called

var str = "abcdefghijklmnopqrstuvwxyz"
console.log(new String(str));
var ap =document.querySelector('p');
ap.innerHTML = (str.bold()).fontsize(30);//Inserting styles directly into HTML is now obsolete
console.log(str.__proto__)  //The str prototype object is a String object
console.log(str.__proto__.__proto__)  //The prototype Object of str prototype Object is Object object

numerical value

var num = 123;
console.log(num.__proto__.constructor);

Boolean value

var a = true;
console.log(a.__proto__.constructor);

null,undefined

var b = null;
console.log(b.__proto__.constructor); //report errors
// Uncaught TypeError: Cannot read property '__proto__' of null

var c = undefined;
console.log(c.__proto__.constructor); //report errors
// Uncaught TypeError: Cannot read property '__proto__' of undefined


//The list of constructors of all function prototypes in JS = = = = > is shown below
Object.prototype.constructor === Object
Number.prototype.constructor === Number
Array.prototype.constructor === Array

//All function prototypes are object Prototype, as shown below
Function.prototype.__proto__ === Object.prototype
Number.prototype.__proto__ === Object.prototype
Array.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

//The constructor of all functions in JS is function, as shown below    
Object.constructor === Function
Number.constructor === Function
Array.constructor === Function

//The prototype of all functions is function Prototype, as shown below
Object.__proto__ === Function.prototype
Number.__proto__ === Function.prototype
Array.__proto__ === Function.prototype

II Objects and attributes

Determine whether the attribute exists

1. Directly manage the object to verify whether an attribute exists

Object management calls attributes, which we have talked about in previous courses, traversing the prototype chain. So you can see whether the attribute is on yourself or on the prototype chain. If yes, return the value; If not, return undefined

var obj = {
    a : 1,
    b : 2,
    c : 3
    // m: undefined
}
obj.__proto__ = {
    d : 4
}

console.log(obj.m); //undefined
console.log(obj.a); //1
console.log(obj.b); //2
console.log(obj.c); //3
console.log(obj.d); //4

There is a misunderstanding, such as obj If the value of m is undefined, then obj m still returns undefined. Do not know if the m attribute exists. At this point, we need the following methods to solve it

2. in operator

Returns a Boolean value indicating whether this property is an object property.

var obj = {
    a : 1,
    b : 2,
    c : false
}

console.log("a" in obj);    //true
console.log("b" in obj);    //true
console.log("c" in obj);    //true
console.log("d" in obj);    //false

// If there are methods on the prototype
obj.__proto__ = {
    d: 20
}
console.log("d" in obj);    //true

In not only detects whether the object itself has this attribute, but also returns true if there is this attribute on the prototype chain. If the entire prototype chain does not have this attribute, it returns false. In other words, the in operator performs prototype chain lookup.

The for in loop will list all enumerable attributes on the prototype chain:

    var obj = {
      a: 1,
      b: 2,
      c: false
    }
    obj.__proto__ = {
      d: 20
    }
    for (var key in obj) {
      console.log(key);
    }

What is enumerable? The default properties of the system (such as constructor) are not enumerable. The for in loop can list the attributes added by itself. It lists not only the attributes on itself, but also all the attributes on the prototype chain, which is inconvenient to distinguish.

3. Hasownproperty method (is it your own property)

This method is defined in Object Prototype Object, so any Object can have this method.

This method returns true and false. Indicates whether you own this attribute, regardless of the prototype chain. It depends on whether you have this attribute. You don't search the prototype chain.

var obj = {
    a : 1,
    b : 2,
    c : 3
}
obj.__proto__ = {
    d : 4
}

console.log(obj.hasOwnProperty("a")); //t
console.log(obj.hasOwnProperty("b")); //t
console.log(obj.hasOwnProperty("c")); //t
console.log(obj.hasOwnProperty("d")); //f

for... in considers the prototype chain, so we can embed a judgment and output our own attributes:

for(var k in obj){
    obj.hasOwnProperty(k) && console.log(k);
}

The most reliable way is the combination of the two

Define attribute object Defineproperty() defines a property of an object

js engine allows the control of attribute operations, which requires the use of method object Defineproperty(). This method receives three parameters: the object where the attribute is located, the name of the attribute, and the descriptor object.

      var obj = {
        name: "Zhang San"
      }
      // defineProperty is the description of the Object's properties. defineProperty is attached to the Object
      //     The first parameter: which object to add attributes to
      //     The second parameter: the added attribute name should be quoted
      //     The third parameter: the description object of the attribute
      Object.defineProperty(obj, 'age', {
        //  value:1111, / / indicates the attribute value. Do not set it together with get set. The value of age in the console is light
        //get set are all functions
        get: function () {     //obtain
          console.log("You want to get age attribute", arguments);  //Arguments has no arguments
          return 123;         //return determines the value to get
        },
        set: function (value) {       //set up
          console.log("You're giving me age assignment", arguments);
        }
      })

      obj.age = "777777"  //To obj When you set the value of age, you will call the set function
      console.log(obj);//At this time, the age (...) color in the console is light, indicating that the value is obtained dynamically
// console.log(obj.age); // Get obj The value of age actually goes through the get method

configurable: indicates whether the attribute can be deleted through delete to reset the attribute

 var obj = {
      name: "Zhang San"
    }
    Object.defineProperty(obj, 'age', {
      configurable: true,//If configurable is set to true, the age attribute can be deleted  
      get: function () {
        console.log("You want to get age attribute", arguments);
        return 123;
      },
      set: function (value) {
        console.log("You're giving me age assignment");
      }
    })
    console.log(obj);



    var obj = {
      name: "Zhang San",
      sex: "female"
    }
    Object.defineProperty(obj, 'name', {
      configurable: false   //name can be deleted before. It cannot be deleted after setting configurable:false
    })
    console.log(delete obj.name);
    console.log(obj);

Whether the property can be redefined by using the defineProperty method (normally, the method can be used repeatedly to reset the property). The default is true.

The attributes of js are gray. enumerable is false by default and cannot be traversed through for in.

    var obj = {
      name: "Zhang San",
      sex: "female"
    }
    Object.defineProperty(obj, 'age', {  
      enumerable:true,   //age is gray in the console and will not be traversed by for(var key in obj). Only dark attributes will be traversed. If you want to traverse to add enumerable:true, it will become dark
      get(){
           return 34;
         }
    })
    console.log(obj);
    for(var key in obj){ console.log(key)}

writable: assign a new value to the attribute. Allow configuration

    var obj = {
      name: "Zhang San",
      sex: "female"
    }
    Object.defineProperty(obj, 'age', {  
      value:1111,
      writable:true
    })
    obj.age =2222;  // You cannot assign a value at this time. Add writable:true to assign a value to age
    console.log(obj);

Value: attribute value. This value is read first when reading the attribute value of an object. The default value is undefined.

Define multiple attributes object defineProperties()

var book = {};
    Object.defineProperties(book, {
        _year: {
            value: 2004
        },
        edition: {
            value: 1
        },
        year: {
            get: function () {
                return this._year;
            },
            set: function (newValue) {
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                }
            }
        }

    });

Read the description object of the property getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor(obj,"name")
4. instanceof operator

Class is called class in English, and instance is called instance in English.

instaceof operator:

    function AA() { }
    var aa = new AA()
    var bb = {}
    console.log(aa instanceof AA);//true yes
    console.log(bb instanceof AA);//false no

Verify whether the A object is an instance of class B.

give an example:

//Class, constructor
function Dog(){

}
//Instantiate an object
var d = new Dog();
//Verify if d is an instance of Dog
console.log(d instanceof Dog);  // true

One thing to note here: "Hello Kitty is a dog":

//Class, constructor
function Dog(){

}

function Cat(){

}
Cat.prototype = new Dog();  //inherit

var hellokitty = new Cat(); //An instance through cat
console.log(hellokitty.constructor); //Dog
console.log(hellokitty instanceof Cat); //true
console.log(hellokitty instanceof Dog); //true

Mechanism of instanceof operator: visit every prototype object in the prototype chain of Hello Kitty object. If the prototype object is the prototype of a constructor, it is considered that Hello Kitty is an instance of this constructor and returns true.

Check array:

When an array is detected with typeof, object is returned

var arr = [];
console.log(typeof arr);

The instanceof operator can easily solve the identification of arrays:

var arr = [];
console.log(arr instanceof Array);

ECMAScript5 standard adds an API validation array:

Array.isArray(arr)


var arr=[20,30];
arr.add="plus"
console.log(arr);
console.log(Array.isArray(arr));//Static method, the calling body will never change

IE9 becomes compatible.

To sum up, A instanceof B cannot prove that A comes from new B(), because it may be inherited.

5. Method of detecting data type

Object.prototype.toString.call(), as shown in the figure below

Object.prototype.toString.call(123);  //"[object Number]"
Object.prototype.toString.call("123");  //"[object String]"
Object.prototype.toString.call(true);  //"[object Boolean]"
Object.prototype.toString.call(undefined);  //"[object Undefined]"
Object.prototype.toString.call(null);  //"[object Null]"
Object.prototype.toString.call(function(){});  //"[object Function]"
Object.prototype.toString.call([]);  //"[object Array]"
Object.prototype.toString.call({});  //"[object Object]"

Package judgment data type

function getTypeof(val){
var  str = Object.prototype.toString.call(val)
var aa = str.split(' ')[1]//Cut according to the space to the following part
return aa.slice(0,aa.length-1)
}
console.log(getTypeof(123));
console.log(getTypeof('ss'));
console.log(getTypeof(true));
console.log(getTypeof(undefined));

III inherit

1. Prototype chain inheritance

Take the instance of the parent class as the prototype of the child class

function People(name){
    this.name = name;
}
People.prototype.sayHello = function(){
    alert("Hello, I'm" + this.name);
}

// var xiaoming = new People("Xiaoming");
// xiaoming.chifan();

function Student(name,xuehao){
    this.name = name;
    this.xuehao = xuehao;
}

//Core statement. The implementation of inheritance depends on this statement:
Student.prototype = new People('Daming');

//Student.prototype.sayHello = function(){
//    alert("Hello, I'm a pupil, my student number is" + this.xuehao);
//}
Student.prototype.study = function(){
    alert("study hard and make progress every day");
}

var xiaohong = new Student("Xiao Hong",1001);

xiaohong.sayHello();

Subclasses can override some methods of the parent class. The methods of the parent class do not conflict, because the methods appended by our subclasses are appended to the instances of the parent class.

However, the parent class adds new prototype methods / prototype properties, which can be accessed by all subclasses. Once the parent class changes, everything else changes

2. Constructor inheritance

Using the constructor of the parent class to strengthen the instance of the child class is equivalent to copying the instance property of the parent class to the child class (without using the prototype)

function People(name,age){
    this.name = name;
    this.age = age
    //This function points to window by default, and the name attribute is on window by default. If you want this to point to the object that calls this function, the displayed binding call can implement people call(this,name)
}
function Student(name,xuehao,age){
    //  Core statement
    People.call(this,name,age)//Binding calls displayed
    this.xuehao = xuehao;
}
var xiaohong = new Student("Xiao Hong",1001,19);
for(var key in xiaohong){
    console.log(key);
}
console.log(xiaohong);

Methods are defined in the constructor. They can only inherit the instance properties and methods of the parent class, cannot inherit the prototype properties / methods, and cannot realize function reuse. Each subclass has a copy of the instance function of the parent class, which affects the performance

3. Combination inheritance

Is to combine prototype chain inheritance and constructor inheritance; Inherit two advantages

By calling the parent class construction, inherit the properties of the parent class and retain the advantages of passing parameters,

Then, the function reuse is realized by taking the parent class instance as the subclass prototype

function People(name){
    this.name = name;
}
People.prototype.sayHello = function(){
    alert("Hello, I'm" + this.name);
}

function Student(name,xuehao){
    People.call(this,name);
    this.xuehao = xuehao;
}

//Core statement. The implementation of inheritance depends on this statement:
Student.prototype = new People('Daming');

Student.prototype.study = function(){
    alert("study hard and make progress every day");
}

var xiaohong = new Student("Xiao Hong",1001);

xiaohong.sayHello();

The parent class constructor is called twice and two instances are generated (the subclass instance masks the one on the subclass prototype)

4. Parasitic combinatorial inheritance

Through parasitism, the instance attribute of the parent class is cut off, so that when calling the construction of the parent class twice, the instance method / attribute will not be initialized twice, so as to avoid the disadvantage of combined inheritance

function People(name){
    this.name = name;
}
People.prototype.sayHello = function(){
    alert("Hello, I'm" + this.name);
}

function Student(name,xuehao){
    People.call(this,name);
    this.xuehao = xuehao;
}

//Core statement. The implementation of inheritance depends on this statement:
function Fn(){}
Fn.prototype = People.prototype
Student.prototype = new Fn();

Student.prototype.study = function(){
    alert("study hard and make progress every day");
}

var xiaohong = new Student("Xiao Hong",1001);

xiaohong.sayHello();
5. Holy Grail mode

The Holy Grail pattern is to use the constructor to encapsulate it

function inherit(People,Student){
    function Fn(){}
    Fn.prototype = People.prototype
    Student.prototype = new Fn();
    Student.prototype.constructor = Student;
    Student.prototype.parent = People;
}
inherit(People,Student )

Added by wystan on Sat, 26 Feb 2022 09:40:54 +0200