JavaScript继承

1. 原型链继承

在原型搜索机制中,构造函数,实例,和原型对象的关系为,原型对象是构造函数的一个属性prototype,原型对象中有一个属性constructor指回构造函数,构造函数生成的实例中具有指针指向构造函数上的原型对象。
原型链继承
当搜索一个属性/方法时,先搜索实例本身,如果实例本身没有找到,再从指针找到其指向的原型对象,向上查找的特性与作用域链相似。也就是说,如果在实例中设置了要查找的属性名,就不会再向上查找。这时候如果没找到,并且该原型对象中也有一个指向另一个原型对象的指针,就从指针向上搜索原型的原型,直到原型链的末端,也就是原型不再包含指向另一个原型的指针为止。
特点:

  1. 父类新增的原型属性/方法,子类都能够访问到
  2. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例

缺点:

  1. 来自原型对象的所有属性被所有实例共享
  2. 创建子类实例时,无法向父类构造函数传参

2. 构造函数继承

在子类构造函数中调用父类构造函数。

function Animal(){
    this.categorys = ['cat', 'rabbit'];
}
function Dog(){
    //继承Animal
    Animal.call(this);
}
var d1 = new Dog();//d1调用Dog构造函数,其内部this指向d1,所以Animal.call(this)就相当于Animal.call(d1),就相当于d1.Animal()。最后d1去跳用Animal方法时,Animal内部的this就指向d1.那么Animal内部this上的所有属性和方法,都被拷贝到了d1上。所以,每个实例都具有自己的categorys属性副本,互不影响。
d1.categorys.push('dog');
console.log(d1.categorys);//[ 'cat', 'rabbit', 'dog' ]
var d2 = new Dog();
console.log(d2.categorys);//[ 'cat', 'rabbit', 'dog' ]

特点:

  1. 创建子类实例时,可以向父类传递参数
  2. 可以实现多继承(call多个父类对象)

缺点:

  1. 必须在构造函数中定义方法,无法实现函数复用
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 实例并不是父类的实例,只是子类的实例

3. 组合继承(伪经典继承)

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

function Animal(){
    this.categorys = ['cat', 'rabbit'];
}

Animal.prototype.sayCategorys = function(){
    console.log(this.categorys);
}

function Dog(){
    Animal.call(this);//构造函数继承
}

Dog.prototype = new Animal();//原型链继承,先执行

var d1 = new Dog();//调用Dog构造函数,在实例中创建categorys属性,屏蔽了原型上的categorys属性
d1.categorys.push('dog');
console.log(d1.categorys);//[ 'cat', 'rabbit', 'dog' ]
var d2 = new Dog();
console.log(d2.categorys);//[ 'cat', 'rabbit', 'dog' ]

d1.sayCategorys();//[ 'cat', 'rabbit', 'dog' ]

特点:

  1. 既是子类的实例,也是父类的实例
  2. 不存在引用属性共享问题
  3. 可传参
  4. 函数可复用

缺点:
调用了两次父类构造函数,生成了两份实例

4. 寄生组合继承

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

function Animal(){
    this.categorys = ['cat', 'rabbit'];
}

Animal.prototype.sayCategorys = function(){
    console.log(this.categorys);
}

function Dog(){
    Animal.call(this);
}
//Object.create()为ES5方法,相当于以下函数(如果浏览器不兼容)
if(!Object.create){
    Object.create = function(proto){
        function F(){};
        F.prototype = proto;
        return new F();
    }
}

// Dog.prototype = new Animal();
Dog.prototype = Object.create(Animal.prototype);//将Animal的原型对象赋值给Dog的原型对象
var d1 = new Dog();
d1.categorys.push('dog');
console.log(d1.categorys);//[ 'cat', 'rabbit', 'dog' ]
var d2 = new Dog();
console.log(d2.categorys);//[ 'cat', 'rabbit', 'dog' ]

d1.sayCategorys();//[ 'cat', 'rabbit', 'dog' ]
  • Object.create 是创建一个新对象,使用现有的对象来提供新创建对象的 proto。意思就是生成一个新对象,该新对象的 proto(原型) 指向现有对象。
  • new 生成的是构造函数的一个实例,实例继承了构造函数及其 prototype(原型属性)上的属性和方法。

new关键字创建的对象会保留原构造函数的属性,而用Object.create()创建的对象不会。
缺点:
如果在执行Object.create()方法之前,Dog.prototype上有定义的属性/方法,将会被覆盖

5. 拷贝继承

把一个对象中的属性或者方法直接复制到另一个对象中

function extend(obj, cloneObj){
    if(typeof obj !== 'object'){
        return false;
    }
    cloneObj = cloneObj || {};
    for(var i in obj){
        if(typeof obj[i] === 'object' && typeof obj[i] !== null){
            cloneObj[i] = obj[i] instanceof Array? []:{};
            extend(obj[i], cloneObj[i]);
        } else {
            cloneObj[i] = obj[i];
        }
    }
    return cloneObj;
}

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

var obj1 = extend(obj)
obj.c.c.c = 3
console.log(obj)
console.log(obj1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值