首先我们了解,js中的继承是主要是由原型链实现的。那么什么是原型链呢?

  由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的原型对象,如此循环,就行成了原型链。

  在了解原型链之后,我们还需要了解属性搜索机制,所谓的属性搜索机制,就是当我们访问对象上的一个属性时,我们如何找到这个属性值。首先,我们现在当前实例中查找该属性,如果找到了,返回该值,否则,通过__proto__找到原型对象,在原型对象中进行搜索,如果找到,返回该值,否则,继续向上进行搜索,直到找到该属性,或者在原型链中没有找到,返回undefined。

  根据《javascript高级程序设计》中,可以有六种继承方式,下面我们一一来介绍:

  1. 原型链

     // 父亲类
function Parent() {
this.value = 'value';
}
Parent.prototype.sayHi = function() {
console.log('Hi');
}
// 儿子类
function Child() { }
// 改变儿子的prototype属性为父亲的实例
Child.prototype = new Parent(); var child = new Child();
// 首先现在child实例上进行查找,未找到,
// 然后找到原型对象(Parent类的一个实例),在进行查找,未找到,
// 在根据__proto__进行找到原型,发现sayHi方法。 // 实现了Child继承
child.sayHi();

但是这种继承方式存在一个问题,那就是引用类型属性共享。

 // 父亲类
function Parent() {
this.color = ['pink', 'red'];
} // 儿子类
function Child() { }
Child.prototype = new Parent(); var child1 = new Child();
var child2 = new Child();
// 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2也会受到影响
console.log(child2.color); // ["pink", "red", "white"]

  它存在第二个问题,就是无法向父类种传参。

  2. 借用构造函数

  在这里,我们借用call函数可以改变函数作用域的特性,在子类中调用父类构造函数,复制父类的属性。此时没调用一次子类,复制一次。此时,每个实例都有自己的属性,不共享。同时我们可以通过call函数给父类传递参数。

  2.1 解决引用类型共享问题

     // 父亲类
function Parent(name) {
this.name = name;
this.color = ['pink', 'red'];
} // 儿子类
function Child() {
Parent.call(this); // 定义自己的属性
this.value = 'test';
} var child1 = new Child();
var child2 = new Child(); // 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2并没有受到影响
console.log(child2.color); // ["pink", "red"]

  2.2 解决传参数问题

     // 父亲类
function Parent(name) {
this.name = name;
this.color = ['pink', 'red'];
} // 儿子类
function Child(name) {
Parent.call(this, name); // 定义自己的属性
this.value = 'test';
} var child = new Child('qq');
// 将qq传递给Parent
console.log(child.name); // qq

  当时,上述方法也存在一个问题,共享的方法都在构造函数中定义,无法达到函数复用的效果。

  3. 组合继承

  根据上述两种方式,我们可以扬长避短,将需要共享的属性使用原型链继承的方法继承,将实例特有的属性,用借用构造函数的方式继承。

     // 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
// 借用构造函数继承
Parent.call(this); // 下面可以自己定义需要的属性
}
// 原型链继承
Child.prototype = new Parent(); var child1 = new Child();
var child2 = new Child(); // 每个实例特有的属性
// 先输出child1和child2种color的值
console.log(child1.color); // ["pink", "red"]
console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white
child1.color.push('white');
console.log(child1.color); // ["pink", "red", "white"]
// child1上的改动,child2并没有受到影响
console.log(child2.color); // ["pink", "red"] // 每个实例共享的属性
child1.sayHi(); // Hi
child2.sayHi(); // Hi

  上述方法,虽然综合了原型链和借用构造函数的优点,达到了我们想要的结果,但是它存在一个问题。就是创建一次实例时,两次调用了父类构造函数。

 // 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
Parent.call(this); // 第二次调用构造函数:在新对象上创建一个color属性
}
  
Child.prototype = new Parent(); // 第一次调用构造函数Child.prototype将会得到一个color属性,屏蔽了原型中的color属性。

  因此,出现了寄生组合式继承。在了解之前,我们先了解一下什么是寄生式继承。

  4. 寄生式继承

  同工厂模式类似,将我们需要继承的函数进行封装,然后进行某种增强,在返回对象。

     function Parent() {
this.color = ['pink', 'red'];
} function createAnother(o) {
// 获得当前对象的一个克隆
var another = new Object(o);
// 增强对象
o.sayHi = function() {
console.log('Hi');
}
// 返回对象
return another;
}

  5. 寄生组合式继承

     // 创建只继承原型对象的函数
