阿里巴巴技术文章分享 Javascript继承机制的实现
作者:lijiao 发布时间:2024-04-30 09:59:16
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
如下图所示:
原型链的这个特性,和继承很相似,所以自然而然,我们可以利用它来实现继承机制。而原型链对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)。这样我们得到 * 类结构:
实现这个设计的代码如下:
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
这篇文章不错吧,那就给个赞吧!


猜你喜欢
- 使用mysqldump命令备份时候,--all-databases 可以备份所有的数据库。 使用ignore-table 还可以排除制定的表
- 还记得以前在工作中,将爬来的其它网站的数据导到xml。但是会遇到一个问题:即网页会有ascII的控制字符。一开始以为是别人为了防止采集而加入
- 最近在备考软考的软件设计师考试,在学习过程遇到很多于计算机基础计算相关的知识点,正好最近在学Go语言,所以就把计算的方式用Go语言实现一下。
- 正在看的ORACLE教程是:Oracle RMAN快速入门指南。前言: 这篇文章主要介绍RMAN的常用方法,其中包含了作者一些自己的经验,里
- 今天我们就从这个问题说起:临时表有哪些特征,适合哪些场景?这里,我需要先帮你厘清一个容易误解的问题:有的人可能会认为,临时表就是内存表。但是
- 本文实例讲述了Python实现程序的单一实例用法。分享给大家供大家参考。具体如下:这里先使用win32ui.FindWindow查找窗口名字
- 一、logging模块讲解1.函数:logging.basicConfig()参数讲解:(1)level代表高于或者等于这个值时,那么我们才
- Redis数据类型String:二进制安全,可以包含任何数据Hash:一个键值(key=>value)对集合List:简单的字符串列表
- 如下所示:df = df[df['cityname']==u'北京市']记得,如果用的python2,一定要
- 首先是抓包,使用scapy模块,sniff()函数 在其中参数为本地文件路径时,操作为打开本地文件若参数为BPF过滤规则和回调函数,则进行S
- 前言猪年除夕之夜在亲人群抢红包心血来潮,想用python做比较好玩的新年祝福给亲人们乐呵乐呵。奈何初学Python,底子比较薄,通过查阅相关
- 这一版,对虹软的功能进行了一些封装,添加了人脸特征比对,比对结果保存到文件,和从文件提取特征进行比对,大体功能基本都已经实现,可以进行下一步
- 介绍 append()语法list.append( element )参数element:任何类型的元素列表「末尾」添加元素nam
- rfind()方法返回所在子str 被找到的最后一个索引,或者-1,如果没有这样的索引不存在,可选择限制搜索字符串string[
- 前言ipaddress库提供了处理IPv4与IPv6网络地址的类。这些类支持验证,查找网络上的地址和主机,以及其他常见的操作。本篇,将详细介
- 发现pyautocad模块:可以用python控制autocad的包。今天把文档中的重点内容摘录出来,以后绘图、计算大工程量、或者识别施工图
- 目录1. 单变量 :=2. 多变量 :=3. 小结:=??Go 语言中 = 和 := 有什么区别1. 单变量 :=Go 语言中新增了一个特殊
- 首先mysql更新数据的某个字段,一般这样写:UPDATE mytable SET myfield = 'value' WH
- 如何一行输入多个数,并存入列表在python里,如果你仅使用input()的话是输入一行的内容并将该行的内容以字符串的形式存到变量中,但如果
- 1、range函数range(起始值,终点值,间隔)终点值不一定是最后一个取到的值得到的是range类的对象,最后用list转换为【】,In