【翻译】简单的JavaScript继承

原文地址:[url]http://ejohn.org/blog/simple-javascript-inheritance/[/url]
译者注:cocos2d-html5使用了这种类继承方式

[size=large][align=center][b]简单的JavaScript继承[/b][/align][/size]

关于JavaScript继承,最近我做了很多事,主要是为了我的JavaScript书能顺利出版,同时也检验了多种JavaScript经典继承方式的模拟技术。其中,我最感兴趣的是base2([url]http://code.google.com/p/base2/[/url])和Prototype([url]http://prototypejs.org/[/url])。

我想把这几种技术的精华以简单、可复用、容易理解并没有任何依赖的形式展现出来。并且,我希望结果简单而高可用。下面是一个例子:

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()实现。

我对结果很满意:它强制类为一个结构,维护简单的继承,并且可以调用父类方法。

[b]简单的类创建和继承[/b]
下面是具体实现(做了格式化并添加了注释)——大概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方法”。我想简要地介绍一下这些部分,这样你就可以更好地理解我们在这个方法中实现了什么。

[b]初始化[/b]
为了使用一个函数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对象,谁知道呢)可以有效避免无法正常工作的。

[b]Super方法[/b]
当你继承的时候,创建一个类,然后从父类继承一些功能,大家最希望有的功能就是可以访问被覆盖的父类方法。最终,在这个实现方案中是通过一个临时方法(._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类的创建和继承。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值