2009-02-25 46 views
2

我最近尝试在javascript中使用map的实现来创建一堆项目,然后将它们应用到对象添加方法。在JavaScript中实现支持对象方法为映射函数的地图?

首先用map的bog标准实现。

var map = function (fn, a) 
{ 
    for (i = 0; i < a.length; i++) 
    { 
     a[i] = fn(a[i]); 
    } 
} 

设置。

var translateMenu = new Menu; 

var languages = [ ['Chinese' , 'zh-CN'] 
       , ['German' , 'de'] 
       , ['French' , 'fr'] 
       , ['Portugese' , 'pt'] 
       , ['Hindi'  , 'hi'] 
       ]; 

而且我的功能...(不是匿名的,因为它增加时,translateMenu到MainMenu的。以后使用)

var langItem = function (language, subMenu) 
    { 
     return new MenuItem(language[0], 'http://translate.google.com/translate?u=www.example.com&hl=en&ie=UTF-8&tl=en&sl=' + language[1] , "" , subMenu); 

    } 

map (langItem , languages); 

这一切都工作得很好,我现在有了的MenuItems的数组扔周围。

试图调用map(Menu.add , languages)会导致Menu的内部变量未定义,并且调用失败。
现在我确定这与Menu.add()方法的范围有关,所以我想如果我也传入对象,它可能会工作。

我试着创建一个新的地图函数,它可以接受对象和函数,但有相同的未定义错误。

objMap (fn , obj , a) { 
    for (i = 0; i < a.length; i++) 
    { 
     obj.fn(a); 
    } 
} 
objMap (add , translateMenu , languages); // failed 

我工作围绕这通过扩展菜单的addAll用()取一个数组,工作正常...

Menu.prototype.addAll = function (items){ 
    for (i = 0; i < items.length; i++) 
    { 
     this.add(items[i]); 
    } 
} 

translateMenu.addAll(languages); // yay! but I want a more elegant solution. 

不管怎样,我的问题是,我怎么能实现地图(或一个类似的泛型函数)实际上支持使用对象方法作为我的映射函数?

回答

11

试图调用地图(Menu.add,语言)

这里你的问题是几乎可以肯定的是JavaScript的缺乏束缚的方法。

函数'this'的设置仅在调用时通过检查如何获得方法来确定。如果你说其中的一个:

obj.method(); 
obj['method'](); 

JavaScript将选取对'obj'的引用并在方法调用中设置'this = obj'。但如果你说:

obj2.method= obj.method; 
obj2.method(); 

现在“这个”里面的功能将是obj2的,的obj!

同样,如果你选择的方法关闭它的对象,并把它称为一流的对象:

var method= obj.method; 
method(); 

将有“本”,以获得设置为没有对象,所以JavaScript的设置它到全局对象(也称为Web窗口)。这可能是你的情况发生了什么:'Menu.add'方法失去了对其所有者'Menu'的所有引用,所以当它被回调时,它很可能在不知不觉中写入'window'对象的成员而不是菜单。

这对于面向对象语言来说当然是非常不寻常的,而且几乎从来不是你想要的,但是,嘿,这就是JavaScript如何滚动。造成沉默,难以调试的错误是语言基本原理的一部分。

要解决这个问题,你可以在你的地图功能传递一个对象的引用,然后使用Function.call()/应用()设置“这个”正常参考:

function mapMethod(fn, obj, sequence) { 
    for (var i= 0; i<sequence.length; i++) 
     sequence[i]= fn.call(obj, sequence[i]); 
} 

mapMethod(Menu.add, Menu, languages) 

更一般的方法是手动绑定功能的引用,使用封闭:

function bindMethod(fn, obj) { 
    return function() { 
     fn.apply(obj, arguments) 
    }; 
} 

map(bindMethod(Menu.add, Menu), languages) 

此功能将内置到JavaScript的未来版本:

map(Menu.add.bind(Menu), languages) 

并且可以通过写入Function.prototype.bind将此设施添加到当前的浏览器 - 实际上,一些JS框架已经可以实现。但是请注意:

  • 的ECMAScript 3.1承诺你也可以额外的参数传递到上面绑定()做局部的功能应用,这需要比bindMethod(多一点的代码);

  • 当您开始像绑定DOM对象之类的事件处理程序时,IE浏览器喜欢泄漏内存。

+0

谢谢,这是一个很棒的答案。 :d – garrow 2009-02-25 22:35:32