ECMAscript只支持实现继承,主要是依靠原型链来实现的。

JavaScript实现继承的方式:

  • 类式继承
  • 构造函数继承
  • 组合继承
  • 寄生组合式继承

1.类式继承

 //类式继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(){
this.name = 'dog';
} //类式继承父类(父类实例作为子类的原型对象)
Dog.prototype = new Animal('white'); //子类实例继承父类的属性和方法
var dog1 = new Dog();
console.log(dog1.name); //dog
console.log(dog1.type); //[ 'pig', 'cat' ]
dog1.greet('wangwangwang'); //'wangwangwang' //缺陷1:修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
dog1.type.push('dog');
var dog2 = new Dog();
console.log(dog2.type); //[ 'pig', 'cat', 'dog' ] //缺陷2:无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数
//无法为不同dog赋值不同的颜色,所有dog只能同一种颜色
console.log(dog1.color); //'white'
console.log(dog2.color); //'white'
  原理说明:在实例化一个类时,新创建的对象复制了父类的构造函数内的属性与方法并且将原型__proto__指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法。
 类式继承的两个缺陷
  (1)修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
  (2)无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数

2.构造函数继承

 //构造函数继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(color, age){
Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
this.age = age;
} //子类实例继承父类的属性和方法
var dog1 = new Dog('white', 1);
var dog2 = new Dog('red', 2);
dog1.type.push('dog'); console.log(dog1.color); //'white'
console.log(dog1.type); //[ 'pig', 'cat', 'dog' ]
console.log(dog2.color); //'red'
console.log(dog2.type); //[ 'pig', 'cat' ] //缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
dog1.greet('wangwangwang'); //TypeError: dog1.greet is not a function
dog2.greet('wangwangwang'); //TypeError: dog2.greet is not a function

  构造函数继承方式可以避免类式继承的缺陷,在子类构造函数内部调用父类构造函数。通过使用apply()和call()方法可以改变函数的作用域,在新创建的对象上执行构造函数。

所以在上面的例子中,我们在Dog子类中调用这个方法也就是将Dog子类的变量在父类中执行一遍,这样子类就拥有了父类中的共有属性和方法。

  构造函数继承也是有缺陷的,那就是我们无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

//缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
dog1.greet('wangwangwang'); //TypeError: dog1.greet is not a function
dog2.greet('wangwangwang'); //TypeError: dog2.greet is not a function

3.组合继承

组合继承其实就是将类式继承和构造函数继承组合在一起:

 //类式继承和构造函数继承结合的组合继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(color, age){
Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
this.age = age;
} //类式继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog; //子类实例继承父类的属性和方法
var dog1 = new Dog('white', 1);
var dog2 = new Dog('red', 2);
dog1.type.push('dog'); console.log(dog1.color); //'white'
console.log(dog1.type); //[ 'pig', 'cat', 'dog' ]
console.log(dog2.color); //'red'
console.log(dog2.type); //[ 'pig', 'cat' ] //获取到父类的共有方法
dog1.greet("wang"); //'wang'
dog2.greet("miao"); //'miao' //缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用
  在上面的例子中,我们在子类构造函数中执行父类构造函数,在子类原型上实例化父类,这就是组合继承了,可以看到它综合了类式继承和构造函数继承的优点,同时去除了缺陷。组合继承是JavaScript中最常用的继承方式。 
  可能你会奇怪为什么组合式继承可以去除类式继承中的引用缺陷?其实这是由于原型链来决定的,由于JavaScript引擎在访问对象的属性时,会先在对象本身中查找,如果没有找到,才会去原型链中查找,如果找到,则返回值,如果整个原型链中都没有找到这个属性,则返回undefined。也就是说,我们访问到的引用类型(比如上面的type)其实是通过apply复制到子类中的,所以不会发生共享。
组合继承的缺陷就是它调用了两次父类的构造函数。
//缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用

4.寄生组合式继承

寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数。由于现在用的最多的还是组合继承,寄生组合式继承不做深入的探究,有兴趣可以 参考js高程P172 。

总结

  JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问父类型的所有属性和方法,这一点与基于类的继承很相似。

  原型链的问题对象实例共享所有继承的方法和属性,修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例,因此不适宜单独使用。

  解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用父类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。但是使用这种方式导致的结果就是无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

  使用最多的集成模式是组合继承方式,这种模式综合了类式继承和构造函数继承的优点,同时去除了缺陷。即使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。

参考:JavaScript实现继承的方式

