Javascript: comment visiter le 'this' membre d'une fonction de construction?

Dans la console, j'ai créé une fonction de construction 'Car' comme ci-dessous, et new-ed un objet nommé 'mycar', et il s'est passé comme ceci:

> var Car = function() { ... this.make = "Ford"; ... } undefined > var mycar = new Car() undefined > mycar.make // when I visit mycar.make, it worked 'Ford' > Car.make // when I visit Car.make, it didn't work undefined 

Je voudrais donc demander:

Comment visiter le 'Car.make' comme si je vis le 'mycar.make'?

Clarifier: visiter le nom via Car mais pas via mycar

Où est-ce que le champ this.make="Ford" allé?

J'ai également essayé d'étendre son __proto__ dans la console Chrome dev, mais je ne l'ai pas trouvé.

Merci!

Avant this

Examinons attentivement le code suivant:

 Car = function (brand) { this.brand = brand; } Car.prototype.getBrand = function () { return this.brand; }; ford = new Car("Ford"); fiat = new Car("Fiat"); ford.getBrand(); // "Ford" fiat.getBrand(); // "Fiat" 

À première vue, vous pouvez penser que getBrand est la propriété de ford et fiat , mais c'est faux. Lorsque vous souhaitez savoir comment les choses sont organisées en mémoire, vous ne devez pas compter uniquement sur le code, cela peut être trompeur. Voici un instantané approprié:

 / ├── Car │ └── prototype │ └── getBrand ├── ford │ ├── brand │ └── __proto__ -> /Car/prototype └── fiat ├── brand └── __proto__ -> /Car/prototype 

Pensez-y comme une structure de dossier où les répertoires sont des objets, les fichiers sont des attributs ou des méthodes, et les liens sont des références à d'autres objets. Comme je l'ai dit, /ford/getBrand n'existe pas. Vérifiez par vous-même, ford.hasOwnProperty("getBrand") donne false . Alors, vous pouvez demander pourquoi ford.getBrand() ne se bloque pas? C'est là que cette chose étrange appelée __proto__ entre.

__proto__ est une propriété que vous pouvez trouver dans chaque objet. Dans notre code, /ford/__proto__ pourrait être considéré comme un lien vers /Car/prototype . Cela pourrait également être considéré comme un fichier caché car vous ne le verrez pas si vous écrivez console.log(ford) . Le fait est que vous n'êtes pas censé jouer avec lui, "L'utilisation de __proto__ est controversée et a été découragée". (En savoir plus sur MDN ).

Mais au-delà de la controverse __proto__ , ford.getBrand() fonctionne car JavaScript implémente ce qu'on appelle un mécanisme de recherche. Lorsqu'il ne parvient pas à trouver quelque chose dans un objet, il continuera à chercher dans le prototype de cet objet. Comme il se produit, puisque /ford/getBrand n'existe pas, JavaScript examinera /ford/__proto__ .

Ce que je veux vous montrer, c'est qu'il y a beaucoup de choses qui se passent derrière la scène, mais il n'y a rien de magique. La langue effectue quelques astuces que vous devez trouver et démystifier 🙂

À propos de this

this s'agit d'un mot-clé contextuel, en d'autres termes, sa valeur dépend d'un contexte. Un contexte (= une portée) est un ensemble de valeurs. Par exemple, le contexte global ( / ) contient Car , ford et fiat , mais il contient également this qui fait référence à … le contexte global lui-même! Plus intéressant, JavaScript vous permet de créer de nouveaux contextes. Pour autant que je sache, appeler une fonction est le seul moyen de le faire:

 function f (v) { // context birth // ... // context death } f(); // new local context f(); // new local context 

Dans un contexte local comme celui ci-dessus, this réfère au contexte global par défaut. Vous voudrez peut-être remplacer le comportement par défaut, malheureusement, vous ne pouvez pas écrire this = anything . Nevermind, vous pouvez toujours prendre le contrôle de this utilisant le new mot-clé, un appel contextuel ou … la force brute!

Par défaut :

 Car("Ford"); console.log(brand); // prints "Ford" :-\ told you, // in the context of `Car` // `this` refers to the global // context by default 

Avec le new mot-clé:

 audi = new Car("Audi"); // in the context of `Car` // `this` refers to `audi` 

Avec un appel contextuel:

 ford.getBrand(); // "Ford" // in the context of `getBrand` // `this` refers to `ford` 

Utilisation de la "force brute":

 ford.getBrand.call(fiat); // "Fiat" // in the context of `getBrand` // `this` refers to `fiat` ! 

Cependant, comme vous pouvez le voir, this ne fait jamais référence à la Car , c'est pourquoi /Car/brand manque. L'ajout de propriétés à partir de l'intérieur du constructeur modifie l'instance ( ford , fiat ou audi ), et non la classe ( Car ).

Après this

Trace of ford = new Car("Ford") :

 1. /Car exists ? yes 2. create a new object 3. call Car with this = the new object 3.1. add __proto__ to this 3.2. set this.__proto__ to /Car/prototype 3.1. add brand to this 3.2. set this.brand to "Ford" 3.3. return this (the new object) 4. set ford to the new object 

Trace of ford.getBrand.call(fiat) :

 1. /ford exists ? yes 2. /ford/getBrand exists ? no 3. /ford/__proto__/getBrand exists ? yes 4. call /ford/__proto__/getBrand with this = fiat 4.1. /fiat exists ? yes 4.2. /fiat/brand exists ? yes 4.3. return /fiat/brand 

Une petite démo:

 Car = function (brand) { console.log("this === Car =", this === Car); this.brand = brand; } Car.prototype.getBrand = function () { console.log("this === ford =", this === ford); return this.brand; }; ford = new Car("Ford"); ford.getBrand(); console.log("ford.hasOwnProperty(\"getBrand\") =", ford.hasOwnProperty("getBrand")); console.log("Car.prototype === ford.__proto__ =", Car.prototype === ford.__proto__); console.log("Car =", Car); console.log("ford =", ford); console.log("Car.prototype =", Car.prototype); console.log("ford.__proto__ =", ford.__proto__); 

Ce mot this clé dans la fonction se réfère à l'objet créé par le new mot-clé.

Si vous souhaitez créer une propriété "statique", vous devriez la configurer directement sur le nom de la fonction comme ceci:

 function Car() { } Car.make = "Ford"; 

Fondamentalement, vous ne pouvez pas avoir "les deux". La propriété est statique (sur la fonction elle-même) ou instance (sur l'objet créé par new )