深入理解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 相关文章推荐
safari,opera嵌入iframe页面cookie读取问题解决方法
Jun 23 Javascript
前端开发的开始---基于面向对象的Ajax类
Sep 17 Javascript
如何减少浏览器的reflow和repaint
Feb 26 Javascript
使用Raygun来自动追踪AngularJS中的异常
Jun 23 Javascript
最全面的百度地图JavaScript离线版开发
Sep 10 Javascript
用Axios Element实现全局的请求loading的方法
Mar 15 Javascript
p5.js入门教程之鼠标交互的示例
Mar 16 Javascript
JavaScript面向对象的程序设计(犯迷糊的小羊)
May 27 Javascript
webpack项目使用eslint建立代码规范实现
May 16 Javascript
Vee-validate 父组件获取子组件表单校验结果的实例代码
May 20 Javascript
layer.confirm()右边按钮实现href的例子
Sep 27 Javascript
JS数组Reduce方法功能与用法实例详解
Apr 29 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中利用substr_replace将指定两位置之间的字符替换为*号
2011/01/27 PHP
JS日历 推荐
2006/12/03 Javascript
Javascript-Mozilla和IE中的一个函数直接量的问题
2007/01/09 Javascript
利用js实现选项卡的特别效果的实例
2013/03/03 Javascript
JS将光标聚焦在文本最后的实现代码
2014/03/28 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
2015/06/03 Javascript
javascript实现连续赋值
2015/08/10 Javascript
轻松学习jQuery插件EasyUI EasyUI创建树形菜单
2015/11/30 Javascript
javascript实现查找数组中最大值方法汇总
2016/02/13 Javascript
使用Javascript实现选择下拉菜单互移并排序
2016/02/23 Javascript
浅谈JS中的三种字符串连接方式及其性能比较
2016/09/02 Javascript
jQuery实现输入框邮箱内容自动补全与上下翻动显示效果【附demo源码下载】
2016/09/20 Javascript
JavaScript实现的select点菜功能示例
2017/01/16 Javascript
基于cookie实现zTree树刷新后展开状态不变
2017/02/28 Javascript
基于vue2.0动态组件及render详解
2018/03/17 Javascript
vue3.0 加载json的方法(非ajax)
2020/10/26 Javascript
十条建议帮你提高Python编程效率
2016/02/16 Python
python基础教程之分支、循环简单用法
2016/06/16 Python
Python基于回溯法子集树模板解决找零问题示例
2017/09/11 Python
python+PyQT实现系统桌面时钟
2020/06/16 Python
Python中的xlrd模块使用原理解析
2020/05/21 Python
通过实例解析python创建进程常用方法
2020/06/19 Python
Keras 数据增强ImageDataGenerator多输入多输出实例
2020/07/03 Python
Cotton On美国网站:澳洲时装连锁品牌
2016/10/25 全球购物
欧洲第一中国智能手机和平板电脑网上商店:CECT-SHOP
2018/01/08 全球购物
德国内衣、泳装和睡衣网上商店:Bigsize Dessous
2018/07/09 全球购物
法国女性内衣购物网站:Glamuse
2019/05/13 全球购物
能否解释一下XSS cookie盗窃是什么意思
2012/06/02 面试题
介绍一下Python中webbrowser的用法
2013/05/07 面试题
外科实习自我鉴定
2013/10/06 职场文书
总裁办公室主任职责
2014/01/02 职场文书
微型企业创业投资计划书
2014/01/10 职场文书
2015年中秋放假通知范文
2015/08/18 职场文书
2015年大学组织委员个人工作总结
2015/10/23 职场文书
团支部书记竞选稿
2015/11/21 职场文书
MySql学习笔记之事务隔离级别详解
2021/05/12 MySQL