首先我们了解,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. ssh无法远程登陆别的机器

    ssh无法远程登陆别的机器,提示报错: ssh: symbol lookup error: ssh: undefined symbol: EVP_aes_128_ctr 解决方法如下: 给相应配置文件 ...

  2. JDBC连接数据库(二)——连接池

    参考博客:http://www.cnblogs.com/xdp-gacl/p/4002804.html 一. 应用程序直接建立数据库连接模型 应用程序直接每次访问数据库时,都建立创建一个数据库的链接, ...

  3. css之line-height

    行内框盒子模型1."内容区域"(content area)2."内联盒子"(inline boxes)3."行框盒子"(line boxes ...

  4. Lucky7(容斥原理)

    Problem Description When ?? was born, seven crows flew in and stopped beside him. In its childhood, ...

  5. MongoDB【第一篇】安装

    第一步:准备 1. 操作系统 CentOS-7-x86_64-Everything-1511 2. MongoDB 版本 mongodb-linux-x86_64-rhel70-3.4.2 3. 设置 ...

  6. 百度网盘免费扩容 免费扩容到2048G

    您可以用手机端扩容 http://yun.baidu.com/1t 免费扩容到2048G

  7. 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap

    1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...

  8. SpringMvc的运行流程

    一.先用文字描述 1.用户发送请求到DispatchServlet 2.DispatchServlet根据请求路径查询具体的Handler 3.HandlerMapping返回一个HandlerExc ...

  9. 《类型编程晋级——shapeless类库使用指南》前言及第一章翻译

    从年初开始进行此项工作,我和合作伙伴包亮付出了大量而艰辛的劳动,现基本翻译完毕,有出版意向,如果有意向欢迎联系,不甚感激!也欢迎各位博友对此翻译提出意见建议以及指导如何出版,在此谢过! 前言 时间回到 ...

  10. 理解margin负值

    效果 上和左方的margin负值使元素向上和左方向移动,如果该元素position不是absolute或fixed,这还会导致之后的元素也向上,左移 下和右方的margin负值会缩小下和右方的空间,使 ...