JS继承

1.原型链继承

特点:父类私有+公有–>子类公有
原型链继承:B.prototype = new A;子类原型等于父类的一个实例。

function A() {
    this.x = 100;
}
A.prototype = {
    constructor: A,
    getX: function () {
        console.log(this.x);
    }
}
function B() {
    this.y = 200;
}
B.prototype = {
    constructor: B,
    getY: function () {
        console.log(this.y);
    }
}

// 子类B的原型等于父类A的一个实例,这样原来定义在B原型中的属性和方法都没了,需手动添加
B.prototype = new A;
B.prototype.constructor = B;
B.prototype.getY = function () {
    console.log(this.y);
}

注意两点:

  1. 继承之后,在父类原型上增加方法,子类也拥有了该方法;父类修改原型上的方法,子类同样改变。
    var b = new B;
    A.prototype.setX = function () {
        this.x = 1000;
    }
    console.log(b);
    
    上述代码的输出结果为
    在这里插入图片描述
    b执行setX方法
    b.setX();
    console.log(b);
    
    执行结果如下(相当于给子类的实例对象b增加了一个私有属性x):
    在这里插入图片描述
  2. 子类也能改变父类原型上的引用类型的数据。有两点规则:
    (1)获取值的时候:先看私有是否存在,不存在则往公有上找。
    (2)设置值的时候:不管私有是否存在,都是给私有设置值。
    举个栗子,在下列代码中,我们给b.getXb.obj重新赋值:
    b.getX = function () {
        console.log("bbb");
    };
    b.obj = {
        name: "BBB"
    };
    a.getX();   //=>100
    b.getX();   //=>bbb
    console.log(b);
    console.log(a);
    
    输出结果如下,对父类的实例a并没有影响,因为改变的值都被添加为子类实例b的私有属性和方法了。
    以下是console.log(b);的输出结果:
    在这里插入图片描述
    以下是console.log(a);的输出结果:
    在这里插入图片描述
    如果直接在b中直接修改obj.name,也会修改a中的值。
    console.log(b.obj); //=>{ name: 'AAA' }
    console.log(a.obj); //=>{ name: 'AAA' }
    b.obj.name = "BBB";
    console.log(b.obj); //=>{ name: 'BBB' }
    console.log(a.obj); //=>{ name: 'BBB' }
    

2.call继承(借用构造函数)

特点:父类私有–>子类私有
call继承:子类构造函数中调用 父类名.call(this);在子类的函数体中,将父类当做普通函数执行,并且让父类中的this变成子类中的this(即,变成子类的一个实例),这就相当于把父类中的所有私有属性都增加到子类的实例中了。
举个栗子:

function A() {
    this.x = 100;
}
A.prototype = {
    constructor: A,
    getX: function () {
        console.log(this.x);
    }
};
function B() {
    this.y = 200;
    A.call(this);
}
var b = new B;
console.log(b); //=>B { y: 200, x: 100 }

缺点:只能继承A中的私有属性和方法,当做B中的私有的,公有属性无法处理。

3.组合继承1:原型链+call

特点:父类私有–>子类私有父类公有+私有-->子类公有。(父类私有继承了两遍)

function A() {
    this.x = 100;
}
A.prototype = {
    constructor: A,
    getX: function () {
        console.log(this.x);
    }
};
function B() {
    this.y = 200;
    A.call(this);
}
B.prototype = new A;
B.prototype.constructor = B;

A中私有的在B中私有和公有中都存在(重复了,但没有太大影响,因为原型链机制会优先查找私有的)。或者我们也可以写一个方法删除子类B原型中的父类A的私有属性:

B.prototype.delPrivate = (function () {
    var a = new A;
    for(var key in this){
        if(a.hasOwnProperty(key)){
            delete this[key];
        }
    }
    return null;    //返回null,这样函数自执行完成后,delPrivate=null,浏览器释放delPrivate的空间
}).call(B.prototype);   //自执行

缺点:父类私有属性继承了两次,需要自己写一个函数删除子类原型上的父类私有属性。(下面的组合继承2解决了这个问题)

4.原型式继承

特点:父类公有–>子类公有
原型式继承:子类.prototype = Object.create(父类.prototype);

父类公有–>子类公有,一种直接的方法是子类.prototype = 父类.prototype;这样写的问题在于:
相当于子类和父类的原型指向了同一个内存,当我们重新定义子类.prototype.consturctor = 子类名;的时候,父类.prototype.consturctor此时也是子类,所以用instanceof运算符判断一个实例对象所属类的时候,根本不知道是子类的还是父类的。

所以需要用Object.create将子类的prototype和父类的prototype隔离。下面自定义的object方法相当于Object.create方法。


function object(o){
    function F() {

    }  
    F.prototype = o;   
    return new F(); //返回一个实例对象:好处在于,之后再修改子类prototype上的constructor属性(非引用类型的属性)的时候,父类prototype上的不会变
}  

原型式继承栗子:

function A() {

}
A.prototype.say = function () {

};
function B() {

}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
var a = new A;
var b = new B;
console.log(a instanceof A);    //=>true
console.log(b instanceof B);    //=>true

5.组合继承2:原型式+call

特点:父类私有–>子类私有父类公有–>子类公有。(公有私有都只继承一遍)

function inheritPrototype(subClass, superClass){
    var prototype = Object.create(superClass.prototype);    //创建对象
    prototype.constructor = subClass;   //增强对象
    subClass.prototype = prototype; //制定对象
}
function A() {
    this.x = 100;
    this.arr = [1,2,3];
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    A.call(this, name); //继承私有
    this.y = 200;
}
inheritPrototype(B,A);  //继承公有

6.冒充对象继承

上述5中方法中,除了call继承,其他四种(原型链继承,原型式继承,组合继承1,组合继承2)子类都会改变父类原型中引用类型的数据。有一种解决方法:

深拷贝:参考jQuery源码中的extend方法。

所以通过冒充对象继承都继承为私有的。
冒充对象继承的特点:父类私有+公有–>子类私有
冒充对象继承:在子类的函数体中,先获取父类中的一个实例,然后遍历这个实例中的属性,把所有属性依次赋值给子类的实例的私有属性。

function A() {
    this.x = 100;
}
A.prototype = {
    constructor: A,
    getX: function () {
        console.log(this.x);
    }
};
function B() {
    this.y = 20;
    var temp = new A;
    for(var key in temp){
        if(key !== "constructor"){
            this[key] = temp[key];
        }
    }
}

缺点:将父类私有公有的属性和方法全部继承为子类私有的,这样父类中的函数无法复用,降低效率。

7.中间类继承

该继承方法使用到了__proto__,但是IE下不支持操作__proto__,所以不兼容IE浏览器。
举个栗子:

function sum() {
    //arguments是类数组,没有sort方法,因此将arguments的__proto__直接指向Array.prototype
    //这样数组的方法,arguments都能使用了
    arguments.__proto__ = Array.prototype;
    var res = arguments.sort(function (a,b) {
        return a-b;
    });
        console.log([...res]);  //=>[ 10, 12, 14, 15, 23, 25 ]
}
sum(12,23,25,14,15,10);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值