深入理解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 相关文章推荐
10个新的最有前途的JavaScript框架
Mar 12 Javascript
JS模拟面向对象全解(二、类型与赋值)
Jul 13 Javascript
Egret引擎开发指南之创建项目
Sep 03 Javascript
深入理解JavaScript编程中的原型概念
Jun 25 Javascript
js文本框走动跑马灯效果代码分享
Aug 25 Javascript
原生JS获取元素集合的子元素宽度实例
Dec 14 Javascript
微信小程序getPhoneNumber获取用户手机号
Sep 29 Javascript
vue-cli构建项目使用 less的方法
Oct 04 Javascript
Vue2 SSR渲染根据不同页面修改 meta
Nov 20 Javascript
微信小程序实现滚动消息通知
Feb 02 Javascript
Vue 组件注册实例详解
Feb 23 Javascript
JavaScript 常见的继承方式汇总
Sep 17 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
ionCube 一款类似zend的PHP加密/解密工具
2010/07/25 PHP
php注销代码(session注销)
2012/05/31 PHP
基于empty函数的输出详解
2013/06/17 PHP
Zend Framework开发入门经典教程
2016/03/23 PHP
PHP 返回13位时间戳的实现代码
2016/05/13 PHP
php使用GD2绘制几何图形示例
2017/02/15 PHP
php高性能日志系统 seaslog 的安装与使用方法分析
2020/02/29 PHP
JS仿百度搜索自动提示框匹配查询功能
2013/11/21 Javascript
用C/C++来实现 Node.js 的模块(二)
2014/09/24 Javascript
最流行的Node.js精简型和全栈型开发框架介绍
2015/02/26 Javascript
JS排序方法(sort,bubble,select,insert)代码汇总
2016/01/30 Javascript
CSS3 media queries结合jQuery实现响应式导航
2016/09/30 Javascript
详谈js中数组(array)和对象(object)的区别
2017/02/27 Javascript
微信小程序 登录的简单实现
2017/04/19 Javascript
详细AngularJs4的图片剪裁组件的实例
2017/07/12 Javascript
javaScript封装的各种写法
2017/08/14 Javascript
微信小程序url与token设置详解
2017/09/26 Javascript
ReactNative 之FlatList使用及踩坑封装总结
2017/11/29 Javascript
小程序瀑布流组件实现翻页与图片懒加载
2020/05/19 Javascript
vue 解决data中定义图片相对路径页面不显示的问题
2020/08/13 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
微信小程序调用后台service教程详解
2020/11/06 Javascript
Django中的CACHE_BACKEND参数和站点级Cache设置
2015/07/23 Python
python中的字典使用分享
2016/07/31 Python
python机器学习之随机森林(七)
2018/03/26 Python
django加载本地html的方法
2018/05/27 Python
使用python-cv2实现Harr+Adaboost人脸识别的示例
2020/10/27 Python
拉飞逸官网:Lafayette 148 New York
2020/07/15 全球购物
总经理助理的八要求
2013/11/12 职场文书
促销活动策划方案
2014/01/12 职场文书
开学典礼决心书
2014/03/11 职场文书
大学生志愿者活动总结
2014/06/27 职场文书
学习十八届四中全会精神思想汇报
2014/10/23 职场文书
个人年底工作总结
2015/03/10 职场文书
Pycharm 如何设置HTML文件自动补全代码或标签
2021/05/21 Python
python脚本框架webpy模板控制结构
2021/11/20 Python