Home  >  Article  >  Web Front-end  >  Summary of things to pay attention to in the JavaScript prototype chain

Summary of things to pay attention to in the JavaScript prototype chain

不言
不言forward
2018-10-20 15:59:551648browse

This article brings you a summary of what you need to pay attention to in the JavaScript prototype chain. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Foreword: I have recently been reading Javascript Advanced Programming. For me, the Chinese version of the book has a lot of unsatisfactory translations, so I try to interpret it using what I understand. If there are any mistakes or omissions, we will be very grateful for your pointing them out. Most of the content in this article is quoted from "JavaScript Advanced Programming, Third Edition"

1. Don't forget the default prototype

In fact, the prototype chain shown in the previous example One link is missing.

We all know that all reference types inherit Object by default, and this inheritance is also implemented through the prototype chain.

The default prototype of all functions is an instance of Object. Because the prototype object of a function is also an object! The object is of course an instance of Object!

So the prototype of the function will contain an internal pointer (__proto__), pointing to Object.prototype.

This is also the fundamental reason why all custom types inherit default methods such as toString() and valueOf().

So, the prototype chain of the prototype shown in the previous example should also include another inheritance level.

The following code shows the complete prototype chain.

//完整原型链的伪代码
function Object() {
}
Object.prototype = {
    constructor: f Object(),
    hasOwnProperty: f hasOwnProperty(),
    isPrototypeOf: f isPrototypeOf(),
    propertyIsEnumerable: f propertyIsEnumerable(),
    toLocaleString: f toLocaleString(),
    toString: f toString(),
    valueOf: f valueOf()
}
//SuperType 父类型
function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperProperty = function() {
    console.log(this.property);
    return this.property;
}
/*
SuperType.prototype = {
    constructor: f SuperType(),
    getSuperProperty: function() {
    console.log(this.property);
    return this.property;
    }, 
    __proto__ : {
        constructor: f Object(),
        hasOwnProperty: f hasOwnProperty(),
        isPrototypeOf: f isPrototypeOf(),
        propertyIsEnumerable: f propertyIsEnumerable(),
        toLocaleString: f toLocaleString(),
        toString: f toString(),
        valueOf: f valueOf()
    }
}
*/
//SubType 子类型
function SubType() {
    this.subproperty = false;
}
//子类型 继承 父类型
SubType.prototype = new SuperType();
//实际上子类型的原型是这样的。
/*SubType.prototype = {
    property: true,
    __proto__:  {
        constructor : SuperType,
        getSuperProperty:function() {
            console.log(this.property);
            return this.property;
        }
    }
}
*/
SubType.prototype.getSubProperty = function(){
    console.log(this.subproperty);
    return this.subproperty;
}
//那么现在子类型的原型对象是这样的
/*SubType.prototype = {
    property: true,
    getSubProperty: function()  {
    console.log(this.subproperty);
    return this.subproperty;
    },
    __proto__:  {
        constructor : SuperType,
        getSuperProperty:function() {
            console.log(this.property);
            return this.property;
        }
    }
}
*/

var subInstanceObject = new SubType();
console.log(subInstanceObject.getSuperProperty()); // true

In a word, SubType (subtype) inherits SuperType (parent type),

And SuperType (parent type) inherits Object (ancestor).

When calling subInstanceObject.toString(), what is actually called is the method saved in Object.prototype.

2. Determine the relationship between prototype and instance objects

The relationship between prototype and instance can be determined in two ways.

The first way is to use the instanceof operator. As long as the prototype chain in the detected instance object contains a constructor that has appeared, the result will return true.
Because this means that they are all involved in the creation of instance objects.

console.log(subInstanceObject instanceof Object); // true
console.log(subInstanceObject instanceof SuperType); // true
console.log(subInstanceObject instanceof SubType); // true

Due to the prototype chain, we can say that subIntanceObject is an instance of any type among Object, SuperType or SubType.

