很长一段时间以来,我一直在努力使我的JavaScript更加面向对象。我已经看了几个不同的实现,但我无法决定是否有必要。
我想回答的是以下问题
答案 0 :(得分:19)
咦。对我而言,它看起来要复杂得多。
实际上更仔细地看一下,我真的对于在方法中提供this._super()
它正在做什么做出异常,以调用超类方法。
代码引入了对typeof==='function'
的依赖(某些对象不可靠),Function#toString
(argh,函数分解也不可靠),并根据您是否使用了序列决定是否换行函数体中的字节_super
(即使您只在字符串中使用它。如果您尝试例如。this['_'+'super']
它将失败)。
如果你在函数对象上存储属性(例如MyClass.myFunction.SOME_PRIVATE_CONSTANT
,你可以这样做以保持命名空间清洁),包装将阻止你获得这些属性。如果在方法中抛出异常并捕获同一对象的另一个方法,_super
将最终指向错误的东西。
这一切只是为了让你的超类同名方法更容易调用。但我不认为在JS中特别难以做到这一点。它对自己的好处太聪明了,并且在整个过程中使整体变得不那么可靠。 (哦,并且arguments.callee
在严格模式下无效,尽管这并不是他的错,因为在发布之后就发生了。)
这就是我目前用于课程的内容。我并不认为这是“最好的”JS类系统,因为有加载不同的方法和一些你可能想要添加或不添加的不同功能。但它非常轻巧,旨在成为“JavaScript”,如果这是一个词。 (事实并非如此。)
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
超类调用是通过专门命名所需的方法并call
来完成的,有点像Python。如果您想避免再次命名_super
,那么可以将Person
成员添加到构造函数中(因此您要说Ninja._super.prototype._init.call
,或者{{1} }})。
答案 1 :(得分:5)
JavaScript是基于原型的,而不是基于类的。我的建议不是打击它并以JS的方式声明子类型:
MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;
答案 2 :(得分:5)
了解如何在不使用继承的情况下获得多少。将其视为一种性能破解(在不需要真正必要的情况下不情愿地应用),而不是设计原则。
在像JS这样的高度动态语言中,很少需要知道对象是否是 Person
。您只需知道是否具有 firstName
属性或eatFood
方法。您通常不需要知道对象是否是数组;如果它有一个length属性和一些以整数命名的其他属性,那通常就足够了(例如Arguments对象)。 “如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那就是鸭子。”
// give back a duck
return {
walk: function() { ... },
quack: function() { ... }
};
是的,如果您正在制作大量小对象,每个小对象都有几十种方法,那么无论如何都要将这些方法分配给原型,以避免在每个实例中创建数十个插槽的开销。但将其视为减少内存开销的一种方法 - 仅仅是优化。并且通过隐藏在某种工厂函数后使用new
来帮助您的用户,因此他们甚至不需要知道如何创建对象。他们只需要知道它有方法foo
或属性bar
。
(请注意,在这种情况下,您不会真正建模经典继承。它仅仅相当于定义单个类以获得共享vtable的效率。)