Maison > Article > interface Web > Compréhension approfondie des portées JavaScript
Introduction
JavaScript est un langage full stack, en particulier en 2016, j'entends souvent dire que JavaScript va dominer le monde. Il y a même des rumeurs selon lesquelles vous pouvez trouver un emploi si vous connaissez Vue.js en 2016, tout comme vous pouvez trouver un emploi si vous connaissez TableView sur iOS à l'époque. (tableView est assez basé sur ListView d'Android, mais maintenant ils utilisent essentiellement RecyclerView)
Les technologies frontales populaires en 2016 sont essentiellement liées à JavaScript, comme le multiplateforme mobile React Native produit par Facebook et Alibaba. Weex, la technologie de réparation à chaud JSPath et Node.js sur le backend (une pile technologique que j'aime beaucoup). Je suis allé sur gibhub hier soir pour le vérifier. Le nombre d'étoiles pour Vue a dépassé celui de jQuery. le nombre d'étoiles ne prouve rien, du moins nous l'avons déjà fait. Je peux voir que la pensée frontale a changé depuis l'opération de document précédente vers un développement basé sur les données (si vous êtes intéressé, je peux combiner Android, iOS et Vue plus tard pour démontrer ce changement de pensée avec une petite démo). Certaines entreprises ont même commencé à essayer d'utiliser Hungry Element produit par cette société pour remplacer EasyUI (les étudiants qui ont fait du back-end doivent savoir qu'EasyUI est vraiment de qualité AV...)
La technologie JS émerge sans fin, et il y avait déjà eu un article très populaire, apprendre JS en 2016. De quel genre d'expérience il s'agissait, cela a effrayé de nombreuses personnes en un instant. Tout le monde s'est concentré sur les frameworks et les nouvelles technologies. , mais JS natif a été laissé de côté, je voulais donc partager avec vous quelques problèmes de base de JS. Communiquons ensemble (j'espère que tout le monde m'emmènera voler, mais ne me laissez pas voler sur Malaysia Airlines...)
Portée en JavaScript
Une question simple :
<script> var str1 = "hello"; var str2 = "world"; function t1() { console.log(str1); console.log(str2); var str2 = "toby"; console.log(str2); } //这里会输出什么? t1(); </script>
Il s'agit d'un problème de portée JS très simple, mais plus vous insistez sur le mot "simple", plus il est facile pour les gens de relâcher leur vigilance, ce qui amène certains étudiants à répondre sans réfléchir
gir bonjour
gird monde
gi toby
Mais le résultat est la sortie
gi bonjour
gi non défini
Quantity toby
Alors c'est étrange, pourquoi y a-t-il undefined ? Non Devrait-il être world ? Tout d'abord, il faut comprendre que la recherche de variables suivra le principe de proximité, donc js le fera ? recherchez d'abord dans la fonction, puis regardez à l'extérieur si elle est introuvable. Il y a str2 dans la fonction, mais lors de l'exécution vers console.log(str2 ) lorsque str2 n'est pas défini, donc indéfini
analyse lexicale
Pour savoir ce qui se passe, il faut savoir pourquoi, alors regardons un quelques exemples supplémentaires
Exemple 1
<script> function t(userName) { console.log(userName);//这里输出什么? function userName() { console.log('tom'); } } t('toby'); </script>
Quel est le résultat de sortie Cet exemple semble être différent de celui ci-dessus. les mathématiques scolaires et la confusion lorsque le type de question change. À ce moment-là, certains élèves peuvent penser que c'est Toby, mais le résultat réel est
function userName() { console.log('tom'); }
Pourquoi est-ce fonctionnel Quoi ? Le problème de portée peut être obtenu grâce à un "ensemble de formules". Cette formule est une analyse lexicale dans JS. L'une des choses qui doivent être faites avant que la fonction dans JS ne soit exécutée est une analyse lexicale. Alors, qu'est-ce que c'est exactement ? ? Analyser les paramètres, analyser les déclarations de variables, analyser les déclarations de fonctions, puis nous utiliserons cette question pour appliquer la formule
Lorsque t('toby') sera exécuté, deux étapes commenceront, une C'est l'étape d'analyse. Après l'analyse, elle passe à l'étape d'exécution
Étape d'analyse :
● Au moment où la fonction s'exécute, un objet actif (ci-après dénommé l'objet AO) sera généré, qui pourra être trouvé dans la portée d'une fonction. Toutes les variables de sont sur AO À ce stade, le code est exprimé comme : t.AO = {}
● Paramètres d'analyse : recevoir les paramètres, utiliser les noms de paramètres comme attributs et. valeurs des paramètres en tant que valeurs d'attribut, car il n'y a pas de paramètres, donc le résultat de l'analyse est exprimé en code : t.AO = {userName : toby}
● Analyse de l'instruction var : il n'y a pas d'instruction var. dans la fonction t, sautez
● Analyse de l'instruction de fonction : cette déclaration de fonction a une caractéristique S'il y a un attribut du même nom que le nom de la fonction sur l'AO, il sera écrasé par cette fonction. Étant donné que la fonction est également un type de variable dans le champ JS, elle est exprimée en code comme suit : t.AO = { userName: function userName() {console.log('tom');}}
Phase d'exécution :
Lors de l'exécution de t('toby'), lors de l'exécution sur console.log(userName) Lorsque , t.AO.userName est appelé, le résultat final est donc function userName() {console. log('tom');}
Exemple 2
<script> function t(userName) { console.log(userName);//这里输出什么? var userName = function () { console.log('tom'); } } t('toby'); </script>
Alors quel est le résultat ici Cela semble être différent de l'exemple ci-dessus, et il ? est à nouveau tombé dans la confusion ? N'ayez pas peur des ennuis, et suivez fermement le processus selon la formule (l'exemple ci-dessus est écrit comme Plus détaillé, l'analyse suivante sera simplement écrite)
Avant l'analyse, vous devez d'abord comprendre deux concepts, l'un est appelé déclaration de fonction et l'autre est appelé expression de fonction
//这个叫函数声明 function userName() { console.log('tom'); } //这个叫函数表达式 var userName = function () { console.log('tom'); }
Étape d'analyse :
● Créer un objet AO, t.AO = {}
● Paramètres d'analyse : t.AO = {userName : toby}
● 分析var声明: 在AO上,形成一个属性,以var的变量名为属性名,值为undefined,(因为是先分析,后执行,这只是词法分析阶段,并不是执行阶段,分析阶段值都是undefined,如果执行阶段有赋值操作,那值会按照正常赋值改变),也就是说代码应该表示为:t.AO = {userName : undefined},但是还有另外一个原则,那就是如果AO有已经有同名属性,则不影响(也就是什么事都不做),由于分析参数时,AO上已经有userName这个属性了,所以按照这个原则,此时什么事都不做,也就是说,此时按照分析参数时的结果t.AO = {userName : toby}
● 分析函数声明: 此时没有函数声明,略过
执行阶段:
调用t.AO.userName,所以,最后的输出结果是toby
例子3
<script> t(); t2(); function t() { console.log('toby');//这里会输出什么? } var t2 = function () { console.log('hello toby');//这里会输出什么? }; </script>
那么我们再来看一个例子,这下彻底回到高中时代,做了两个例子好像感觉掌握了,结果考试你给来看这个?
答案是,t()输出为toby,t2()则会报错.这又是为什么?
● t()可以调用是因为在词法分析的过程,就已经完成了t函数的分析,所以可以调用
● t2()不能调用是因为在词法分析的阶段,分析到有一个t2声明,在AO上只是形成了一个属性,但是值为undefined
例子4
<script> function t(userName) { console.log(userName);//这里输出什么? function userName() { console.log(userName);//这里输出什么? } userName(); } t('toby'); </script>
函数里面套函数,这次竟然又和前面不一样了...这次我不说答案了,直接先套公式走一波
t('toby')的分析和执行阶段
分析阶段:
● 创建AO对象,t.AO = {}
● 分析参数: t.AO = {userName : toby}
● 分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}
● 分析函数声明: 有同名属性,覆盖: t.AO = {userName : function userName() {console.log(userName);}}
执行阶段: t.AO.userName 输出为function userName() {console.log(userName);}}
userName()的分析和执行阶段
这里也要搞清楚两个概念
//执行userName()分析的是 function () { console.log(userName); }; //而不是 var userName = function () { console.log(userName); };
分析阶段:
● 创建AO对象,userName.AO = {}
● 分析参数: 无,略过
● 分析var声明: 无,略过
● 分析函数声明: 无,略过
执行阶段: 因为此时userName.AO = {}是个空对象,无法执行userName.AO.userName,所以会向上一层找,所以输出t.AO.userName的结果,也就是function userName() {console.log(userName);}}
例子5
<script> function t(userName) { console.log(userName);//这里输出什么? var userName = function () { console.log(userName);//这里输出什么? } userName(); } t('toby'); </script>
好吧,我保证这个是最后一道...这个输出结果是什么呢?我们只要坚定公式没问题,就一定能得出结果,那么再套公式走一波
t('toby')的分析和执行阶段
分析阶段:
● 创建AO对象,t.AO = {}
● 分析参数: t.AO = {userName : toby}
● 分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}
● 分析函数声明: 无,略过
执行阶段: 执行console.log(userName);时调用t.AO.userName 输出为toby,执行完后,代码继续往下执行,那么就到了进行var的赋值操作(var的分析和执行的区别看例子2中我有解释),此时t.AO = {userName : function userName() {console.log(userName);}}},代码继续往下执行,接着就执行到了userName()
userName()的分析和执行阶段
分析阶段:
● 创建AO对象,userName.AO = {}
● 分析参数: 无,略过
● 分析var声明: 无,略过
● 分析函数声明: 无,略过
执行阶段: 按照例子4我们知道userName.AO是个空对象,所以会往上调用t.AO.userName,所以输出为:function userName() {console.log(userName);}}}
总结
JavaScript作用域会先在自己的AO上找,找不到就到父函数的AO上找,再找不到再找上一层的AO,直到找到window.这样就形成一条链,这条AO链就是JavaScript中的作用域链.JavaScript中有两条很重要的链,一条是作用域链,一条是原型链,