JavaScript实现继承的方式和各自的优缺点的更多相关文章

  1. javascript 中继承实现方式归纳

    转载自:http://sentsin.com/web/1109.html 不同于基于类的编程语言,如 C++ 和 Java,javascript 中的继承方式是基于原型的.同时由于 javascrip ...

  2. javascript实现继承的方式

    this this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用a ...

  3. JavaScript的继承实现方式

    1.使用call或apply方法,将父对象的构造函数绑定在子对象上 function A(){ this.name = 'json'; } function B(){ A.call(this); } ...

  4. JavaScript 的继承与多态

    本文先对es6发布之前javascript各种继承实现方式进行深入的分析比较,然后再介绍es6中对类继承的支持以及优缺点讨论.最后介绍了javascript面向对象编程中很少被涉及的“多态”,并提供了 ...

  5. javascript实现继承的几种方式

    原型链方式实现继承 function SuperType(){ this.property = true; this.colors = ['red','blue','green']; } SuperT ...

  6. JavaScript各种继承方式和优缺点

    好久没写博客啦,嘻嘻,这个月是2017年的最后一个月啦,大家应该都开始忙着写年终总结了吧,嘻嘻,小颖今天给大家分享下Javascript中的几种继承方式以及他们的优缺点. 1.借助构造函数实现继承 原 ...

  7. DOM笔记(十三):JavaScript的继承方式

    在Java.C++.C#等OO语言中,都支持两种继承方式:接口继承和实现继承.接口继承制继承方法签名,实现继承则继承实际的方法和属性.在SCMAScript中,由于函数没有签名,所以无法实现接口继承, ...

  8. 【编程题与分析题】Javascript 之继承的多种实现方式和优缺点总结

    [!NOTE] 能熟练掌握每种继承方式的手写实现,并知道该继承实现方式的优缺点. 原型链继承 function Parent() { this.name = 'zhangsan'; this.chil ...

  9. javascript继承(七)—用继承的方式实现照片墙功能

    照片墙DEMO下载 注意:图片有四种类型:1可放大:2可拖动:3既可放大也可拖动:4都不行.由于每个图片的构造函数不同而不同(目前在火狐上调试的,其它的浏览器可能不行,请见谅,主要讲继承的思想.以后会 ...

随机推荐

  1. U-BOOT 对 Nand Flash 命令的支持

    U-BOOT 对 Nand Flash 命令的支持 在 U­BOOT 下对 Nand Flash 的支持主要是在命令行下实现对 nand flash 的操作.对 nand flash 实现的命令 为: ...

  2. vue中 给router-view 组件的 绑定 key 的原因

    不设置 router-view 的 key 属性 由于 Vue 会复用相同组件, 即 /page/1 => /page/2 或者 /page?id=1 => /page?id=2 这类链接 ...

  3. leetcode-第五场双周赛-1133-最大唯一数

    第一次提交: class Solution: def largestUniqueNumber(self, A: List[int]) -> int: dict = {} for i in A: ...

  4. 基于 RocketMQ 的同城双活架构在美菜网的挑战与实践

    本文整理自李样兵在北京站 RocketMQ meetup分享美菜网使用 RocketMQ 过程中的一些心得和经验,偏重于实践. 嘉宾李样兵,现就职于美菜网基础服务平台组,负责 MQ ,配置中心和任务调 ...

  5. Emmet 快捷支持

    1.https://docs.emmet.io/    [快捷demo视频演示] 2.https://docs.emmet.io/cheat-sheet/   [更多Emmet快捷案例示范]

  6. sshfs实践记录

    sshfs是一款利器,可以将远程linux服务器的路径通过ssh协议挂载到本地指定路径.本地的文件一改动,就自动同步到远程服务器中. 本次的实验在centos 6.9中进行. 1.下载.安装所有的依赖 ...

  7. Jmeter安装与配置(第一篇)

    Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域. 它可以用于测试静态和动态资源,例如静态文 ...

  8. RTNETLINK answers: File exists错误解决方法

    >一.写在前面: 因为是我刚学习linux好多问题需要解决,bolg仅作为记录自己的在技术这条道路上的点点滴滴. 二.事件起因: 最近因为女友的原因消沉的好长时间,在马哥那里的课程的结束到现在已 ...

  9. Leetcode139. Word Break单词拆分

    给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词. 说明: 拆分时可以重复使用字典中的单词. 你可以假设字典中没有重复 ...

  10. layui 表格点击图片放大

    表格 ,cols: [[ //表头 {checkbox: true,fixed: true} ,{type: 'numbers', title: 'ID', sort: true,width:80} ...