The second way is to use the isPrototypeOf() method. Similarly, as long as it is a prototype that appears in the prototype chain, it can be said to be the prototype of the instance object derived from the prototype chain.

console.log(Object.prototype.isPrototypeOf(subInstanceObject)); //true
console.log(SuperType.prototype.isPrototypeOf(subIntanceObject)); // true
console.log(SubType.prototype.isPrototypeOf(subIntanceObject)); //true

3. Define methods carefully

Subtypes sometimes need to override a method of the parent type, or add a method that does not exist in the parent type .

But no matter what, the code for adding methods to the prototype must be placed after the statement that replaces the prototype.

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
}

function SubType() {
    this.subproperty = false;
}

//继承了SuperType
SubType.prototype = new SuperType();

//给原型添加方法的代码一定要放在替换原型的语句之后
//添加新方法
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

//重写 超类型中 的 方法
SubType.prototype.getSuperValue = function() {
    return false;
}

var instance = new SubType();
console.log(instance.getSuperValue())

In the above code, the first method getSubValue() is added to SubType.
The second method getSuperValue() is a method that already exists in the prototype.
Overriding this method will cause the prototype of the subclass to find its own getSuperValue() method.
When getSuperValue() is called through the SuperType instance object, the original method will continue to be called.

Once again, you must define two methods after replacing the prototype with an instance object of SuperType.

One more thing that needs to be reminded is that when inheritance is implemented through the prototype chain, object literals cannot be used to create prototype methods. This will rewrite the prototype chain.

function SuperType(){
    this.property = true;
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
}

function SubType(){
    this.subproperty = false;
}

//继承SuperType
SubType.prototype = new SuperType();

/* 
现在的原型
SubType.prototype = {

    property: true,
    __proto__: {
        constructor: SuperType,
        getSuperValue: function() {
            return this.property;
        }
    }
}
*/

//使用对象字面量语法会改写原型,导致上一行代码无效
// SubType.prototype = new Object();
SubType.prototype = {

    getSubValue: function() {
        return this.subproperty;
    },

    someOtherMethod: function () {
        return false;
    }

    /*,
    __proto__ : {
        constructor: fn Object(),
        .......
    }
    */

}

var instance =  new SubType();
console.log(instance.getSuperValue()); // error: instance.getSuperValue is not a function

The above code shows the problem caused by just assigning the instance object of SuperType to the prototype, and then replacing the prototype with an object literal.

Because the prototype of SubType actually saves an instance of Object instead of an instance object of SuperType, this chain is broken.

4. Problems with the prototype chain

Although the prototype chain is very powerful and can be used to implement inheritance, it always has shortcomings. There is no perfect method in the world.

The main problem comes from prototypes containing reference type values.

Prototype properties containing reference type values ​​will be shared by all instance objects.

And this is why the prototype pattern and the constructor pattern are used in combination.
Define properties in constructor mode and define shared methods in prototype mode.

When implementing prototypal inheritance through a prototype, the prototype will actually become an instance object of another type.

The original instance object properties have become the current prototype properties.

function SuperType() {
    this.colors = ['red', 'green', 'blue'];
}

function SubType() {
}

// 子类型继承父类型
SubType.prototype = new SuperType();

/*
SubType.prototype = {
    colors: ['red', 'green', 'blue'],
    __proto__: {
        constructor: fn SuperType(),
        .....
    }
}
*/

var instance1 = new SubType();

instance1.colors.push('black');

console.log(instance1.colors); // ['red', 'green', 'blue', 'black']

var instance2 = new SubType();

console.log(instance2.colors); // ['red', 'green', 'blue', 'black']

The second problem with the prototype chain is that there is no way to pass parameters to the constructor of the parent type without affecting all instance objects.

Due to the above two problems, the prototype chain is rarely used alone in events.

The above is the detailed content of Summary of things to pay attention to in the JavaScript prototype chain. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete