阿里巴巴技术文章分享 Javascript继承机制的实现


Posted in Javascript onJanuary 14, 2016

Javascript作为一门脚本语言,在设计之初并没有考虑到面向对象的特性。即便到了当今这个遍布现代浏览器的年代,各种Javascript 框架/库如雨后春笋般地疯狂生长,Javascript中连个 class 关键字都没有。如果你要编写一个类,你还得借助于function,至于继承、重载什么的,就别奢望了。

可是,没有继承,日子怎么过啊?难道把所有的共有逻辑都拷贝一遍,实现最低级的代码复用?

答案当然是——NO,所以,我们要自己实现继承!

目标

最关键的目标当然是继承——子类自动拥有父类的所有公共属性和方法。

支持instanceof,例如c是子类的实例,而P是父类,c instanceof P应该返回true。

其次应该能够重写(Override)父类的方法,并且在子类的方法中,能够方便地调用到父类的同名方法。

至于说重载(Overload),由于Javascript的语言特性(不可以有同名方法,即便它们参数列表不一样),无法实现。

设计与实现

Javascript的对象有一个很重要的属性——__proto__,也就是原型。原型实质上也是一个对象,所有它也可以有自己的原型,这样就形成一个原型链。当你调用某个对象的某个方法,或者读取该对象的某个属性,Javascript执行器是这样做的:

1、首先到该对象中找对应的方法或属性,如果找不到,
2、到该对象的原型中找,如果还找不到,
3、到原型的原型里面找
4、...
5、直到最后找到Object的原型为止,如果还没有则返回undefined
如下图所示:

阿里巴巴技术文章分享 Javascript继承机制的实现

原型链的这个特性,和继承很相似,所以自然而然,我们可以利用它来实现继承机制。而原型链对instanceof的支持,使得它成为很好的选择。

我们定义extend函数,这个函数接受两个参数,第一个是父类,第二个是子类,如下所示:

function extend(ParentClass, ChildClass) {
  ...
  return ChildClass;
}

这个函数对子类进行处理,并返回子类。处理的逻辑如下:

建立原型链

通过将子类的原型链与父类的原型链连接起来,子类可以自动拥有父类的方法和属性:

var pp = ParentClass.prototype,
  cp = ChildClass.prototype;
function T() {};
T.prototype = pp;
ChildClass.prototype = new T();

为了连接原型链,需要创建一个父类的实例,并将其赋给子类的原型属性。但我们不希望在extend方法里面就实例化父类,所以引入了一个中间类T,以解决这个问题。

实现重写

原型链建立之后,原来子类原型上的方法和属性我们也需要保留下来:

方法

如果父类有同名方法,我们使用一个闭包,来保留对父类方法和子类方法的引用。然后,修改新的原型中该方法的引用,将其指向一个新的 function。在这个function里面,我们创建一个临时属性super,将其指向父类方法,并调用子类方法,这样在子类方法中,通过 this.super可以调用该父类方法:

ChildClass.prototype[name] = (function(pm, cm) {
  return function() {
    var _super = this.super;
    this.super = pm;
    var result = cm.apply(this, arguments);
    this.super = _super;
    return result;
  };
})(pp[name], cp[name]);

属性

对于属性,不存在重写的问题,所以直接将子类原来的原型中的属性加到新的原型中即可:

ChildClass.prototype[name] = cp[name];

构造器

为了让子类能够访问到父类的构造器,我们将父类赋给子类的super属性:

ChildClass.super = ParentClass;

如何使用

假设我们要设计一个管理系统,里面涉及到客户、工人和经理等。将客户和员工的共性抽象出来,我们得到人(People);然后将工人和经理的共性抽象得到员工(Employee)。这样我们得到三级类结构:

阿里巴巴技术文章分享 Javascript继承机制的实现

实现这个设计的代码如下:

function People(firstname, lastname) {
  this.firstname = firstname;
  this.lastname = lastname;
}

function Employee(firstname, lastname, company) {
  Employee.super.apply(this, arguments);
  this.company = company;
}

function Manager(firstname, lastname, company, title) {
  Manager.super.apply(this, arguments);
  this.title = title;
}

我们希望对每个人都有一个描述,People是姓+名;员工在姓+名之后,还包括公司名称;而经理在员工的描述之后,还包括职位。代码如下:

People.prototype.summary = function() {
  return this.firstname + " " + this.lastname;
};

Employee.prototype.summary = function() {
  return this.super.call(this) + ", " + this.company;
};

Manager.prototype.summary = function() {
  return this.super.call(this) + ", " + this.title;
};

在所有的成员方法都已经定义好之后,声明类的继承(必须先定义方法,再声明类的继承,否则无法在方法中使用this.super调用父类方法!):

extend(People, Employee);
extend(Employee, Manager);

使用这些类就比较简单,直接new就好了:

var people = new People("Alice", "Dickens");
var employee = new Employee("Bob", "Ray", "Alibaba");
var manager = new Manager("Calvin", "Klein", "Alibaba", "Senior Manager");
console.log( people.summary() ); //Alice Dickens
console.log( employee.summary() ); //Bob Ray, Alibaba
console.log( manager.summary() ); //Calvin Klein, Alibaba, Senior Manager

这篇文章不错吧,那就给个赞吧!

Javascript 相关文章推荐
Javascript 面向对象 重载
May 13 Javascript
JavaScript ( (__ = !$ + $)[+$] + ({} + $)[_/_] +({} + $)[_/_] )
Feb 25 Javascript
js获取电脑分辨率的思路及操作
Nov 22 Javascript
js+flash实现的5图变换效果广告代码(附演示与demo源码下载)
Apr 01 Javascript
node.js基于mongodb的搜索分页示例
Jan 22 Javascript
基于Vue2.0的分页组件
Mar 16 Javascript
原生javascript实现的全屏滚动功能示例
Sep 19 Javascript
Vue.js 踩坑记之双向绑定
May 03 Javascript
详解JavaScript中typeof与instanceof用法
Oct 24 Javascript
Vue+Koa2 打包后进行线上部署的教程详解
Jul 31 Javascript
vue移动端使用canvas签名的实现
Jan 15 Javascript
原生JS封装vue Tab切换效果
Apr 28 Vue.js
AngularJS初始化静态模板详解
Jan 14 #Javascript
基于jQuery实现文本框只能输入数字(小数、整数)
Jan 14 #Javascript
jquery拖拽效果完整实例(附demo源码下载)
Jan 14 #Javascript
基于javascript实现随机颜色变化效果
Jan 14 #Javascript
JavaScript事件类型中UI事件详解
Jan 14 #Javascript
JavaScript事件 "事件对象"的注意要点
Jan 14 #Javascript
详解JavaScript中localStorage使用要点
Jan 13 #Javascript
You might like
php+jquery编码方面的一些心得(utf-8 gb2312)
2010/10/12 PHP
php图片处理:加水印、缩略图的实现(自定义函数:watermark、thumbnail)
2010/12/02 PHP
Laravel 5.0 发布 新版本特性详解
2015/02/10 PHP
JS打印gridview实现原理及代码
2013/02/05 Javascript
js showModalDialog参数的使用详解
2014/01/07 Javascript
动态显示可输入的字数提示还可以输入的字数
2014/04/01 Javascript
JavaScript设计模式之单件模式介绍
2014/12/28 Javascript
.NET微信公众号开发之创建自定义菜单
2015/07/16 Javascript
jQuery on()方法示例及jquery on()方法的优点
2015/08/27 Javascript
js实现首屏延迟加载实现方法 js实现多屏单张图片延迟加载效果
2017/07/17 Javascript
AngularJs+Bootstrap实现漂亮的计算器
2017/08/10 Javascript
js数字滑动时钟的简单实现(示例讲解)
2017/08/14 Javascript
微信小程序loading组件显示载入动画用法示例【附源码下载】
2017/12/09 Javascript
JS实现的DOM插入节点操作示例
2018/04/04 Javascript
JavaScript实现图片的放大缩小及拖拽功能示例
2019/05/14 Javascript
谈谈IntersectionObserver懒加载的具体使用
2019/10/15 Javascript
关于vue.js中实现方法内某些代码延时执行
2019/11/14 Javascript
[00:34]TI7不朽珍藏III——地穴编织者不朽展示
2017/07/15 DOTA
Linux下编译安装MySQL-Python教程
2015/02/02 Python
python引用DLL文件的方法
2015/05/11 Python
详解在Python程序中解析并修改XML内容的方法
2015/11/16 Python
python append、extend与insert的区别
2016/10/13 Python
浅析python的优势和不足之处
2018/11/20 Python
浅谈Python类中的self到底是干啥的
2019/11/11 Python
tensorflow获取预训练模型某层参数并赋值到当前网络指定层方式
2020/01/24 Python
Flask模板引擎Jinja2使用实例
2020/04/23 Python
浅谈keras保存模型中的save()和save_weights()区别
2020/05/21 Python
Python如何发送与接收大型数组
2020/08/07 Python
css3类选择器之结合元素选择器和多类选择器用法
2017/03/09 HTML / CSS
阿迪达斯印度官方商城:adidas India
2017/03/26 全球购物
ECCO爱步加拿大官网:北欧丹麦鞋履及皮具品牌
2017/07/08 全球购物
复古斯堪的纳维亚儿童服装:Baby go Retro
2017/09/09 全球购物
致跳远运动员加油稿
2014/02/11 职场文书
竞选班长演讲稿400字
2014/08/22 职场文书
党支部创先争优活动总结
2014/08/28 职场文书
《蓝鲸的眼睛》读后感5篇
2020/01/15 职场文书