原文地址:
http://ejohn.org/blog/simple-javascript-inheritance/
译者注:cocos2d-html5使用了这种类继承方式
简单的JavaScript继承
关于JavaScript继承,最近我做了很多事,主要是为了我的JavaScript书能顺利出版,同时也检验了多种JavaScript经典继承方式的模拟技术。其中,我最感兴趣的是base2(
http://code.google.com/p/base2/)和Prototype(
http://prototypejs.org/)。
我想把这几种技术的精华以简单、可复用、容易理解并没有任何依赖的形式展现出来。并且,我希望结果简单而高可用。下面是一个例子:
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
},
dance: function(){
return this.dancing;
}
});
var Ninja = Person.extend({
init: function(){
this._super( false );
},
dance: function(){
//调用dance()的继承版本
return this._super();
},
swingSword: function(){
return true;
}
});
var p = new Person(true);
p.dance(); // => true
var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true
//应该都是true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class
接下来对这种实现方式做一些解释:
1.创建构造函数很简单(本例中通过使用init方法实现)
2.为了创建一个新的“类”,你需要extend一个已经存在的类
3.所有类继承自同一祖先:Class。所以,如果你想创建一个新类,那它必须是Class的子类
4.最有挑战的是:这里提供了重载方法实现。参考上面的代码,可以通过在init()和dance()中调用this._super()实现。
我对结果很满意:它强制类为一个结构,维护简单的继承,并且可以调用父类方法。
简单的类创建和继承
下面是具体实现(做了格式化并添加了注释)——大概25行,欢迎反馈。
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// 受base2和Prototype启发
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
//基类实现(什么都没做)
this.Class = function(){};
//创建一个新类,继承自class
Class.extend = function(prop) {
var _super = this.prototype;
// 初始化一个基类(但是只创建了实例,没有调用init构造函数)
initializing = true;
var prototype = new this();
initializing = false;
//将属性复制到新的prototype
for (var name in prop) {
//检查是否覆盖了已有的方法
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// 在父类增加一个相同的._super()方法
this._super = _super[name];
//方法只是临时绑定,所以执行后把它移除了
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
//构造函数
function Class() {
//所有构造过程在init方法中完成
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
//我们构造的protype对象
Class.prototype = prototype;
//强制构造函数为我们需要的内容
Class.prototype.constructor = Class;
//然后使该类可继承
Class.extend = arguments.callee;
return Class;
};
})();
我觉得最有技巧的地方是“初始化/不要调用init”和“创建_super方法”。我想简要地介绍一下这些部分,这样你就可以更好地理解我们在这个方法中实现了什么。
初始化
为了使用一个函数prototype模拟继承,我们使用传统的技术来创建父类方法的实例,并且把它赋给prototype。不使用以上技术它应该看起来像这样:
function Person(){}
function Ninja(){}
Ninja.prototype = new Person();
//允许instanceof可用
(new Ninja()) instanceof Person
然而挑战在哪里,我们需要的就是“instanceof”的好处吗,这不值得初始化一个Person对象并运行它的构造函数。为了抵消损失,我在代码中用了一个变量,initializing,在我们仅想初始化一个类用于prototype时,设置为true。
if ( !initializing )
this.init.apply(this, arguments);
尤其重要的是init方法可以运行任何耗费资源的启动代码(连接服务器,创建DOM对象,谁知道呢)可以有效避免无法正常工作的。
Super方法
当你继承的时候,创建一个类,然后从父类继承一些功能,大家最希望有的功能就是可以访问被覆盖的父类方法。最终,在这个实现方案中是通过一个临时方法(._super)来实现的,它只能在父类方法中访问,引用父类相关的方法。
例如,如果你想调用父类的构造函数,你可以使用以下技术实现。
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
}
});
var Ninja = Person.extend({
init: function(){
this._super( false );
}
});
var p = new Person(true);
p.dancing; // => true
var n = new Ninja();
n.dancing; // => false
实现该功能需要多步操作。首先,用来扩展已存在类(例如传递给Person.extend的这个)的对象需要合并到基类new Person实例中(构建过程前面我们讲了)。在合并过程中,我们做个简单的检查:我们要合并的属性是个方法吗,我们要覆盖的也是个方法吗?如果是这样,我们需要找到一种方法使父类方法可用。
我们创建了一个匿名的闭包(返回一个方法)来封装新的加强后的父类方法。我们需要保存原来的this._super(不管是否真的存在)的引用,完成后再恢复。这样会帮助我们解决同名变量已经存在的问题(而不是意外地弄丢它)。
接下来,我们创建一个新的_super方法,只是对父类prototype已经存在方法的一个引用。谢天谢地,我们不用再做其他改变或者修改作用域之类的,如果它是我们对象的一个属性,功能的上下文会自动设置(this是指与父类相对的实例)。
最后,我们调用最开始的方法,它做自己的事(很可能也是利用_super),之后我们恢复_super到原始状态并从方法返回。
现在有很多方法实现上面的类似功能(我见过另外一种实现方法是将super方法绑定到方法本身,从过arguments.callee访问),但是我认为这种方法实现了可用性和简单性最好的结合。
我会在我的书中深入介绍JavaScript prototype的本质,但是我希望把这个类实现方式单独剥离出来分享给大家。在简化代码上还有很多可以讨论的地方(容易学,容易扩展,下载量下),所以这个实现是个很好的开始用来学习JavaScript类的创建和继承。
分享到:
相关推荐
此外,JavaScript的面向对象编程(OOP)概念,如类、构造函数、原型链、继承、封装和多态,也是JavaScript进阶学习的重点。Lingoes的翻译功能使得这些复杂的概念能以中文的形式呈现,便于理解。 在实际应用中,...
此外,原型继承是JavaScript中实现面向对象编程的一种方式,而ES6引入的类和模块系统则让JavaScript的面向对象编程更加符合传统的类继承模型。 总的来说,JavaScript是一种强大且灵活的语言,不仅限于网页开发,还...
### 外文翻译:学用JavaScript设计模式 #### 序言与重要性 设计模式作为软件工程中的一个重要组成部分,其核心在于提供了一套标准的方法论,帮助开发人员以优雅、高效的方式解决常见的软件设计问题。《学用...
在JavaScript中,虽然传统意义上没有类的概念,但它支持基于原型(Prototype)的继承机制,也支持使用ES6引入的类语法来更好地支持面向对象编程。 面向对象编程具有几个核心概念,包括封装(Encapsulation)、继承...
原型链和原型继承是JavaScript实现面向对象编程的关键机制。 **5. 数组** 数组是有序元素的集合,可以存储任意类型的值。JavaScript提供了多种操作数组的方法,如`push`、`pop`、`shift`、`unshift`、`splice`、`...
《JavaScript权威指南中文第6版》是JavaScript编程领域的一本经典著作,由淘宝前端团队精心翻译,确保了内容的准确性和专业性。本书全面覆盖了JavaScript语言的各种核心概念和技术,对于初学者和经验丰富的开发者来...
《JavaScript 权威指南》是JavaScript编程领域的一本经典之作,中文第六版由淘宝前端团队翻译,为读者提供了全面、深入的JavaScript知识体系。这本书详细介绍了JavaScript的核心语法、对象、函数、类、DOM操作、BOM...
Zakas所著的JavaScript领域的专业书籍,是《Professional JavaScript for Web Developers》第三版的中文翻译版。这本书旨在帮助读者深入理解JavaScript编程语言,从基础到高级特性,再到最佳实践,涵盖了JavaScript...
Secrets of the JavaScript Ninja(翻译:JavaScript忍者禁术)适用有JS基础的,这本书以函数为中心(函数也确实是js的核心),对函数的讲解非常全面细致,涉及到函数的定义、扩展、重载、curry化、闭包、重定向、继承...
《JavaScript高级程序设计》是学习这一语言的经典教材,现在已经更新到第三版,并提供了完整的中文翻译,对于国内开发者来说,无疑是一份宝贵的资源。这本书深入浅出地讲解了JavaScript的核心概念和高级特性,帮助...
javascript中的继承有三种(不同的书上对这三中的名称翻译都不一样,我按照我的理解来命名吧): a.构造函数继承(类式继承,组合继承,伪经典继承) b.原型继承 c.原型赋值(遍历)继承(寄生式继承) 2.构造函数...
《JavaScript权威指南(第6版)》是JavaScript学习者必读的经典著作,由淘宝前端团队精心翻译,确保了中文版本的准确性和权威性。这本书全面深入地探讨了JavaScript语言的各个方面,包括其语法特性、对象模型、DOM...
尽管实现类继承模型在JavaScript上相对容易,但要在JavaScript中实现原型继承则复杂得多。 在JavaScript中,对象是数据类型,并且可以作为哈希表使用,保存命名的键与值的对应关系。对象可以通过对象字面量(使用{}...
《JavaScript宝典(第6版)》是一本深入探讨JavaScript编程语言的重要著作,由Danny Goodman和Michael Morrison共同撰写,并由张文波翻译成中文。这本书是JavaScript开发者和学习者的宝贵资源,涵盖了广泛的JavaScript...
众多贡献者,包括Caio Romão(拼写校对)、Andreas Blixt(语言修正),以及中文翻译三生石上,共同使得这份文档得以完善并广泛传播。根据MIT许可协议,文档在GitHub上开放,鼓励社区成员参与修正和改进。 #### ...
中文第四版是针对这个备受赞誉的指南的最新中文翻译,旨在帮助中国读者深入理解和掌握JavaScript这门强大的脚本语言。 这本书详细介绍了JavaScript的核心概念和语法,包括数据类型、变量、操作符、语句、函数、对象...
类是面向对象编程的核心概念之一,在JavaScript中,虽然它不是原生支持的,但可以通过构造函数和原型继承来实现。 ##### 1. 封装 确保类的内部状态被良好地封装,并通过公共方法暴露对外的行为。 ##### 2. 继承 ...
本人一行注释一行代码翻译了该大师的艺术作品--目的说明它是在第1,2阶段文档演示的JavaScript面向对象的书写方式的进一步改进,它是现代JavaScript面向对象编程方式(使用基本类来编码)的过渡代码--没有它就没有当今...