3 Stimmen

Implementieren Sie Karte in Javascript, die Objektmethoden als gemappte Funktionen unterstützt?

Ich habe vor kurzem versucht, eine Implementierung der Karte in Javascript verwenden, um eine Reihe von Elementen zu erstellen, dann gelten sie für ein Objekt hinzufügen Methode.

Erstens mit einer Standardimplementierung von map.

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

Einrichten.

var translateMenu = new Menu;

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

Und meine Funktion... (nicht anonym, da sie später beim Hinzufügen des translateMenu zu mainMenu verwendet wird).

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 );

Dies alles funktionierte gut, ich hatte jetzt ein Array von MenuItems um zu werfen.

Versuchen Sie anzurufen map( Menu.add , languages ) würde dazu führen, dass interne Variablen von Menu undefiniert sind und der Aufruf fehlschlägt.
Ich bin mir sicher, dass dies mit dem Umfang der Arbeit zu tun hat. Menu.add() Methode, also dachte ich, wenn ich das Objekt auch übergebe, könnte es funktionieren.

Ich habe versucht, eine neue Map-Funktion zu erstellen, die Objekte und Funktionen akzeptieren würde, aber ich hatte den gleichen undefinierten Fehler.

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

Ich umging dies, indem ich Menu mit addAll() erweiterte, um ein Array zu nehmen, was gut funktioniert...

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.

Wie auch immer, meine Frage ist, wie könnte ich map (oder eine ähnliche generische Funktion) implementieren, um die Verwendung von Objektmethoden als meine zugeordneten Funktionen zu unterstützen? .

13voto

bobince Punkte 512550

Ich versuche, map( Menu.add , languages ) aufzurufen.

Hier ist Ihr Problem mit ziemlicher Sicherheit das Fehlen von gebundenen Methoden in JavaScript.

Die Einstellung von "this" für eine Funktion wird erst zum Zeitpunkt des Aufrufs bestimmt, indem untersucht wird, wie die Methode erhalten wurde. Wenn Sie eine der folgenden Aussagen machen:

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

JavaScript übernimmt den Verweis auf 'obj' und setzt 'this= obj' innerhalb des Methodenaufrufs. Aber wenn Sie sagen:

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

Jetzt wird "this" innerhalb der Funktion zu obj2, no obj!

Ähnlich verhält es sich, wenn Sie die Methode aus ihrem Objekt herausnehmen und sie als Objekt erster Klasse bezeichnen:

var method= obj.method;
method();

Es gibt kein Objekt, auf das "this" gesetzt werden kann, also setzt JavaScript es auf das globale Objekt (auch bekannt als "window" für Webbrowser). Das ist wahrscheinlich das, was in Ihrem Fall passiert: Die Methode "Menu.add" verliert alle Verweise auf ihren Besitzer "Menu", so dass sie, wenn sie wieder aufgerufen wird, höchstwahrscheinlich unwissentlich in Mitglieder des Objekts "window" statt in ein Menü schreibt.

Das ist natürlich höchst ungewöhnlich für eine OO-Sprache und fast nie das, was man sich wünscht, aber hey, so ist JavaScript nun mal. Das Verursachen von stillen, schwer zu debuggenden Fehlern ist Teil der Logik der Sprache.

Um dieses Problem zu umgehen, könnten Sie eine Objektreferenz an Ihre Map-Funktion übergeben und dann Function.call()/apply() verwenden, um die 'this'-Referenz korrekt zu setzen:

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)

Ein allgemeinerer Weg wäre die manuelle Bindung von Funktionsreferenzen unter Verwendung einer Closure:

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

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

Diese Fähigkeit wird in eine künftige Version von JavaScript integriert werden:

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

Und es ist möglich, diese Möglichkeit zu aktuellen Browsern hinzuzufügen, indem man Function.prototype.bind schreibt - einige JS-Frameworks tun dies bereits. Beachten Sie jedoch:

  • ECMAScript 3.1 verspricht, dass Sie auch in der Lage sein werden, zusätzliche Argumente an bind() zu übergeben, um eine partielle Funktionsanwendung durchzuführen, was ein wenig mehr Code erfordert als bindMethod() oben;

  • IE liebt es, Speicher zu verlieren, wenn Sie anfangen, Verweise wie gebundene Methoden auf DOM-Objekte wie Event-Handler zu verlassen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X