John Resig 的 OO JavaScript 实现生产安全吗?
Posted
技术标签:
【中文标题】John Resig 的 OO JavaScript 实现生产安全吗?【英文标题】:Is John Resig's OO JavaScript implementation production safe? 【发布时间】:2011-04-18 13:26:34 【问题描述】:很长一段时间以来,我一直在想使我的 javascript 更加面向对象的想法。我也查看了一些不同的实现,但我无法确定是否有必要。
我想回答的是以下问题
John Resig's 简单的继承结构在生产环境中使用安全吗? 有什么方法可以判断它的测试效果如何? 除了Joose,我还有哪些其他选择?我需要一款易于使用、快速且强大的产品。它还需要与 jQuery 兼容。【问题讨论】:
我已经看到 Javascript “在生产中”,它会让小猫天使哭泣,所以它对你的单元/页面测试的影响真的取决于你。这是一件如此简单的事情,很难想象它会带来严重的问题。 +1 尖尖的。我刚刚在下面进行了批评,但实际上 99% 的网站已经有比他们更多的可疑代码...... 现在小心蒂姆,我们不要开始做事......! @bobince:到目前为止,我似乎已经成功了。 【参考方案1】:嗯。对我来说,它看起来比实际需要的复杂得多。
实际上,仔细观察,我真的对在方法中提供this._super()
来调用超类方法的做法表示异议。
代码引入了对typeof==='function'
(对某些对象不可靠)、Function#toString
(啊,函数分解也是不可靠的)的依赖,并根据你是否使用过字节序列来决定是否换行@987654325 @ 在函数体中(即使你只在字符串中使用过它。如果你尝试例如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
的情况下进行写作,并导致其他未这样定义的对象出现问题。无论哪种方式都比让this.
属性落在window
上并在以后神秘地出错的不可接受的JS 默认值要好。
一个可继承的_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
ing 来完成的,有点像在 Python 中。如果您想避免再次命名Person
(所以您会说Ninja._super.prototype._init.call
,或者可能是Ninja._base._init.call
),您可以将_super
成员添加到构造函数。
【讨论】:
typeof
仅在处理宿主对象时才对函数不可靠,并且您不想将其中一个子类化。或者至少,你不应该想要。我当然同意 Resig 的代码过于复杂和脆弱,根本不需要它。
@Tim:是的,你不能对宿主对象进行原型设计,但函数测试所做的是检查每个成员,而不是超类对象。因此,作为类成员的宿主对象可能会导致问题。 (例如,在 Firefox 3.0 中,正则表达式作为函数出现,因此 Class.extend 中的包装可能会通过尝试像函数一样包装正则表达式成员来影响它们)。
哦,我总是忘记正则表达式的事情。是的,你是对的;我没有像以前那样彻底阅读 Resig 先生的代码,现在它看起来比以前更加脆弱。顺便说一句,很好地使用“nadger”这个词。它在 SO 上没有得到充分利用。
这是一个很棒的例子 bobince .....我只是想在接受之前看看是否有其他人有任何其他建议。非常感谢您对它如此彻底。
如果您关心性能,以下链接会很有帮助。使用 JavaScript 是基于原型的,而不是基于类的。我的建议是不要与之抗争并以 JS 方式声明子类型:
MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;
【讨论】:
这个问题是调用new MySuperObj()
会调用MySuperObj()
的构造函数中的初始化代码,就好像你实际上是在实例化MySuperObj
一样。只要您在构造函数中有任何重要内容,这将不起作用,因为您需要在构造 MyDerivedObj
实例时调用 MySuperObj
初始化代码,而不是在定义时。
我同意您可能希望将初始化代码分解到一个单独的方法中,并从 MyDerivedObj 的构造函数中调用它......更好的是,考虑使用依赖注入。我发现这大大减少了构造函数代码。【参考方案3】:
看看在不使用继承的情况下你能走多远。将其视为性能技巧(在真正需要的情况下不情愿地应用)而不是设计原则。
在像 JS 这样高度动态的语言中,很少需要知道一个对象 是否是一个 Person
。您只需要知道它是否具有 firstName
属性或eatFood
方法。您通常不需要知道一个对象是否是一个数组。如果它有一个长度属性和一些以整数命名的其他属性,那通常就足够了(例如 Arguments 对象)。 “如果它走路像鸭子,叫起来像鸭子,那就是鸭子。”
// give back a duck
return
walk: function() ... ,
quack: function() ...
;
是的,如果您要制作大量的小对象,每个对象都有几十个方法,那么请务必将这些方法分配给原型,以避免在每个实例中创建几十个插槽的开销。但将其视为减少内存开销的一种方式 - 仅仅是优化。并且通过将您对 new
的使用隐藏在某种工厂函数后面来帮助您的用户,这样他们甚至不需要知道对象是如何创建的。他们只需要知道它有方法foo
或属性bar
。
(请注意,在这种情况下,您不会真正对经典继承进行建模。这仅相当于定义单个类以获得共享 vtable 的效率。)
【讨论】:
以上是关于John Resig 的 OO JavaScript 实现生产安全吗?的主要内容,如果未能解决你的问题,请参考以下文章
对 John Resig 的 JavaScript 类框架的改进
《jQuery实战》 Jquery之父John Resig 推荐序言
[转] jquery作者John Resig编写的微模板引擎:JavaScript Micro-Templating
使用 John Resig 的“简单 JavaScript 继承”如何从方法中调用超级方法以及额外代码?