js继承

js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现。

在面向对象的语言中,我们使用类来创建一个自定义对象。然而js中所有事物都是对象,那么用什么办法来创建自定义对象呢?这就需要用到js的原型(prototype)。

我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,新实例化的对象内部有一个看不见的__Proto__指针,指向原型对象)。

js可以通过构造函数和原型的方式模拟实现类的功能。 另外,js类式继承的实现也是依靠原型链来实现的。

既然要实现继承,那么首先我们得有一个父类,代码如下:

// 定义一个Parent
function Parent(name){
    this.name = name || "parent";
}
// 原型方法
Parent.prototype.getName = function(){
    return this.name;
};

一、简单原型链

  • 具体实现

    这种是最简单实现原型继承的方法,直接把父类的对象赋值给子类构造函数的原型,这样子类的对象就可以访问到父类以及父类构造函数的prototype中的属性。
    
var Child= function(name){
    this.name = name ||'child';
};
// 继承Parent
Child.prototype = new Parent();
var pp = new Parent();
var cc = new Child();
console.log(pp.name); // parent
console.log(cc.name); // child
console.log(pp.getName()); // parent
console.log(cc.getName()); // Uncaught TypeError: cc.getName is not a function
console.log(cc instanceof Parent); // true
console.log(cc instanceof Child); // true 
  • 原型继承图

这里写图片描述

  • 优点 - 1.非常纯粹的继承关系,实例是子类的实例,也是父类的实例; 2.父类新增原型方法/原型属性,子类都能访问到; 3.简单,易于实现。
  • 缺点 - 1.来自原型对象的引用属性是所有实例共享的; 2.创建子类实例时,无法向父类构造函数传参;3.无法实现多继承;

二、构造函数绑定(类式继承)

  • 具体实现

     借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例装上了(完全没有用到原型)。
    
function Child(name,age){
    // 继承Parent
    Parent.call(this, name); // 或 Parent.apply(this,[a1,a2,a3...])(又叫对象冒充)
    this.age= age;
}
Child.prototype = new Parent();
var pp = new Parent();
var cc = new Child("小明","18");
console.log(pp.getName()); // parent
console.log(cc.getName()); // 小明
console.log(cc instanceof Parent); // false
console.log(cc instanceof Child); // true 
  • 原型继承图

这里写图片描述

  • 优点 - 1.解决了子类实例共享父类引用属性的问题; 2.创建子类实例时,可以向父类构造函数传参。
  • 缺点 - 1.无法实现函数复用,影响内存

三、组合继承(伪经典继承)(最常用)

  • 具体实现

    使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现在实例属性的继承;这样既保障了原型上方法的复用,有保障了每个实例都有它自己的属性。
    
function Child(name,age){
    // 继承属性
    Parent.call(this, name); // 第二次调用Parent()
    this.age = age;
}
// 继承方法
Child.prototype = new Parent(); // 第一次调用Parent()
Child.prototype.getAge = function(){
    return this.age;
}
var pp = new Parent();
var cc = new Child("小明","18");
console.log(pp.getName()); // parent
console.log(cc.getName()); // 小明
console.log(pp.getAge()); // Uncaught TypeError: pp.getAge is not a function
console.log(cc.getAge()); // 18
console.log(cc instanceof Parent); // true
console.log(cc instanceof Child); // true 
  • 原型继承图

这里写图片描述

  • 优点 - 1.不存在引用属性共享问题;2.可传参;3.函数可复用。
  • 缺点 - 子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的,浪费内存。

四、原型式继承

  • 具体实现

    用生孩子函数得到得到一个“纯洁”的新对象(“纯洁”是因为没有实例属性),再逐步增强之(填充实例属性)。
    
function beget(o){// 生孩子函数 beget:龙beget龙,凤beget凤。
    function F(){}
    F.prototype = o;
    return new F();
}
var parent = new Parent();
var child = new beget(parent);
child.age = '18';
child.sex = 'male';
console.log(child.name); // parent
console.log(child instanceof Parent); // true
console.log(child instanceof beget); // false
  • 优点 - 从已有对象衍生新对象,不需要创建自定义类型(对象的浅复制)
  • 缺点 - 1.原型引用属性会被所有实例共享,因为是用整个父类对象来充当了子类原型对象;2.无法实现代码复用。

五、 寄生式继承

  • 具体实现

    寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是他做了所有工作一样返回对象。
    
function createAnother(original){
    var clone = Object.create(original); //通过调用函数创建一个新对象
    clone.sayHi = function(){    // 以某种方式来增强这个对象
        alert("Hi");
    };
    return clone;      //返回这个对象
}
var parent = new Parent();
var anotherChild = createAnother(parent); // 相当于复制
console(anotherChild.getName()); // parent
  • 优点 - 从已有对象衍生新对象,不需要创建自定义类型(对象的浅复制)
  • 缺点 - 1.原型引用属性会被所有实例共享,因为是用整个父类对象来充当了子类原型对象; 2.无法实现代码复用。

六、寄生组合继承(最佳方式)

  • 具体实现

    1.通过借用构造函数来继承属性;
    2.通过原型链的混和方式来继承方法。
    
function beget(obj){   // 生孩子函数 beget:龙beget龙,凤beget凤。
    var F = function(){};
    F.prototype = obj;
    return new F();
}
function Child(name, age){
    Parent.call(this,name);
    this.age = 18;
}
var proto = beget(Parent.prototype); // 创建对象
proto.constructor = Child; // 增强对象
Child.prototype = proto; // 指定对象
Child.prototype.getAge = function(){
    return this.age;
} 
var pp = new Parent();
var cc = new Child("小明","18");
console.log(pp.getName()); // parent
console.log(cc.getName()); // 小明
console.log(pp.getAge()); // Uncaught TypeError: pp.getAge is not a function
console.log(cc.getAge()); // 18
console.log(cc instanceof Parent); // true
console.log(cc instanceof Child); // true 
  • 原型继承图

这里写图片描述

目前来说,寄生组合式是最好的一种继承的方法。

以上如有不对之处请大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值