长期以来,我一直在思考如何使我的JavaScript更加面向对象。我也看过几种不同的实现方法,但我无法决定是否有必要这样做。
我试图回答以下问题:
- 约翰·雷西格(John Resig)的简单继承结构是否可以安全地用于生产环境?
- 有没有办法能够判断它被测试得有多好?
- 除了Joose之外,我还有哪些选择?我需要一个易于使用、快速、稳健的方案。它还需要与jQuery兼容。
长期以来,我一直在思考如何使我的JavaScript更加面向对象。我也看过几种不同的实现方法,但我无法决定是否有必要这样做。
我试图回答以下问题:
哎呀,对我来说它看起来比必要的要复杂很多。
仔细看看,我真的反感它在方法中提供this._super()
来调用超类方法的做法。
该代码引入了对typeof==='function'
(对于一些对象不可靠)、Function#toString
(哎呀,函数分解也不可靠)的依赖,以及决定是否进行包装的方式是根据您在函数体中使用字节序列_super
的顺序(即使您只在字符串中使用它。如果尝试例如this['_'+'super']
,它将失败)。
如果您在函数对象上存储属性(例如 MyClass.myFunction.SOME_PRIVATE_CONSTANT
,您可能会这样做以保持命名空间的清洁),那么包装将阻止您访问这些属性。如果在一个对象的方法中抛出异常并在另一个方法中捕获,_super
将指向错误的东西。
所有这些都只是为了更轻松地调用超级类的同名方法。但是我认为在JS中实现这个并不特别困难。它太聪明反被聪明误了,并因此使整个系统变得不够可靠。(噢,arguments.callee
在严格模式下无效,尽管这并不是他的错,因为它发生在他发布之后。)
这是我目前使用的类。我不认为这是“最好”的JS类系统,因为有大量进行它和一堆您可能想要添加或不添加的不同功能的方法。但它非常轻巧,并旨在成为“ JavaScriptic”,如果这是一个词的话。(它不是。)
Function.prototype.makeSubclass= function() {
function Class() {
if (!(this instanceof Class))
throw 'Constructor function requires new operator';
if ('_init' in this)
this._init.apply(this, arguments);
}
if (this!==Object) {
Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
}
return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};
它提供了:
防止意外缺少 new
。另一种选择是将 X()
静默重定向到 new X()
,以便缺少 new
也能正常工作。这两种方法都有利弊;我选择明确的错误提示,避免习惯于编写没有 new
的代码,并在其他未定义为此类的对象上引起问题。无论哪种方式,都比JS默认的让 this.
属性落在 window
上并在后续出现神秘错误要好。
可继承的 _init
方法,因此您不必编写什么都不做,只调用超类构造函数的构造函数。
就是这样。
以下是如何使用它来实现 Resig 的示例:
var Person= Object.makeSubclass();
Person.prototype._init= function(isDancing) {
this.dancing= isDancing;
};
Person.prototype.dance= function() {
return this.dancing;
};
var Ninja = Person.makeSubclass();
Ninja.prototype._init= function() {
Person.prototype._init.call(this, false);
};
Ninja.prototype.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 &&
n instanceof Ninja && n instanceof Person
通过特别指定你想要的方法并调用它来进行超类调用,有点像Python。如果你想避免再次命名Person
,可以在构造函数中添加_super
成员(这样你就可以说Ninja._super.prototype._init.call
或者Ninja._base._init.call
)。
typeof
对于函数来说只是不可靠的,而且你也不想子类化其中的一个。或者至少,你不应该想要这样做。我完全同意Resig的代码过于繁琐和脆弱,而且根本没有必要这样做。 - Tim DownJavaScript是基于原型而不是类的。我的建议是不要与其抗争,而是以JS的方式声明子类型:
MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;
new MySuperObj()
会触发构造函数中的初始化代码,就像您实际上正在实例化一个MySuperObj
一样。一旦构造函数中有任何非平凡的内容,这种方法就不再适用,因为您需要在构造MyDerivedObj
实例时调用MySuperObj
的初始化代码,而不是在定义时调用。 - bobince// give back a duck
return {
walk: function() { ... },
quack: function() { ... }
};
new
的使用,以便为用户提供帮助,使他们甚至不需要知道对象是如何创建的。他们只需要知道它具有方法foo
或属性bar
。