3

私は最近、javascriptでmapの実装を使用して一連のアイテムを作成し、それらをオブジェクトのaddメソッドに適用しようとしました。

まず、マップの標準的な実装です。

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.

とにかく、私の質問は、マップされた関数としてオブジェクトメソッドの使用を実際にサポートするために、マップ(または同様のジェネリック関数)をどのように実装できますか?

4

1 に答える 1

13

map( Menu.add , languages ) を呼び出そうとしています

ここであなたの問題は、ほぼ確実に JavaScript にバインドされたメソッドがないことです。

関数の「this」の設定は、メソッドがどのように取得されたかを調べることによって、呼び出し時にのみ決定されます。次のいずれかを言う場合:

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

JavaScript は「obj」への参照を取得し、メソッド呼び出し内で「this= obj」を設定します。しかし、あなたが言うなら:

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

関数内の「this」は、 objではなくobj2 になります。

同様に、メソッドをそのオブジェクトから取り出してファーストクラス オブジェクトとして参照すると、次のようになります。

var method= obj.method;
method();

「this」に設定するオブジェクトがないため、JavaScript はそれをグローバル オブジェクト (Web ブラウザーの「ウィンドウ」とも呼ばれます) に設定します。これはおそらくあなたのケースで起こっていることです:「Menu.add」メソッドはその所有者「Menu」へのすべての参照を失うため、コールバックされると、無意識のうちに「window」オブジェクトではなく「window」オブジェクトのメンバーに書き込んでいる可能性があります。メニュー。

もちろん、これは OO 言語では非常に珍しいことであり、あなたが望んでいることはほとんどありませんが、JavaScript はそのように機能します。サイレントでデバッグが困難なエラーを引き起こすことは、すべて言語の論理的根拠の一部です。

この問題を回避するには、マップ関数にオブジェクト参照を渡し、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() よりも少し多くのコードが必要です。

  • IE は、イベント ハンドラーなどの DOM オブジェクトにバインドされたメソッドなどの参照を残し始めると、メモリ リークを起こしがちです。

于 2009-02-25T13:06:27.490 に答える