深入理解JavaScript继承的多种方式和优缺点


Posted in Javascript onMay 12, 2017

写在前面

本文讲解JavaScript各种继承方式和优缺点。

注意:

跟《JavaScript深入之创建对象》一样,更像是笔记。

哎,再让我感叹一句:《JavaScript高级程序设计》写得真是太好了!

1.原型链继承

function Parent () {
  this.name = 'kevin';
}

Parent.prototype.getName = function () {
  console.log(this.name);
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

console.log(child1.getName()) // kevin

问题:

1.引用类型的属性被所有实例共享,举个例子:

function Parent () {
  this.names = ['kevin', 'daisy'];
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy", "yayu"]

2.在创建 Child 的实例时,不能向Parent传参

2.借用构造函数(经典继承)

function Parent () {
  this.names = ['kevin', 'daisy'];
}

function Child () {
  Parent.call(this);
}

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy"]

优点:

1.避免了引用类型的属性被所有实例共享

2.可以在 Child 中向 Parent 传参

举个例子:

function Parent (name) {
  this.name = name;
}

function Child (name) {
  Parent.call(this, name);
}

var child1 = new Child('kevin');

console.log(child1.name); // kevin

var child2 = new Child('daisy');

console.log(child2.name); // daisy

缺点:

方法都在构造函数中定义,每次创建实例都会创建一遍方法。

3.组合继承

原型链继承和经典继承双剑合璧。

function Parent (name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
  console.log(this.name)
}

function Child (name, age) {

  Parent.call(this, name);
  
  this.age = age;

}

Child.prototype = new Parent();

var child1 = new Child('kevin', '18');

child1.colors.push('black');

console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]

var child2 = new Child('daisy', '20');

console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。

4.原型式继承

function createObj(o) {
  function F(){}
  F.prototype = o;
  return new F();
}

就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。

缺点:

包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。

var person = {
  name: 'kevin',
  friends: ['daisy', 'kelly']
}

var person1 = createObj(person);
var person2 = createObj(person);

person1.name = 'person1';
console.log(person2.name); // kevin

person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

注意:修改person1.name的值,person2.name的值并未发生改变,并不是因为person1person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。

5. 寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

function createObj (o) {
  var clone = object.create(o);
  clone.sayName = function () {
    console.log('hi');
  }
  return clone;
}

缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

6. 寄生组合式继承

为了方便大家阅读,在这里重复一下组合继承的代码:

function Parent (name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
  console.log(this.name)
}

function Child (name, age) {
  Parent.call(this, name);
  this.age = age;
}

Child.prototype = new Parent();

var child1 = new Child('kevin', '18');

console.log(child1)

组合继承最大的缺点是会调用两次父构造函数。

一次是设置子类型实例的原型的时候:

Child.prototype = new Parent();

一次在创建子类型实例的时候:

var child1 = new Child('kevin', '18');

回想下 new 的模拟实现,其实在这句中,我们会执行:

Parent.call(this, name);

在这里,我们又会调用了一次 Parent 构造函数。

所以,在这个例子中,如果我们打印 child1 对象,我们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue', 'green']。

那么我们该如何精益求精,避免这一次重复调用呢?

如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?

看看如何实现:

function Parent (name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
  console.log(this.name)
}

function Child (name, age) {
  Parent.call(this, name);
  this.age = age;
}

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();


var child1 = new Child('kevin', '18');

console.log(child1);

最后我们封装一下这个继承方法:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

function prototype(child, parent) {
  var prototype = object(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

// 当我们使用的时候:
prototype(Child, Parent);

引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:

这种方式的高效率体现它只调用了一次Parent构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript Event学习第五章 高级事件注册模型
Feb 07 Javascript
javascript跨域的4种方法和原理详解
Apr 08 Javascript
html的DOM中document对象forms集合用法实例
Jan 21 Javascript
jQuery实现表格行上移下移和置顶的方法
May 22 Javascript
javascript判断并获取注册表中可信任站点的方法
Jun 01 Javascript
jQuery文字横向滚动效果的实现代码
May 31 Javascript
微信小程序使用第三方库Underscore.js步骤详解
Sep 27 Javascript
微信小程序 icon组件详细及实例代码
Oct 25 Javascript
jQuery事件绑定方法学习总结(推荐)
Nov 21 Javascript
jQuery实现滚动条滚动到子元素位置(方便定位)
Jan 08 Javascript
简单的jQuery拖拽排序效果的实现(增强动态)
Feb 09 Javascript
微信小程序实现签到功能
Oct 31 Javascript
JS实现图片预加载之无序预加载功能代码
May 12 #Javascript
详解React开发中使用require.ensure()按需加载ES6组件
May 12 #Javascript
vue学习笔记之指令v-text && v-html && v-bind详解
May 12 #Javascript
JS常用正则表达式总结【经典】
May 12 #Javascript
vue.js的安装方法
May 12 #Javascript
JS匹配日期和时间的正则表达式示例
May 12 #Javascript
js如何获取网页所有图片
May 12 #Javascript
You might like
PHP中路径问题的解决方案
2006/10/09 PHP
使用Xdebug调试和优化PHP程序之[1]
2007/04/17 PHP
php 网页游戏开发入门教程一(webgame+design)
2009/10/26 PHP
PHP 反向排序和随机排序代码
2010/06/30 PHP
php 传值赋值与引用赋值的区别
2010/12/29 PHP
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
2012/06/07 PHP
Yii2分页的使用及其扩展方法详解
2016/05/23 PHP
php实现姓名根据首字母排序的类与方法(实例代码)
2018/05/16 PHP
Ruffy javascript 学习笔记
2009/11/30 Javascript
用Javascript实现Sleep暂停功能代码
2010/09/03 Javascript
jQuery学习笔记之Helloworld
2010/12/22 Javascript
5秒后跳转到另一个页面的js代码
2013/10/12 Javascript
JS获取当前日期时间并定时刷新示例
2021/03/04 Javascript
jquery让指定的元素闪烁显示的方法
2015/03/17 Javascript
超实用的JavaScript表单代码段
2016/02/26 Javascript
Node.js编写爬虫的基本思路及抓取百度图片的实例分享
2016/03/12 Javascript
javascript中JSON.parse()与eval()解析json的区别
2016/05/19 Javascript
EasyUI Pagination 分页的两种做法小结
2016/07/09 Javascript
Vue框架之goods组件开发详解
2018/01/25 Javascript
Vue使用Proxy代理后仍无法生效的解决
2020/11/13 Javascript
Python读写ini文件的方法
2015/05/28 Python
深入理解 Python 中的多线程 新手必看
2016/11/20 Python
python迭代dict的key和value的方法
2018/07/06 Python
python读取图片任意范围区域
2019/01/23 Python
pyqt5之将textBrowser的内容写入txt文档的方法
2019/06/21 Python
python查找特定名称文件并按序号、文件名分行打印输出的方法
2020/04/24 Python
python中的时区问题
2021/01/14 Python
加拿大当代时尚服饰、配饰和鞋类专业零售商和制造商:LE CHÂTEAU
2017/10/06 全球购物
亚洲航空公司官方网站:AirAsia
2019/11/25 全球购物
医院辞职信范文
2014/01/17 职场文书
四风问题自查报告剖析材料
2014/02/08 职场文书
工业自动化专业自荐信范文
2014/04/10 职场文书
党支部三会一课计划
2014/09/24 职场文书
教师工作态度自我评价
2015/03/05 职场文书
python实现自动化群控的步骤
2021/04/11 Python
SQL Server删除表中的重复数据
2022/05/25 SQL Server