Cette fois, je vais vous apporter une analyse des fonctions sous-jacentes de la portée javascript, et quelles sont les précautions lors de l'utilisation de la portée sous-jacente de javascript. Ce qui suit est un cas pratique. , jetons un coup d'oeil.
balise : javascript
est générée dans le code source du programme où définit la variable , et est déterminée lors de l'étape de codage du programme. JavaScript est divisé en portée globale (contexte global : window
/global
) et en portée locale (portée locale, également connue sous le nom de portée de la fonction contexte de la fonction). En termes simples, la portée est le 生成环境
ou 上下文
de la fonction actuelle (remarque : ne le confondez pas avec le 执行上下文
mentionné plus tard), y compris les variables définies dans la fonction actuelle et la référence à la portée extérieure.
Portée en javascript :
作用域(Scope) | - |
---|---|
window/global Scope | 全局作用域 |
function Scope | 函数作用域 |
Block Scope | 块作用域(ES6) |
eval Scope | eval作用域 |
Scope définit un ensemble de règles qui définissent la manière dont le moteur interroge les variables en fonction des identifiants de la portée actuelle et des portées imbriquées. D'autre part, la chaîne de portées composée de N portées détermine la valeur trouvée par l'identifiant dans la portée de la fonction.
Nous pouvons donc le résumer ainsi : La portée détermine la visibilité des variables définies dans le contexte actuel, c'est-à-dire que la portée de niveau inférieur est accessible. Et la chaîne de portée (Scope Chain) détermine également comment trouver la valeur de l'identifiant dans le contexte actuel.
La portée est divisée en portée lexicale et portée dynamique. La portée lexicale est littéralement la portée définie au stade lexical. En d’autres termes, la portée est définie lorsque le lexer traite le code source, en fonction de l’emplacement des variables et des blocs dans le code source. JavaScript utilise une portée lexicale.
Règles d'accès aux variables :
Si la variable a est définie à l'intérieur d'une fonction, d'autres variables à l'intérieur de la fonction ont la permission d'accéder à la variable a, mais le code en dehors de la fonction le fait ne pas avoir accès aux autorisations variables de a. Par conséquent, les variables de la même portée peuvent accéder les unes aux autres, c'est-à-dire que a, b et c peuvent accéder les uns aux autres s'ils se trouvent dans la même portée. C'est comme si une mère poule avait un bébé. Les bébés poules peuvent jouer entre elles, mais les autres poules ne peuvent pas jouer avec elles. Pourquoi ? Parce que Mère Poulet ne le permet pas ~ o(^∀^)o.
let a = 1 function foo () { let b = 1 + a let c = 2 console.log(b) // 2 } console.log(c) // error 全局作用无法访问到 c foo()
Si la variable a est définie dans la portée globale (window/global), alors le code exécuté dans la portée locale sous la portée globale ou dans autrement dit Toute expression peut accéder à la valeur de la variable a. Une variable portant le même nom (a) dans une variable locale tronquera l'accès à la variable globale a. (La variable a ici équivaut à l'éleveur, et l'éleveur nourrira les poulets au moment opportun. Cependant, afin de réduire les coûts, l'agriculteur stipule que l'éleveur doit nourrir les poulets à proximité. Lorsque l'éleveur 1 quitte les poulets , Plus récemment, d'autres éleveurs ne pourront pas traverser la rivière Yalu pour nourrir les poulets)
let a = 1 let b = 2 function foo () { let b = 3 function too () { console.log(a) // 1 console.log(b) // 3 } too() } foo()
Encore une fois, la portée javascript limitera strictement la gamme accessible de variables : c'est-à-dire en fonction de l'emplacement du code et des blocs dans le code source auquel la portée imbriquée a accès à la portée imbriquée. (Cette règle montre que toute la ferme a des règles et ne peut pas se nourrir dans la direction opposée.)
La chaîne de portée est composée de l'environnement actuel et de la couche supérieure. L'environnement est composé d'une série de portées, qui garantissent que l'environnement d'exécution actuel dispose d'un accès ordonné aux variables et aux fonctions conformes aux autorisations d'accès.
L'explication ci-dessus est un peu obscure. Pour quelqu'un comme moi qui a un cerveau faible, j'ai besoin de la « lire » plusieurs fois dans mon cerveau pour la comprendre. Alors, à quoi sert la chaîne de portée ? En termes simples, la chaîne de portée analyse l'identifiant et est chargée de renvoyer la valeur de la variable dont dépend l'expression lors de son exécution. Une réponse plus simple : les chaînes de portées sont utilisées pour rechercher des variables. Une chaîne de portées est une série de portées connectées en série.
Pendant l'exécution de la fonction, chaque fois qu'une variable est rencontrée, elle passera par un processus de résolution d'identifiant pour décider où obtenir et stocker les données. Ce processus commence à partir de la tête de la chaîne de portée, qui est la portée de la fonction en cours d'exécution (de gauche à droite dans la figure ci-dessous), et recherche un identifiant avec le même nom, s'il est trouvé, la valeur correspondant à l'identifiant. est renvoyé. S'il n'est pas trouvé, continuez. La portée suivante dans la chaîne de portées est recherchée. Si aucune portée n'est trouvée, l'identifiant est considéré comme non défini. Lors de l'exécution de la fonction, chaque identifiant méritant d'être analysé doit passer par ce processus de recherche.
Afin d'analyser le problème concrètement, nous pouvons supposer que la chaîne de portée est un tableau (Scope Array), et que les membres du tableau sont composés d'une série d'objets variables . Nous pouvons utiliser le canal unidirectionnel du tableau, c'est-à-dire que la figure ci-dessus simule l'interrogation des identifiants dans l'objet variable de gauche à droite, afin que nous puissions accéder aux variables dans la portée supérieure. au niveau le plus élevé (portée globale), et une fois trouvé, la recherche s'arrête. Par conséquent, la variable interne peut protéger la variable externe du même nom. Pensez-y, si les variables ne sont pas recherchées de l'intérieur vers l'extérieur, la conception entière du langage deviendra N plus compliquée (nous devons concevoir un ensemble complexe de règles pour que les bébés poulets trouvent de la nourriture)
Toujours avec la châtaigne ci-dessus :
let a = 1 let b = 2 function foo () { let b = 3 function too () { console.log(a) // 1 console.log(b) // 3 } too() } foo()
La structure de portée imbriquée ressemble à ceci :
栗子中,当 javascript 引擎执行到函数 too 时, 全局、函数 foo、函数 too 的上下文分别会被创建。上下文内包含它们各自的变量对象和作用域链(注意: 作用域链包含可访问到的上层作用域的变量对象,在上下文创建阶段根据作用域规则被收集起来形成一个可访问链),我们设定他们的变量对象分别为VO(global),VO(foo), VO(too)。而 too 的作用域链,则同时包含了这三个变量对象,所以 too 的执行上下文可如下表示:
too = { VO: {...}, // 变量对象 scopeChain: [VO(too), VO(foo), VO(global)], // 作用域链 }
我们可以直接用scopeChain
来表示作用域链数组,数组的第一项scopeChain[0]为作用域链的最前端(当前函数的变量对象),而数组的最后一项,为作用域链的最末端(全局变量对象 window )。所有作用域链的最末端都为全局变量对象。
再举个栗子:
let a = 1 function foo() { console.log(a) } function too() { let a = 2 foo() } too() // 1
这个栗子如果对作用域的特点理解不透彻很容易以为输出是2。但其实最终输出的是 1。 foo() 在执行的时候先在当前作用域内查找变量 a 。然后根据函数定义时的作用域关系会在当前作用域的上层作用域里查找变量标识符 a,所以最后查到的是全局作用域的 a 而不是 foo函数里面的 a 。
变量对象、执行上下文会在后面介绍。
在 JavaScript 中,函数和函数声明时的词法作用域形成闭包。我们来看个闭包的例子
let a = 1 function foo() { let a = 2 function too() { console.log(a) } return too } foo()() // 2
这是一个闭包的栗子,一个函数执行后返回另一个可执行函数,被返回的函数保留有对它定义时外层函数作用域的访问权。foo()()
调用时依次执行了 foo、too 函数。too 虽然是在全局作用域里执行的,但是too定义在 foo 作用域里面,根据作用域链规则取最近的嵌套作用域的属性 a = 2。
再拿农场的故事做比如。农场主发现还有一种方法会更节约成本,就是让每个鸡妈妈作为家庭成员的‘饲养员’, 从而改变了之前的‘饲养结构’。
关于闭包会在后面的章节里也会有介绍。
从作用域链的结构可以发现,javascript
引擎在查找变量标识符时是依据作用域链依次向上查找的。当标识符所在的作用域位于作用域链的更深的位置,读写的时候相对就慢一些。所以在编写代码的时候应尽量少使用全局代码,尽可能的将全局的变量缓存在局部作用域中。
不加强记忆很容记错作用域与后面将要介绍的执行上下文的区别。代码的执行过程分为编译阶段和解释执行阶段。始终应该记住javascript
作用域在源代码的编码阶段就确定了,而作用域链是在编译阶段被收集到执行上下文的变量对象里的。所以作用域、作用域链都是在当前运行环境内代码执行前就确定了。这里暂且不过多的展开执行上下文的概念,可以关注后续文章。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
EasyCanvas绘图库在Pixeler项目开发中使用实战总结
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!