2017-08-22 9 views
2

Depuis que je suis en train de saisir le concept de iterables en JavaScript, je construit un avec le code suivant:Comment le mot-clé "this" fonctionne-t-il avec les objets en JavaScript?

let range = { 
    from: 0, 
    to: 5 
}; 

range[Symbol.iterator] = function() { 
    return { 
    current: this.from, 
    last: this.to, 

    next() { 
     if (this.current <= this.last) { 
     return { done: false, value: this.current++ }; 
     } else { 
     return { done: true }; 
     } 
    } 
    }; 
}; 

for (let num of range) { 
    console.log(num); 
} 

La méthode Symbol.iterator retourne un objet qui contient une méthode (next) et deux propriétés (current et from). Les propriétés de cet objet sont égales aux propriétés de l'objet range.

Depuis current égal this.from et last égal this.to, je suppose que je pourrais utiliser this.from et this.to directement à partir de la méthode next comme si:

let range = { 
    from: 0, 
    to: 5 
}; 

range[Symbol.iterator] = function() { 
    return { 
    next() { 
     if (this.from <= this.to) { 
     return { done: false, value: this.from++ }; 
     } else { 
     return { done: true }; 
     } 
    } 
    }; 
}; 

for (let num of range) { 
    console.log(num); 
} 

Cependant, cela empêche l'itération de démarrer. Ayant observé cela, j'ai deux questions:

  1. Pourquoi peut les propriétés current et last utiliser le mot clé this et l'ont référence à l'objet range alors que la méthode ne peut pas next? current, last et next() font tous partie du même objet, qui est retourné par Symbol.iterator.

  2. En outre, étant donné que Symbol.iterator() renvoie un objet séparé, ne devrait pas le mot-clé this dans cet objet se référer à cet objet lui-même? En d'autres termes, les propriétés current et lastet non ne devraient pas pouvoir accéder aux propriétés de range en utilisant le mot-clé this? Le range n'est-il pas un objet distinct?

+0

Une utilisation de 'this' est à l'intérieur de la méthode' [Symbol.iterator] ', l'autre dans la méthode' .next'. Les littéraux d'objets n'ont pas d'importance, ils n'ont pas leur propre portée. – Bergi

Répondre

2

Le mot-clé this est compliqué. Il est sage d'éviter de l'utiliser quand il y a de meilleures alternatives, ce qui est généralement le cas, car il y a beaucoup de pièges.

Avec cela du chemin, voici la réponse: this fait référence à l'objet d'activation (parfois appelé objet contexte) que sa fonction contenant a été appelée avec.

Qu'est-ce que l'objet d'activation? C'est l'objet auquel la fonction était attachée quand elle a été appelée.L'objet d'activation pour console.log() est console.

Mais ...

const x = { 
    log : console.log 
}; 
x.log(); 

... ici x.log() est exactement la même que celle console.log(), sauf l'objet d'activation est x au lieu de console.

Cela semble assez simple, non? C'est, en quelque sorte. Mais il y a plus de choses à savoir.

  • Il existe un objet de contexte par défaut, utilisé lorsqu'une fonction est appelée sans être liée à un objet. C'est l'objet global, connu sous le nom window dans le navigateur et global dans Node.js.
  • Si le script est en cours d'exécution dans strict mode, alors il n'y a pas objet de contexte par défaut et il sera undefined sinon appelé explicitement à un contexte
  • Arrow functions utilisation de portée lexicale et leur valeur this est pas l'objet habituel contexte du tout - c'est l'objet de contexte de sa fonction parente de partout où la fonction de flèche a été définie

Maintenant, prenons ce qui précède et appliquons-le à votre code. Plus important encore, votre méthode next() fonctionnerait si elle était appelée avec le contexte étant l'objet range. Le problème est, sous le capot, le moteur fait essentiellement ce ...

const returnedObject = range[Symbol.iterator](); 
returnedObject.next(); 

... alors next est appelé à returnedObject son contexte, plutôt que range. Mais la fonction qui a renvoyé l'objet est appelée range comme contexte. Ainsi, this est différent à chaque endroit.

Vous pouvez réellement résoudre votre problème très facilement en utilisant une fonction de flèche au lieu d'une méthode sténographique.

Cela fonctionne:

let range = { 
    from: 0, 
    to: 5 
}; 

range[Symbol.iterator] = function() { 
    return { 
    next :() => { 
     if (this.from <= this.to) { 
     return { done: false, value: this.from++ }; 
     } else { 
     return { done: true }; 
     } 
    } 
    }; 
}; 

for (let num of range) { 
    console.log(num); 
} 
1

ce est connu pour être une chose délicate. Tellement que Kyle Simpson a consacré presque un livre entier au sujet (You Don't Know JS: this & Object Prototypes). Cependant, dans votre exemple, la réponse est facile - dans la deuxième fonction, , cette est dans la fonction next() et ainsi, résout cette fonction.

Pour vérifier la portée de ce, j'utilise l'outil de débogage de Chrome. Si vous faites une pause à la bonne ligne, cela vous indique ce que ce résout à.

+0

Cela a du sens. Cependant, alors pourquoi les propriétés 'current' et' last' (dont les deux valeurs impliquent 'this') se résolvent en l'objet' range' et non en leur propre objet? – Gokhan

+0

Fermeture et héritage prototypique. Cela dit hey je suis en quelque sorte de cette fermeture (fonction/objet) –

+0

'current: this.from' arrive dans l'objet' range' et donc, 'this' fait référence' range'. Dans le second extrait, 'this' est utilisé dans le cadre de la fonction' next() '. – Aydin4ik