”谈一谈“之ES5继承的6种方式

136 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

”谈一谈“之ES5继承

继承

什么叫做继承?我的个人用js的理解就是:继承就是就是创造出来的对象继承了其他的构造函数里面的方法或者属性,那么,这个“其他的构造函数”就是这个对象的父类。那么继承的类叫做“子类”。继承完父类之后,子类就可以获取所有方法或者属性。这边先讲es5的继承,es6有了class类的概念(其实就是与语法糖)后续会讲。 es5是没有类的概念的,所以常用的方式就是通过原型链的方式去实现的。

image.png

原型

搞懂继承的前提就是搞懂原型。先回顾下实例、构造函数和原型的关系:一个构造函数包含了一个原型对象和其他属性和方法,原型对象包含了一个指向了构造函数的指针和一些原型上的属性和方法。那么如果让A原型等于B的实例,B的原型等于的C的实例,C的原型等于D的实例,如此往复就成了实例与原型的链条。这就是所谓原型链。

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;
};
var instance = new SubType();
// alert(instance.getSuperValue());  
console.log(instance)

以上的代码就是通过原型链去继承的,sub的原型等于了实例sup,所以sub可以调用所有sup以下的方法或者属性,如果sub和sup同时有一个方法或者属性,结果会怎么样呢?


 function SuperType() {
            this.property = true;
            this.name = 'sup constructor'
        }
        SuperType.prototype.name = 'sup prototype'
        SuperType.prototype.getSuperValue = function () {
            return this.property;
        };

        function SubType() {
            this.subproperty = false;
            this.name = 'sub constructor'
        }
        //继承了 SuperType 
        SubType.prototype = new SuperType();
        SubType.prototype.name='sub prototype'
        SubType.prototype.getSubValue = function () {
            return this.subproperty;
        };
        var instance = new SubType();
        // alert(instance.getSuperValue());  
        console.log(instance)
        //sub constructor

返回的结果是按照最外层的最外层的name值去返回,当自己没有东西,就会往下一层去找,直至Obejct对象。

image.png

  • 检测实例和原型关系的两个方法
    • instratceof
    • isPrototypeOf
    A instanceof B //true
    A instanceof C //true
    A instanceof Object //true
    
    Object isPrototypeOf A //true
    B isPrototypeOf A //true
    C isPrototypeOf A //true
    

原型链继承的问题:

function A (){
    console.log('A 执行了')
    this.name = [1,2,3]
}
function B(){

}
B.prototype = new A()
let BC = new B()
let BB = new B()
console.log(BC.name)
BC.name.push(4)
console.log(BB.name)

以上代码,B继承了A,A下面有个引用类型。创建出BC 实例,通过BC实例去修改继承的A中的值,这时再创建BB实例,创建出来获取name值,值是被BC修改过得值,这样显然不对。那什么原因导致的呢?可以在打印结果这边看到 “A 执行了”只打印了一次。这就是为什么BB答应出来的name会随着BC的实例去影响。

借用构造函数去继承

为了解决上面的问题,现在引入了一个构造函数去继承这个方法;

function A (){
    console.log('A 执行了')
    this.name = [1,2,3]
    this.age = arguments[0]
}
function B(){
    A.call(this,...arguments)
}
let BC = new B(1)
let BB = new B(2)
console.log(BC.name)
console.log(BC.age)
BC.name.push(4)
console.log(BB.name)
console.log(BB.age)

上面就是通过构造函数去实现了继承