首先我们了解,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. IOS任务管理之NSThread使用

    前言: 无论是Android还是IOS都会使用到多任务,多任务的最小单元那就是线程,今天学习总结一下IOS的NSThread使用. NSThread使用? 第一种方式实例化 //selector :线 ...

  2. java流和文件 保存字节级数据(写)

    重要的知识点: 流的概念:  从数据源到I/O类的输入流(in)    从I/O类到数据接收器的输出流(out) I/O包含子类较多的有四大家族:InputStream,OutputStream,Re ...

  3. Fragment 学习笔记(1)

    网上关于Fragment相关的博客资料很多,写关于这个知识笔记是加深记忆,大神略过: 0x01 了解Fragment 当然看官方文档(http://www.android-doc.com/refere ...

  4. 随机生成并排序 C,去同,有序数组合并排序

    #include<iostream> #include<stdlib.h> #include<time.h> using namespace std; int ma ...

  5. JS 基础学习随想

    2012年就已经接触过了js,给我的印象:这是一门谈不上复杂的语言.大概这就是所谓的学的越浅,用的越少,觉得自己会的东西好像得更多吧!开始做基础练习题的时候觉得好像都十分简单.可是后来在做到对象数组的 ...

  6. IOS8 : UIAlertController

    UIAlertController 和  UIAlertAction 用法: 1. 最简单的提醒视图: 这里我们实现一个最简单的提醒视图,包含1个标题,1行信息,1个按键,按下按键后,什么都不发生: ...

  7. nodemailer中的几个坑

    nodemailer是什么 nodemailer是一个nodejs的邮件服务模块 如何用nodemailer发邮件 1.先安装nodemailer npm i --save nodemailer 2. ...

  8. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

  9. TP框架数据库操作(增删改)

    首先选择一张表,对其进行操作: 对数据库操作之前首先要创建模型: $n = M("account"); 数据库添加数据: 1.使用数组: 1.使用数组 $arr = array(& ...

  10. 每天一个Linux命令 5

    命令名称:touch 功能描叙:创建空文件 格式:touch  文件名 范例:$touch japan.list(当前路径创建) $touch  /root/japan.list(指定路径创建) $t ...