Simple JavaScript Inheritance
原文:http://ejohn.org/blog/simple-javascript-inheritance/
最近,我在javascript 继承这件事上 花了不少功夫,主要原因是 为了完成我的书 《work-in-progress JavaScript book》在这过程中, 我尝试了各种各样的不同的 可以实现 javascript 继承的方法,在所有我看的方法里,我认为我最喜爱的 是一种基于 base2 和 Prototype的一种实现方式.
我希望可以融汇百家所长,在这些方法中 ,提炼成一个,可复用的 简单的 东西。 它不依赖任何其他东西的,而且即便小白也能看懂。另外 我想这个最终的结果一定是 又简单 又 高度可用的好东东。下面向大家展示一个例子:
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() {
// Call the inherited version of 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
// Should all be true
p instanceof Person && \
p instanceof Class && \
n instanceof Ninja && \
n instanceof Person && \
n instanceof Class
//
对于这种实现方式 有以下几点需要注意的:
- 构造函数必须非常简单(在这个例子里,只是简单地提供了一个init 方法)
- 必须从一个已有的类上来拓展你要创建的类。
- 这些所有你创建的‘类’ 都继承自一个共有的祖先‘类’,这样如果你想让你创建的类 可以被其他类继承,那么 他必须继承这个祖先类,或者继承了这个祖先类衍生出来的子类也行。
- 最有挑战的一点是,那些个被子类同名方法被覆盖了的父类方法,仍然可以通过 this._super()访问的到。
那么用这种方法,让我们实现一个简单的继承:
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function() {
var initializing = false,
fnTest = /xyz/.test(function() {
xyz;
}) ? /\b_super\b/: /.*/;
// The base Class implementation (does nothing)
this.Class = function() {};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don’t run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we’re overwriting an existing function
prototype[name] = typeof prop[name] == “
function” && typeof _super[name] == “
function” && fnTest.test(prop[name]) ? (function(name, fn) {
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we’re done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init) this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
初始化
在传统的方式中,为了实现继承,代码可能会像下面这样:
function Person() {}
function Ninja() {}
Ninja.prototype = new Person();
// Allows for instanceof to work:
(new Ninja()) instanceof Person
有一件非常具有挑战性的事情是,我们需要的仅仅是从 ‘instanceof’ 中获益,而不是去真正的实例化一个Person类,并且执行他的构造函数。
继承他 ,我们仅仅是为了使用它的 原型而已。如果Person 类的构造函数中有一个 非常繁重的任务,那么为了使 Ninjia 类继承 Person类 这次 new Person 是完全没有意义的。
Super Method
当你在实现继承的时候,在创建了 一个Class 后 经常需要访问他 已经被覆盖掉了的父类方法,这就比较麻烦了。一种方法是把他父类的原型完全暴露出来 但这样就丧失了继承的意义。
所以我们在给 子类的原型加属性的时候 都裹了一层方法,动态改变他的 this._super 使他指向当前的同名父类方法, 而他的父类原型也不必暴露出来。