2009-02-25 4 views
2

J'ai récemment essayé d'utiliser une implémentation de map en javascript pour créer un tas d'éléments, puis les appliquer à une méthode d'ajout d'objet.Implémenter une carte en javascript qui supporte les méthodes objet comme des fonctions mappées?

Premièrement avec une implémentation standard de la carte.

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

Configuration.

var translateMenu = new Menu; 

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

Et ma fonction ... (pas anonyme, comme il est utilisé plus tard lors de l'ajout du 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); 

Tout cela a bien fonctionné, j'avais maintenant un tableau de MenuItems jeter autour. Si vous essayez d'appeler map(Menu.add , languages), les variables internes de Menu ne seront pas définies et l'appel échouera.
Maintenant, je suis certain que cela a à voir avec la portée de la méthode Menu.add(), donc je pensais que si je passais dans l'objet aussi, cela pourrait fonctionner.

J'ai essayé de créer une nouvelle fonction de carte qui accepterait des objets et des fonctions, mais avec la même erreur non définie.

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

J'ai travaillé autour de ce en étendant Menu avec addAll() pour prendre un tableau, qui fonctionne très bien ...

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. 

Quoi qu'il en soit, ma question est, comment pourrais-je mettre en œuvre la carte (ou une fonction générique similaire) pour prendre réellement en charge l'utilisation de méthodes objet comme fonctions mappées?.

Répondre

11

Essayer d'appeler carte (Menu.add, langues)

Voici votre problème est certainement celui de l'absence de méthodes liées de JavaScript.

Le réglage de 'this' pour une fonction est déterminé uniquement au moment du call, en examinant comment la méthode a été obtenue. Si vous dites que l'un des:

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

JavaScript ramassera la référence à « obj » et set « this = obj » dans l'appel de la méthode. Mais si vous dites:

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

Maintenant, « ce » intérieur de la fonction sera obj2, pas obj!

De même, si vous choisissez la méthode de son objet et faites référence comme un objet de première classe:

var method= obj.method; 
method(); 

Il n'y aura pas d'objet pour « ceci » pour obtenir la valeur, si JavaScript définit ce à l'objet global (aka «fenêtre» pour les navigateurs Web). C'est probablement ce qui se passe dans votre cas: la méthode 'Menu.add' perd toute référence à son propriétaire 'Menu', donc quand elle est rappelée, il est probable qu'elle écrive sans le savoir aux membres de l'objet 'window' au lieu d'un menu.

Ceci est bien sûr très inhabituel pour un langage OO, et presque jamais ce que vous voulez, mais bon, voilà comment roule JavaScript. Causer des erreurs silencieuses et difficiles à corriger fait partie de la logique du langage.

Pour contourner ce problème, vous pouvez passer une référence d'objet à votre fonction de la carte, puis utilisez Function.call()/apply() pour définir le « ceci » référence correctement:

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) 

Plus de façon générale serait de lier des références de fonction manuellement, à l'aide d'une fermeture:

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

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

Cette capacité sera intégrée dans une future version de JavaScript:

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

Et il est possible d'ajouter cette fonctionnalité aux navigateurs actuels en écrivant à Function.prototype.bind - en effet, certains frameworks JS le font déjà. Cependant noter:

  • ECMAScript 3.1 promesses que vous serez également en mesure de passer des arguments supplémentaires dans bind() à faire application de fonction partielle, ce qui nécessite un peu plus de code que bindmethod() ci-dessus;

  • IE aime perdre de la mémoire lorsque vous commencez à laisser des références comme des méthodes liées sur des objets DOM comme des gestionnaires d'événements.

+0

Merci, c'est une excellente réponse. :RÉ – garrow

Questions connexes