3

我最近尝试在 javascript 中使用 map 的实现来创建一堆项目,然后将它们应用于对象添加方法。

首先是地图的沼泽标准实现。

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 来解决这个问题,以获取一个数组,效果很好......

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.

无论如何,我的问题是,我如何实现 map (或类似的通用函数)以实际支持使用对象方法作为我的映射函数?.

4

1 回答 1

13

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

在这里,您的问题几乎可以肯定是 JavaScript 缺少绑定方法。

函数的“this”设置仅在调用时通过检查方法的获取方式来确定。如果你说其中之一:

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

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

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

现在函数内的“this”将是 obj2,而不是obj!

同样,如果您从其对象中选择方法并将其称为一等对象:

var method= obj.method;
method();

'this' 不会被设置为任何对象,因此 JavaScript 将其设置为全局对象(对于 Web 浏览器而言,也称为“窗口”)。这可能是您的情况发生的情况:“Menu.add”方法丢失了对其所有者“Menu”的所有引用,因此当它被回调时,它很可能在不知不觉中写入“window”对象的成员而不是菜单。

这对于 OO 语言来说当然是非常不寻常的,而且几乎从来都不是你想要的,但是,这就是 JavaScript 的运行方式。导致无声的、难以调试的错误都是该语言的基本原理的一部分。

要解决此问题,您可以将对象引用传递给 map 函数,然后使用 Function.call()/apply() 正确设置“this”引用:

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 承诺您还可以将额外的参数传递给 bind() 以执行部分​​函数应用程序,这需要比上面的 bindMethod() 更多的代码;

  • 当您开始在 DOM 对象(如事件处理程序)上留下绑定方法等引用时,IE 喜欢泄漏内存。

于 2009-02-25T13:06:27.490 回答