码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 作用域链、块级块作用域、var / let / const


    目录

    1. 作用域 / 作用域链

    1.1 什么是作用域 / 作用域链?

    1.2 作用域类型

    1.3 作用域链的创建和变化

    1.3.1 函数创建

    1.3.2 函数激活

    1.3.3 函数执行上下文中,作用域链、变量对象的创建过程

    2. 块级块作用域

    2.1 为什么需要块级作用域?

    2.1.1 在 for 语句中声明的循环变量,会泄露为全局变量

    2.1.2 内层变量覆盖外层变量

    2.2 块级作用域特点

    3. var / let / const

    3.1 三者区别概述

    3.2 var / let 经典案例

    3.2.1 用 var 定义 i 变量,循环后打印 i 的值

    3.2.2 用 let 定义 i 变量,循环后打印 i 的值

    3.3 使用闭包 + 函数作用域,实现 let 块级作用域的效果


    对于每个执行上下文,都有三个重要属性:

    • 变量对象(Variable object,VO)
    • 作用域链(Scope chain)
    • this

    1. 作用域 / 作用域链

    1.1 什么是作用域 / 作用域链?

    当查找变量的时候:

    • 先从 当前执行上下文 的变量对象中查找
    • 如果没有找到,就会从父级执行上下文的变量对象中查找
    • 一直找到全局上下文的变量对象,也就是全局对象

    这样,由 多个执行上下文的 变量对象 构成的链表,就叫做作用域链

    通俗理解:可以访问变量的集合,不同的函数执行上下文,全局执行上下文 都可以访问变量

    作用域最大的作用 —— 隔离同名变量,防止冲突(不同作用域下,同名变量不会有冲突)

    1.2 作用域类型

    全局作用域

    函数作用域 —— 声明在函数内部的变量,函数作用域在 定义函数时 就决定了

    块级作用域(ES6 新增)—— 由 {} 包裹,if 和 for 里面的 {},也属于块级作用域

    1.3 作用域链的创建和变化

    函数的 创建 和 激活 两个时期,对应了 作用域链 的 创建 和 变化;

    1.3.1 函数创建

    为什么说 函数的作用域在函数定义的时候就决定 了呢?

    • 函数有一个内部属性 [[scope]]
    • 当函数创建时,[[scope]] 会保存所有父变量对象,也就是说 [[scope]] 是所有父变量对象的层级链
    • 注意:[[scope]] 并不代表完整的作用域链!

    举个栗子~

    1. function foo() {
    2. function bar() {
    3. ...
    4. }
    5. }

    函数创建时,他们的 [[scope]] 长这样:

    1. foo.[[scope]] = [
    2. globalContext.VO
    3. ];
    4. bar.[[scope]] = [
    5. fooContext.AO,
    6. globalContext.VO
    7. ];

     

    1.3.2 函数激活

    当函数激活时,进入函数执行上下文,会将 活动对象(当前使用的函数执行上下文/作用域)添加到作用链的前端

    将此时的 执行上下文的作用域链,命名为 Scope

    Scope = [AO].concat([[Scope]]);

    至此,作用域链创建完毕

    1.3.3 函数执行上下文中,作用域链、变量对象的创建过程

    1. var scope = "global scope"; // 全局作用域
    2. function checkscope(){
    3. var scope2 = 'local scope'; // 函数作用域
    4. return scope2;
    5. }
    6. // 函数调用
    7. checkscope();

     

    checkscope 函数被创建,保存作用域链到 内部属性 [[scope]](ps:此时保存的是根据词法所生成的作用域链)

    1. checkscope.[[scope]] = [
    2. globalContext.VO
    3. ];

     

    执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈 

    1. ECStack = [
    2. checkscopeContext,
    3. globalContext
    4. ];

    checkscope 函数并不立刻执行,开始做准备工作;

    第一步:复制函数 [[scope]] 属性创建作用域链(checkscope 执行的时候,会复制最初的作用域链,作为自己作用域链的初始化)

    1. checkscopeContext = {
    2. Scope: checkscope.[[scope]],
    3. }

    第二步:用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明(根据环境生成变量对象)

    1. checkscopeContext = {
    2. AO: {
    3. arguments: {
    4. length: 0
    5. },
    6. scope2: undefined
    7. },
    8. Scope: checkscope.[[scope]],
    9. }

    第三步:将活动对象压入 checkscope 作用域链顶端(将变量对象,添加到复制的作用域链头部)

    1. checkscopeContext = {
    2. AO: {
    3. arguments: {
    4. length: 0
    5. },
    6. scope2: undefined
    7. },
    8. Scope: [AO, [[Scope]]]
    9. }

    准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值

    1. checkscopeContext = {
    2. AO: {
    3. arguments: {
    4. length: 0
    5. },
    6. scope2: 'local scope'
    7. },
    8. Scope: [AO, [[Scope]]]
    9. }

     

     查找 scope2 的值,返回 scope2 的值,函数执行完毕,函数上下文 从执行栈中弹出

    1. ECStack = [
    2. globalContext
    3. ];

    关于上述过程中的一些问题:

    • 为什么会有两个作用域链? —— 函数创建时,并不能确定最终的作用域的样子
    • 为什么会采用复制的方式而不是直接修改 [[scope]] 呢? —— 因为函数会被调用很多次

    参考文章:

    JavaScript深入之作用域链 · Issue #6 · mqyqingfeng/Blog · GitHub前言 在《JavaScript深入之执行上下文栈》中讲到,当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。 对于每个执行上下文,都有三个重要属性: 变量对象(Variable object,VO) 作用域链(Scope chain) this 今天重点讲讲作用域链。 作用域链 在《JavaScript深入之变量对象》中讲到,当查找变量的时候,会先从当前上下文的变量对象中查找,...https://github.com/mqyqingfeng/Blog/issues/6

    2. 块级块作用域

    2.1 为什么需要块级作用域?

    2.1.1 在 for 语句中声明的循环变量,会泄露为全局变量

    如下所示,循环完成后,我们仍然可以访问到变量 i,但是我们并不需要变量 i 在全局中生效

    1. for(var i = 0; i <= 5; i++) {
    2. console.log("hello");
    3. }
    4. console.log(i); // 5

    2.1.2 内层变量覆盖外层变量

    由于变量提升,函数内部的 temp 变量会提升至全局,替代原来的 temp;

    由于未走 false 语句,导致未进行变量赋值,因此输出 undefined;

    1. var temp = new Date();
    2. function f() {
    3. console.log(temp);
    4. if (false) {
    5. var temp = "hello";
    6. }
    7. }
    8. f(); // undefined

    2.2 块级作用域特点

    ES6之前,临时变量被封装在IIFE(立即执行函数)中,就不会污染上层函数;

    在块级作用域中,可通过 let 和 const 声明变量,该变量在 除了当前块级作用域外 的范围内无法被访问;

    其他特点:

    • 允许块级作用域任意嵌套
    • 外层作用域无法读取内层作用域的变量
    • 内层作用域可以定义外层作用域的同名变量

    3. var / let / const

    3.1 三者区别概述

    var 定义变量 —— 没有块的概念,可以跨块访问, 可以变量提升

    let 定义变量 —— 只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明

    const 定义常量 —— 使用时必须初始化(即必须赋值),只能在块作用域里访问,不能修改,无变量提升,不可以重复声明

    3.2 var / let 经典案例

    3.2.1 用 var 定义 i 变量,循环后打印 i 的值

    i 是 var 声明的,在全局范围内都有效,全局只有一个变量 i,输出的是最后一轮的 i,也就是 10

    1. var a = [];
    2. for (var i = 0; i < 10; i++) {
    3. a[i] = function() {
    4. console.log(i);
    5. };
    6. }
    7. a[0](); // 10

    3.2.2 用 let 定义 i 变量,循环后打印 i 的值

    i 是 let 声明的,for循环体内部,每循环一次,都生成一个单独的块级作用域,相互独立,也就是有 10个 let i,在十个块级作用域里,不会相互影响覆盖

    1. var a = [];
    2. for (let i = 0; i < 10; i++) {
    3. a[i] = function() {
    4. console.log(i);
    5. };
    6. }
    7. a[0](); // 0

    3.3 使用闭包 + 函数作用域,实现 let 块级作用域的效果

    1. var a = [];
    2. // 闭包,可以访问其他函数内部变量的函数
    3. var _loop = function _loop(i) {
    4. a[i] = function() {
    5. console.log(i);
    6. };
    7. };
    8. for (var i = 0; i < 10; i++) {
    9. _loop(i);
    10. }
    11. a[0](); // 0

    参考文章:

    js块级作用域和let,const,var区别 - 野生夜神月 - 博客园1. 块作用域{ } JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }也https://www.cnblogs.com/moumoon/p/10985250.htmlES6 入门教程https://es6.ruanyifeng.com/#docs/let

  • 相关阅读:
    攻防世界----favorite_number
    protobuf语法之proto2简述
    OT算法在协同编辑器中的应用
    2021年数维杯数学建模A题外卖骑手的送餐危机求解全过程文档及程序
    7、设计模式之桥接模式(Bridge)
    港联证券:“火箭蛋”来袭 蛋价涨势能否延续?
    Oracle-触发器和程序包
    每日三题 9.29
    RT-Thread学习笔记(一):认识RT-Thread系统
    Day11--配置tabBar效果
  • 原文地址:https://blog.csdn.net/Lyrelion/article/details/127832162
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号