2010-10-26 52 views
5

Disons que je Réaffecter une fonction avec une autre fonction de ces deux fonctions:Javascript:

function fnChanger(fn) { 
    fn = function() { sys.print('Changed!'); } 
} 
function foo() { 
    sys.print('Unchanged'); 
} 

Maintenant, si je l'appelle foo(), je vois Unchanged, comme prévu. Cependant, si je l'appelle fnChanger d'abord, je vois encore Unchanged:

fnChanger(foo); 
foo(); //Unchanged 

Maintenant, je suppose que cela est dû au fait foo n'est pas passé à fnChanger par référence, mais je peux me tromper.

Pourquoi fnChanger ne modifie-t-il pas foo pour imprimer Changed!?
En outre, comment puis-je obtenir fnChanger pour changer foo sans trop de syntaxe désordonnée? PS: J'utilise node.js pour tester tout cela, c'est de là que vient le sys.print.

Répondre

5

L'affectation à l'argument fn rend juste cet identificateur pour pointer vers la fonction anonyme, foo dans la portée externe n'est pas affectée.

Lorsque vous passez un objet en argument, on peut dire "les références sont passées par valeur". L'affectation remplace simplement l'emplacement auquel l'identificateur fn fait référence.

Voilà comment fonctionne le evaluation strategy en JavaScript.

Juste avant l'affectation dans les fonctions fnChanger, les deux identifiants, l'foo mondiale et l'argument fn, pointez sur le même objet de fonction:

 
       --------------------------------------------- 
    foo -----> |function foo { sys.print('Un changed!'); } | 
       --------------------------------------------- 
       ^
        | 
    fn ------------- 

Après l'affectation, fn simplement pointer vers le nouveau fonction:

 
       --------------------------------------------- 
    foo -----> | function foo { sys.print('Unchanged!'); } | 
       --------------------------------------------- 

       --------------------------------------- 
    fn ------> | function { sys.print('Changed!'); } | 
       --------------------------------------- 

Comment pouvez-vous le changer?

Eh bien, en supposant que foo est une fonction dans la portée globale, vous pourriez faire quelque chose comme ceci:

function fnChanger(obj, name) { 
    obj[name] = function() { sys.print('Changed!'); }; 
} 

function foo() { 
    sys.print('Unchanged'); 
} 

fnChanger(this, 'foo'); 
foo(); // Changed! 

ci-dessus fonctionnera parce que dans la fonction fnChanger, nous avons besoin d'un objet de base et nom de la propriété, les fonctions déclarées dans le contexte d'exécution globale sont liées en tant que propriétés de l'objet global , par conséquent nous pouvons réaffecter sa valeur de cette manière.

La ligne fnChanger(this, 'foo'); doit être exécuté aussi dans la portée globale, il passera la valeur this (qui fait référence à l'objet global dans ce champ) et un nom de propriété, vous permettant de faire une affectation à l'identifiant GlobalObject.foo.

Si ce code était à l'intérieur d'une fonction, il n'y a aucun moyen que nous pouvons obtenir un objet de base , parce que dans cette déclaration « Code fonction d'exécution Contexte », fonction (paramètres formels déclarations variables et fonction aussi) sont liés en tant que propriétés d'un objet non accessible, appelé l'objet variable (une chaîne de ces objets variables, constitue la chaîne d'étendue), et si c'était le cas, la seule solution de contournement serait d'utiliser eval.

Plus d'infos:

+0

Oui, je commençais à soupçonner que ce qui se passe. Merci pour la clarification, et le lien - je n'ai jamais su exactement comment appeler cela ou comment cela a fonctionné. Comment puis-je contourner cela? –

+0

Ok, j'aime cette approche. Pas aussi désordonné que je pensais le faire: passer un objet contenant la fonction à changer. –

2

Comme @CMS souligné vous ne pouvez pas l'affecter au sein de la fonction en raison de la portée. Cependant, vous pouvez le réattribuer comme ceci:

var fnChanger = function() { 
    return function() { 
     alert('changed!'); 
    } 
} 

var foo = function() { 
    alert('Unchanged'); 
} 

foo = fnChanger(); 
foo(); 

example

+0

Ouais, réfléchi à ça. J'hésite à le faire par retour dans le vrai code, cependant. D'une part, c'est plus propre, mais d'autre part, cela ne correspond pas tout à fait à ce que fait la fonction. –