深入理解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实现拼音排序的方法
Nov 20 Javascript
鼠标滚轮控制网页横向移动实现思路
Mar 22 Javascript
Extjs优化(一)删除冗余代码提高运行速度
Apr 15 Javascript
JsRender实用入门教程
Oct 31 Javascript
jquery果冻抖动效果实现方法
Jan 15 Javascript
jQuery实现仿百度首页滑动伸缩展开的添加服务效果代码
Sep 09 Javascript
12种JavaScript常用的MVC框架比较分析
Nov 16 Javascript
JavaScript中boolean类型之三种情景实例代码
Nov 21 Javascript
JS 循环li添加点击事件 (闭包的应用)
Dec 10 Javascript
JavaScript链式调用实例浅析
Dec 19 Javascript
layui监听工具栏的实例(操作列表按钮)
Sep 10 Javascript
Vue开发中常见的套路和技巧总结
Nov 24 Vue.js
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
超神学院:天使彦公认最美的三个视角,网友:我的天使快下凡吧!
2020/03/02 国漫
DEDECMS首页调用图片集里的多张图片
2015/06/05 PHP
WordPress中用于获取文章信息以及分类链接的函数用法
2015/12/18 PHP
php使用GD2绘制几何图形示例
2017/02/15 PHP
几行代码轻松实现PHP文件打包下载zip
2017/03/01 PHP
刷新时清空文本框内容的js代码
2007/04/23 Javascript
JavaScript 面向对象的 私有成员和公开成员
2010/05/13 Javascript
javascript基础知识大集锦(一) 推荐收藏
2011/01/13 Javascript
js解析与序列化json数据(二)序列化探讨
2013/02/01 Javascript
JS实现可改变列宽的table实例
2013/07/02 Javascript
理运用命名空间让js不产生冲突避免全局变量的泛滥
2014/06/15 Javascript
node.js中的url.parse方法使用说明
2014/12/10 Javascript
在Python中使用glob模块查找文件路径的方法
2015/06/17 Javascript
jQuery遍历json的方法分析
2016/04/16 Javascript
BootStrap日期控件在模态框中选择时间下拉菜单无效的原因及解决办法(火狐下不能点击)
2016/08/18 Javascript
JavaScript代码里的判断小结
2016/08/22 Javascript
node.js中的事件处理机制详解
2016/11/26 Javascript
浅谈Vuex的状态管理(全家桶)
2017/11/04 Javascript
Vue动态生成el-checkbox点击无法赋值的解决方法
2019/02/21 Javascript
Vue+Element实现表格编辑、删除、以及新增行的最优方法
2019/05/28 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
[55:39]DOTA2-DPC中国联赛 正赛 VG vs LBZS BO3 第二场 1月19日
2021/03/11 DOTA
[01:08:24]DOTA2-DPC中国联赛 正赛 RNG vs Phoenix BO3 第一场 2月5日
2021/03/11 DOTA
Python中join和split用法实例
2015/04/14 Python
pycharm设置鼠标悬停查看方法设置
2019/07/29 Python
python 中的9个实用技巧,助你提高开发效率
2020/08/30 Python
详解HTML5如何使用可选样式表为网站或应用添加黑暗模式
2020/04/07 HTML / CSS
英国健身仓库:Bodybuilding Warehouse
2019/03/06 全球购物
大学生个人求职信范文
2013/09/21 职场文书
打架检讨书800字
2014/01/10 职场文书
《鲁班和橹板》教学反思
2014/04/27 职场文书
俞敏洪励志演讲稿
2014/04/29 职场文书
外贸会计专业自荐信
2014/06/22 职场文书
乡镇三严三实学习心得体会
2014/10/13 职场文书
承诺函格式模板
2015/01/21 职场文书
酒店优秀员工推荐信
2015/03/24 职场文书