深入理解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做flash做的事..才完成的一个类.Auntion Action var 0.1
Feb 23 Javascript
ExtJs3.0中Store添加 baseParams 的Bug
Mar 10 Javascript
jQuery选中select控件 无法设置selected的解决方法
Sep 01 Javascript
8个实用的jQuery技巧
Mar 04 Javascript
浅谈如何实现easyui的datebox格式化
Jun 12 Javascript
深入浅析search 搜索框的写法
Aug 02 Javascript
深入理解jQuery3.0的domManip函数
Sep 01 Javascript
vue.js选中动态绑定的radio的指定项
Jun 02 Javascript
JavaScript中的E-mail 地址格式验证
Mar 28 Javascript
JavaScript实现的反序列化json字符串操作示例
Jul 18 Javascript
解决JavaScript layui 下拉框不显示的问题
Aug 14 Javascript
使用vue-cli3 创建vue项目并配置VS Code 自动代码格式化 vue语法高亮问题
May 14 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无限分类源码分享(思路不错)
2011/10/13 PHP
PHP实现一维数组转二维数组的方法
2015/02/25 PHP
php中的异常和错误浅析
2017/05/03 PHP
身份证号码前六位所代表的省,市,区, 以及地区编码下载
2007/04/12 Javascript
List Information About the Binary Files Used by an Application
2007/06/18 Javascript
javascript实现的基于金山词霸网络翻译的代码
2010/01/15 Javascript
js 实现复制到粘贴板的功能代码
2010/05/13 Javascript
javascript动态加载二
2012/08/22 Javascript
JavaScript清空数组元素的两种方法简单比较
2015/07/10 Javascript
jquery实现动静态条形统计图
2015/08/17 Javascript
浅谈JavaScript对象的创建方式
2016/06/13 Javascript
nodejs multer实现文件上传与下载
2017/05/10 NodeJs
利用Angular.js编写公共提示模块的方法教程
2017/05/28 Javascript
vue mint-ui学习笔记之picker的使用
2017/10/11 Javascript
js通过Date对象实现倒计时动画效果
2017/10/27 Javascript
js+html获取系统当前时间
2017/11/10 Javascript
详解iframe跨域的几种常用方法(小结)
2019/04/29 Javascript
Vue filter 过滤当前时间 实现实时更新效果
2019/12/20 Javascript
JavaScript实现字符串与HTML格式相互转换
2020/03/17 Javascript
[43:51]2018DOTA2亚洲邀请赛3月30日 小组赛B组 EG VS Secret
2018/03/31 DOTA
python中实现迭代器(iterator)的方法示例
2017/01/19 Python
Windows系统下多版本pip的共存问题详解
2017/10/10 Python
python3.6+django2.0开发一套学员管理系统
2018/03/03 Python
pyqt5的QComboBox 使用模板的具体方法
2018/09/06 Python
基于python实现KNN分类算法
2020/04/23 Python
python模拟菜刀反弹shell绕过限制【推荐】
2019/06/25 Python
Python Socket TCP双端聊天功能实现过程详解
2020/06/15 Python
DBA数据库管理员JAVA程序员架构师必看
2016/02/07 面试题
幼儿园教师教育感言
2014/02/28 职场文书
培训班开班仪式主持词
2014/03/28 职场文书
寒假家长评语大全
2014/04/16 职场文书
《鲁班和橹板》教学反思
2014/04/27 职场文书
2014国庆节主题活动方案:快乐的国庆节
2014/09/16 职场文书
文艺部部长竞选稿
2015/11/21 职场文书
医院岗前培训心得体会
2016/01/08 职场文书
Vue elementUI表单嵌套表格并对每行进行校验详解
2022/02/18 Vue.js