JavaScript继承的实现

JavaScript 通过函数的方式实现继承,其中分为原型链继承,借用构造函数继承,组合继承,原型式继承,寄生式继承与寄生组合式继承。其中最后性能最好的是寄生组合式继承。具体分析如下

原型链继承

实现形式

// A类型
function A(){
    this.a = true;
};
A.prototype.getA = function(){
    return this.a;
};

//B类型
funcion B(){
    this.b = false;
};
B.prototype = new A();
B.prototype.getB = function(){
    return this.b;
};

var instance = new B();
instance.getA();    //false
instance.getB();    //true

特点分析

  • 将派生类的原型指定为基类的一个对象实例
  • 缺点:
    1. 所有派生类的对象实例共享同一个基类实例
    2. 不能给基类的构造函数中传递构造参数,即没办法在不影响所有对象实例的情况下给基类构造函数传递参数

借用构造函数继承

实现形式

// A类型
function A(name){
    this.name = name;
};
A.prototype.getName = function(){
    return this.name;
};

//B类型
funcion B(name, age){
    A.call(this, name);
    this.age = age;
};
B.prototype.getAge = function(){
    return this.age;
};

var instance = new B('XiaoMing', 24);
instance.getName();     //报错,找不到函数 getName
instance.getAge();  //24

特点分析:

  • 在派生类的构造函数中使用 call 函数调用基类的构造函数
  • 实际是在即将要创建的派生类实例的环境下调用了基类的构造函数,这样会在新派生对象上执行基类构造函数中定义的所有对象初始化代码。即此时的 name 属性是直接挂在 intance 对象上的
  • 缺点:
    • 在基类的原型中定义的函数或属性在派生类中不可见,这就导致只能将函数定义在基类构造函数中,这样就不能实现函数的复用了。

组合继承

实现形式

// A类型
function A(name){
    this.name = name;
};
A.prototype.getName = function(){
    return this.name;
};

//B类型
funcion B(name, age){
    A.call(this, name);
    this.age = age;
};

B.prototype = new A();

B.prototype.getAge = function(){
    return this.age;
};

var instance = new B('XiaoMing', 24);
instance.getName();     //XiaoMing
instance.getAge();  //24

特点分析:

  • 在派生类的构造函数中使用 call 调用基类的构造函数,同时将派生类的原型设置为基类的一个对象实例
  • 实现原理

    1. 首先要明确,由于在派生类构造函数中使用 call 导致新的派生类对象会增加基类构造函数中初始化的属性或函数。如 instance.name 中 name 属性就是由A构造函数加到 B 的新对象实例 instance 上的。

    2. 其次,由于将派生类的原型指向基类的一个实例,导致派生类的所有实例对象共享同一个基类对象的问题仍然存在。

    3. 该方法能在派生类的不同对象实例中使用各自的基类属性的原因是:屏蔽。构造函数初始化将基类的属性挂在了派生类对象上,从而屏蔽了派生类对象共享原型中同名的属性
  • 缺点:

    • 派生类对象实例共享的原型中含有重复的基类中普通属性,其实派生类原型只想指向基类原型,而不是执行基类实例。

原型式继承

实现形式

//返回一个中间对象,该中间对象的原型指向传入的参数o
function object(o){
    function F(){};
    F.prototype = o;
    return new F();
};

var person = {
    name : "A",
    friends : [1]
};

var person1 = object(person);
person1.name = 'B';         //该语句为person1创建了新的属性name, 屏蔽了person 中的同名属性
person1.friends.push(2);  //该语句找到了person中的friends属性并在该属性上操作

var person2 = object(person);
person2.name = 'C';
person2.friends.push(3);

//此时: person.name 值为 A, person1.name 值为 B , person2.name 值为3
// friends 值为[1,2,3]

特点分析:

  • object 函数创建了一个对象,该对象的prototype 指向传入的对象。
  • 对object 函数返回的对象实例 (person1,person2),以下操作意味着为该新对象新增属性
person1.name = 'B';

这个语句意味着将屏蔽 person 中的name属性

寄生组合继承

表现形式

function object(o){
    function F(){};
    F.prototype = o;
    return new F();
};

function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); //prototype 是一个中间对象,其原型指向基类的原型
    prototype.constructor = subType; //恢复由于设置该中间对象的原型值后改变了的构造函数指向
    subType.prototype = prototype; //将派生了的原型指向该中间对象
};

//通过上述操作后,派生类的原型指向了一个中间对象,该中间对象的原型则又指向了基类对象的原型。
//在派生类对象到基类对象的原型链之间增加了一个中间对象。该中间对象的作用将在下面说明

function SuperType(name){
    this.name = name;
};

function SubType(name, age){
    SuperType.call(this, name); //为每一个派生类对象实例都增加基类的实例属性与函数(不含基类原型的属性)
    this.age = age;
};

inheritPrototype(SubType, SuperType);
//让派生类的原型指向一个中间对象,该中间对象的原型指向基类的原型

SubType.prototype.sayAge = function(){
    return this.age;
};
//为派生类的原型中增加共享的函数,如果派生类对象的原型没有指向一个中间对象而是直接指向了基类的原型,则该操作会将新增的共享函数加到基类的原型中去。即派生类影响了基类。但通过增加一个中间对象层,该操作会将派生类对象实例间共享的函数加到该对象层。

 特点分析

  • 该操作中重点以下两个方面:
    • 在派生类构造函数中借用了基类的构造函数。
    • 将派生类的原型指向了一个中间对象,该中间对象的原型又指向了基类的原型
  • 这种继承方式没有缺点,即避免了在派生类对象实例和原型中重复包含基类实例属性与函数,同时还保持了原型链的透明(使用过程中发现不了中间对象层的存在)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值