哪种选择最合适,A或B还是有更好的方法?
var component = function(){ var self = this; //A - not sure there's a leak here this.foo = function(){ //var self = this; //B. I can do this but I want to use self in other method as well var dom = getElementById('someid'); dom.onclick = function(){ self.foo2(); // here I used the self reference //i cannot use this here,because it refer to dom } } this.foo2 = function(){ var dom = getElementById('someid'); dom.onclick = function(){ self.foo2(); //here I used the self reference //i cannot use this here,because it refer to dom } } }; // some usage var c1 = new component(); c1.foo();
解决方法
这种方法的问题在于,所有方法的所有代码都将被复制到每个实例中,这可能会变成一个大的内存消耗(除非你不能依赖的执行上下文之间的漂亮优化).你在那里做的好处是点击处理程序直接调用你想要它调用的方法.
通常的做法是通过将它们放在原型上来实现在实例之间共享的方法,并且让实例在一个包含良好的闭包上下文中构建小包装器函数(例如,不关闭无关数据的那些) – 通常由辅助函数创建的原因 – 设置对实例的调用.这样,每个实例只复制少量代码,其中大部分都是共享的.成本是每次点击需要调用一个函数然后转向并调用另一个函数,但坦率地说,开销并不是一个问题,除非在非常紧的循环中(点击处理程序不是),而内存消耗真的可以是今天的webapps中的一个问题.
在原型上设置函数在其他地方得到了很好的覆盖,并且通常通过帮助程序来处理,这使得您比原始JavaScript更清晰,更简洁.有关包含良好的闭包上下文中的包装器构建器的示例,请查看Prototype中的Function#bind实现(或其他几个JavaScript库中的类似函数,如MooTools,Closure库等)
基础知识看起来像这样,但我实际上不会这样做:
var component = function() { this.boundFoo = bind(this,foo); // Remove this if you never use it as a handler this.boundFoo2 = bind(this,foo2); }; component.prototype.foo = function() { var dom = getElementById('someid'); dom.onclick = this.boundFoo2; }; // (Isn't this exactly what foo did?) component.prototype.foo2 = function() { var dom = getElementById('someid'); dom.onclick = this.boundFoo2; }; function bind(context,func) { return function() { func.apply(context,arguments); }; }
注意bind如何接受上下文和函数,并返回一个新函数,该函数将使用该上下文调用给定函数.
您也可以在分配它们的位置绑定函数,而不是将它们设置为实例上的属性,但是如果您要重用它们(如上图所示),那么保留副本就可以实现.
上面有一些问题(所有函数都是匿名的,这意味着你的工具无法帮助你)但是没有进入对象辅助函数,这是基本的想法.
内存泄漏
“Crescent Fresh”指出我最初并没有真正解决内存泄漏问题.要解决的一个重要方面是,对于某些浏览器(主要是IE和衍生产品),在完成它时(例如,离开页面时)解开事件处理程序非常重要.因此,如果您稍后不清除它们,那么您的onclick分配可能会在某些浏览器上出现内存泄漏.这是因为所涉及的浏览器不处理DOM元素和JavaScript对象之间的循环引用,当它们都没有引用时(例如,它们相互引用但没有其他任何引用它们).您必须断开DOM元素和JavaScript函数之间的链接,以确保清除它们.如果你使用他们的方法来附加事件处理程序,那么像Prototype这样的Libary会在你卸载页面时为你做这件事;我不知道其他的libs是否足以评论他们是否这样做.
其他说明
有点OT,但是:
>还要研究“事件委托”的概念 – 点击处理程序是使用委托的好地方,因为它们会冒泡.您可能会发现在容器级别只需要几个处理程序,而不是元素级别的数百个处理程序.
>不是分配给元素的onclick属性(称为“DOM0”风格的设置事件处理程序),而是考虑使用较新的(90年代后期)机制来执行此操作:符合标准的浏览器上的addEventListener
和IE上的attachEvent
.以这种方式做事的一个重大改进是可以在同一个元素上为同一个事件设置多个处理程序,而使用DOM0处理程序时,如果有一个新的那个,则分配一个新的处理程序.