function inheritPrototype(parent, child) {
// 创建一个原型对象副本
var prototype = new Object(parent.prototype);
// 设置constructor属性
prototype.constructor = child;
child.prototype = prototype;
} // 父亲类
function Parent() {
this.color = ['pink', 'red'];
}
Parent.prototype.sayHi = function() {
console.log('Hi');
} // 儿子类
function Child() {
Parent.call(this);
} inheritPrototype(Parent, Child);

  6. 原型式继承  

  思想:基于已有的对象创建对象。

     function createAnother(o) {
// 创建一个临时构造函数
function F() { }
// 将传入的对象作为它的原型
F.prototype = o;
// 返回一个实例
return new F();
}

js中实现继承的几种方式的更多相关文章

  1. js中原型继承的三种方式

  2. JavaScript学习12 JS中定义对象的几种方式【转】

    avaScript学习12 JS中定义对象的几种方式 转自:  http://www.cnblogs.com/mengdd/p/3697255.html JavaScript中没有类的概念,只有对象. ...

  3. JavaScript学习12 JS中定义对象的几种方式

    JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...

  4. JS中事件绑定的三种方式

    以下是搜集的在JS中事件绑定的三种方式.   1. HTML onclick attribute     <button type="button" id="upl ...

  5. js中声明Number的五种方式

    转载自:http://www.jb51.net/article/34191.htm <!DOCTYPE html> <html> <head> <meta c ...

  6. javascript中实现继承的几种方式

    javascript中实现继承的几种方式 1.借用构造函数实现继承 function Parent1(){ this.name = "parent1" } function Chi ...

  7. JS中检测数据类型的几种方式及优缺点【转】

    1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string&quo ...

  8. JS中检测数据类型的几种方式及优缺点

    1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string&quo ...

  9. JS中检测数据类型的四种方式及每个方式的优缺点

    //1.typeof 用来检测数据类型的运算符 //->typeof value //->返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number". ...

随机推荐

  1. mybatis入门-mapper代理原理

    原始dao层开发 在我们用mybatis开发了第一个小程序后,相信大家对于dao层的开发其实已经有了一个大概的思路了.其他的配置不用变,将原来的test方法,该为dao的方法,将原来的返回值,直接在d ...

  2. 腾讯优图及知脸(ZKface)人脸比对接口测试(python)

    一.腾讯优图 1.开发者地址:http://open.youtu.qq.com/welcome/developer 2.接入流程:按照开发者页面的接入流程接入之后,创建应用即可获得所需的AppID.S ...

  3. matlab中小技巧

    关于matlab中可能遇到的小知识点 一.字符串的比较 不能使用“==”,需要使用函数strcmp() %matlab中字符串的比较 %字符串比较要用strcmp.相同则返回1,不相同则返回0. cl ...

  4. 【吐血整理】svn命令行,Subversion的正确使用姿势~

    一.写在前面 前面一直博主一直用svn的桌面版本,但看项目经理一直都用的命令行方式,不为性能,还能直接装逼呀!在这里先感谢赵哥,也把它分享给感兴趣的你们~ 二.直接上干货 1. svn checkou ...

  5. 树莓派上搭建arduino开发环境

    -------------还是博客园上面的格式看这舒服,不去新浪了------------- 为什么要在树莓派上开发arduino呢?总要把树莓派用起来嘛,不然老吃灰. 树莓派使用SSH时没有图形界面 ...

  6. 转:微信开发之使用java获取签名signature(贴源码,附工程)

    微信开发之使用java获取签名signature(贴源码,附工程) 标签: 微信signature获取签名 2015-12-29 22:15 6954人阅读 评论(3) 收藏 举报  分类: 微信开发 ...

  7. Android中使用开源框架EventBus3.0实现Fragment之间的通信交互

    1.概述 在之前的博文中简单介绍过如何实现fragment之间的信息交互:<Android中Fragment与Activity之间的交互(两种实现方式)>,今天继续给大家介绍一种可以实现此 ...

  8. 对await(),notify()的理解

    await(),notify()是java Object类的方法.在两个线程同时访问一个对象的时候可以利用这2个方法实现线程的通信.看下面的例子. public class Account { pri ...

  9. Xcode的中常用到的快捷键,印象笔记中常用到的快捷键

    Xcode提供了很多快捷键,灵活使用快捷键可以提升开发效率.但对于初学者来说,一次性的去记住并掌握如此多的快捷键显然是不现实的,本文就是来帮助大家了解在iOS开发过程中,使用最频繁的一些快捷键. 1. ...

  10. jQuery给CheckBox全选与不全选

    $(function(){ $("#checkAll").click(function() {//全选 $('input[name="DATA"]').prop ...