首先我们了解,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. foreach和for循环的区别

    for循环 for循环,通过下标,对循环中的代码反复执行,功能强大,可以通过index取得元素.在处理比较复杂的处理的时候较为方便. foreach循环 foreach,从头到尾,对于集合中的对象遍历 ...

  2. Phoenix和SQuirrel安装详解

    Phoenix安装详解 描述 现有hbase的查询工具有很多如:Hive,Tez,Impala,Shark/Spark,Phoenix等.今天的主角是Phoenix. phoenix,中文译为“凤凰” ...

  3. java-7继承

    请自行编写代码测试以下特性(动手动脑):在子类中,若要调用父类中被覆盖的方法,可以使用super关键字. public class QWE {    public void main(String[] ...

  4. 聊聊dmClock算法

    作者:吴香伟 发表于 2017/01/08 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 人们常常容易忽略一些不起眼但特别重要的事物.曾经跟同事聊Python, ...

  5. 图片转换PDF

    组件在我的文件里,需要的可以找找. public partial class MainForm : Form { private string srcFile, destFile; bool succ ...

  6. Hadoop基本开发环境搭建(原创,已实践)

    软件包: hadoop-2.7.2.tar.gz hadoop-eclipse-plugin-2.7.2.jar hadoop-common-2.7.1-bin.zip eclipse  jdk1.8 ...

  7. IOS高级开发~开机启动&无限后台运行&监听进程

    一般来说, IOS很少给App后台运行的权限. 仅有的方式就是 VoIP. IOS少有的为VoIP应用提供了后台socket连接,定期唤醒并且随开机启动的权限.而这些就是IOS上实现VoIP App的 ...

  8. 【排序算法】快速排序算法 Java实现

    快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod). 基本思想 先从数组中找出一个数作为基 ...

  9. 【转】Jqgrid学习之ColModel API

    ColModel 是jqGrid里最重要的一个属性,设置表格列的属性. 属性 数据类型 备注 默认值 align string left, center, right. left classes st ...

  10. PostgreSQL指南

    PostgreSQL指南 历史简介 最近几年Postgres的关注度变得越来越高. 它加快了Postgres的发展步伐, 与此同时其他 的关系数据库系统的发展放缓. 在数据库领域中 Postgre S ...