javascript 的执行上下文与作用域

目录

    • 1. 初步了解 上下文(context)
    • 2. 全局上下文(global context)
    • 3. 上下文栈 (context stack)
    • 4. 作用域链( scope chain)
    • 5. 作用域(scope)
    • 6. 作用域链增强
    • 7. 变量声明
      • 7.1 var 声明变量
      • 7.2 let 声明变量
      • 7.3 const 常量声明

1. 初步了解 上下文(context)

   上下文(context) 全称 执行上下文 (execution context) 。

  变量和函数的上下文 决定了它们可以访问哪些数据,以及它们的行为。所以 上下文 也可以说是 变量和函数所处的环境

  每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上。虽然无法通过代码访问变量对象,但后台处理数据总是用到它。

2. 全局上下文(global context)

  全局上下文是最外层的上下文。浏览器中,全局上下文就是 window 对象,所有通过 var 定义的全局变量和函数都会成为window对象的属性和方法
  使用 let 和const 的顶级声明不会定义在全局上下文中,但在作用链解析上效果是一样的。上下文在其所有代码都执行完毕后会被销毁,包含定义在它上面的所有变量和函数,全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器。

3. 上下文栈 (context stack)

  每个函数调用都有自己的上下文,当代码执行到某个函数时,函数的上下文被推到(push)一个 上下文的栈结构 中,忘了栈结构的,可以查看 栈的基本操作。在函数执行完之后,上下文栈会弹出(pop)该函数的上下文,将控制权返还给之前的执行上下文。ECMAScript程序的执行流就是通过这个上下文栈进行控制的。

4. 作用域链( scope chain)

   学习作用域,先了解一下做应用于链。这个链,其实就是上面说的上下文栈结构。
  上下文中的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终处于作用域链的最前端。如果上下文是函数,则其活动对象(activation object) 用作 变量对象。活动对象最初只有一个变量:参数(arguments)。题外话:上面提到的全局上下文是没有这个变量的。作用域链中的下一个变量对象来自包含上下文,再下一个对象来自下一个包含上下文。依次类推直至全局上下文。全局上下文的变量对象始终是作用域链的最后一个变量对象。

  代码执行的标识符解析是通过沿作用域链逐级搜索标识符名称完成的。搜索过程始终从作用域链的最前端开始,然后逐级往后,直到找到标识符。(未找到标识符,通常会报错)

5. 作用域(scope)

作用域,就是 变量可用性的范围。通俗的讲,就是哪些函数可以访问到这个变量。
先看下面的例子:


var color = "blue";
function changeColor() {if (color === "blue") {color = "red";} else{color = "blue";}
}
changeColor();

上述函数 changeColor() 的作用域包含两个对象:一个是它自己的变量对象(即 arguments 对象的那个),另一个是全局上下文的变量对象。这个函数内部之所以能够访问变量color,就是因为可以在作用域链中找到它。
局部作用域中定义的变量可用于在局部上下文中替换全局变量。看看下面这个例子:


var color = "blue";
function changeColor() {let anotherColor = "red";function swapColors(){let tempColor = anotherColor;anotherColor = color;color = tempColor;//此处为swapColors内部,可以访问color、anotherColor 和tempColor}//此处为changeColor内部,可以访问 color 和anotherColor ,但访问不到tempColor
}
// 这里只能访问color 
changeColor();

   以上代码涉及到3个上下文:全局上下文、changeColor()的局部上下文和 swapColors()的局部上下文。他们的关系是全局上下文包含 changeColor()上下文,changeColor()包含 swapColors()。
他们的关系如下图:
在这里插入图片描述

   对于swapColos来说,发现变量color, 先在自己的局部上下文 swapColors() 中搜索,发现自己并没有color的定义。往父级上下文changeColor () 中搜索,还是没有搜索到,则到全局作用域 window中搜索,本次搜索到了。

6. 作用域链增强

上下文包含全局上下文和函数上下文两种方式,按照栈的结构形成作用域链,按照顺序向上级搜索,但是有两种情况增强作用域链:

 a.  try/catch 语句的catch块b.  with 语句

这两种情况 直接在作用域链前端临时添加一个上下文,在代码执行后被删除。
对于with语句来说,会想作用域链前端添加一个指定的对象;
对于catch语句而言,则会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明。

function buildUrl(){let qs = "?debug=true";let locHost;with (location) {let url = href + qs;locHost = host;}return url;  // 这里的url为 undefined
}

   上面代码中,with 语句将 location 对象作为上下文,因此 location会被添加到作用域链前端。buildUrl函数中定义了一个变量 qs。 当 with 语句中的代码应用变量 href 时, 实际上引用的是 location.href,也就是自己变量对象的属性。。
在这里插入图片描述

   在引用 qs 时,引用的则是定义在 buildUrl() 中的那个变量,它定义在函数上下文的变量对象上。而在with 语句中如果使用 var声明的变量 url 会成为函数上下文的一部分,可以作为函数的值返回;但这里使用 let 声明的变量url ,因为被限制在块级作用域,所以在with 块之外没有定义。

7. 变量声明

7.1 var 声明变量

   在使用 var 声明变量时,变量会被自动添加到最接近的上下文。在函数中,最近的上下文就是函数的局部上下文;在with语句中,最接近的上下文也是函数上下文。如果变量未经声明就被初始化了,那么它就会自动被添加到全局上下文。

	function add(num1,num2){var sum = num1 + num2 ;return sum;}let  result = add(10,20); // 30 console.log(sum); //这里会报错,因为变量 sum 是在 函数 add() 的上下文中。

   上面例子中,函数 add() 定义的局部变量 sum ,在函数外部是访问不了的。因为sum是在函数 add() 的上下文中。再看下面的例子:

	function add(num1,num2){sum = num1 + num2 ;return sum;}let  result = add(10,20);  // 30 console.log(sum); 				// 30,这里不会报错

   这一次 函数外部,就能直接访问sum。因为sum没有声明,直接初始化,就被当做全局变量了。

注意 未经声明而初始化变量是 JavaScript 编程中一个非常常见的错误,会导致很多问题。
为此,读者在初始化变量之前一定要先声明变量。在严格模式下,未经声明就初始化变量
会报错 。(摘自 《javascript 高级程序设计》)

   var 声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。这个现象叫作“提升”(hoisting)。提升让同一作用域中的代码不必考虑变量是否已经声明就可以直接使用。可是在实践中,提升也会导致合法却奇怪的现象,即在变量声明之前使用变量。下面的例子展示了在全局作用域中两段等价的代码:

var name = "bourne";
//等价于
name = "bourne";
var name;

两个等价函数:

	function func1(){var name ="bourne";}//等价于function func2(){var name;name = "bourne" ;}

   通过在声明之前打印变量,可以验证变量会被提升。由于声明的提升会输出 未赋值(undefined) ,而不是 引用错误(Reference Error)

 console.log(name);		//undefined(未赋值), 而不是 Reference Error (引用错误)var name = "bourne";function (){console.log(name); //undefined(未赋值), 而不是 Reference Error (引用错误)var name = "bourne";}

7.2 let 声明变量

ES6新增 的 let 关键字。 let 块级作用域, var 为函数级作用域。块级作用域由最近的一对包含花括号 { } 界定 。这也就意味着 ,if 块,while 块,function 块,甚至 单独的只有花括号的内部代码块 也是 let 声明 变量的作用域。


if (true) {let a ;
}
console.log(a); // Reference Error : a 无定义while (true){let b;
}
console.log(b); // Reference Error : b 无定义function func() {let c;
}
console.log(c); // Reference Error : c 无定义//下面只有花括号的情况,(js解释器默认其合法的)
{let d;
}
console.log(d); // Reference Error : d 无定义

   let 与 var 还有不同之处是 同一作用域内不能声明两次。重复的 var 声明 会被忽略(个人认为这是 js 设计之初考虑不周的地方),而重复的 let 声明 会抛出 语法错误(Syntax Error)。

var a ;
var a ; //这里不会报错
let b ;
let b ; // Syntax Error ,因为 标识符 b 已经声明过了

let 的特性使它非常适合在循环中声明迭代变量。使用 var 声明的迭代变量会泄漏到循环外部,这种情况应该避免。来看下面两个例子:

for (var i = 0 ; i < 5 ; ++i){}
console.log(i); //5 此处仍然可以识别到 ifor (let j = 0; j < 10; ++j){}
console.log(j);		//Reference Error, 无法识别 j了

严格来讲, let 在 JavaScript 运行时中也会被提升,但由于“暂时性死区”(temporal dead zone)的缘故,实际上不能在声明之前使用 let 变量。因此,从写 JavaScript 代码的角度说, let 的提升跟 var是不一样的。(这一段摘自《javascript 高级程序设计》中原文,因为没看懂,直接摘抄过来了)

7.3 const 常量声明

ES6 新增关键字 const。使用const 声明变量必须同时初始化(因为常量放在内存的数据区中的常量区(只读的,所以不能后面修改))。声明以后,在其生命周期的任何时候都不能再重新赋予新值。

const a ;  //Syntax Error,语法错误,因为常量声明时,没有初始化
const b = 3;
console.log(b); //3
b = 4 ; //TypeError :给常量赋值

const 也是块级作用域,和 let 的作用域一样:

if (true) {const a ;
}
console.log(a); // Reference Error : a 无定义while (true){const b;
}
console.log(b); // Reference Error : b 无定义function func() {const c;
}
console.log(c); // Reference Error : c 无定义//下面只有花括号的情况,(js解释器默认其合法的)
{const d;
}
console.log(d); // Reference Error : d 无定义

const 声明 引用类型变量,是否可以修改其属性?看一下下面的例子:


//第一种情况
const people = {};
people = {};	// TypeError: 类型错误,因为给常量赋值了//第二种情况
const people2 = {name:"bourne"};
people2.name = "pitt"; 		
console.log(people2.name); 	//输出 pitt ,属性被修改了

上面第二种情况,之所以能够修改对象的属性,是因为 变量 people 内保存的其实是 {name:“bourne”} 这个对象的地址。地址是常量,故不可以修改,但是对象存放在内存堆区,它的属性是可以修改的。

那么想要连对象的属性也不能修改,有没有办法呢? 答案:有。就是 Object.freeze()。
看看下面代码:

 const people3 =Object.freeze({name:"bourne"});people3.name ="Pitt";console.log(people2.name);  //输出 bourne, 属性没有被修改。

由于 const 声明暗示变量的值是单一类型且不可修改,JavaScript 运行时编译器可以将其所有实例都替换成实际的值,而不会通过查询表进行变量查找。(虽然 js是解释型语言,但是 谷歌的V8引擎 采用先编译后运行)

注意 :开发实践表明,如果开发流程并不会因此而受很大影响,就应该尽可能地多使用 const 声明,除非确实需要一个将来会重新赋值的变量。这样可以从根本上保证提前发现重新赋值导致的 bug。摘自《javascript 高级程序设计》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/49122.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

轨迹优化 | 基于ESDF的共轭梯度优化算法(附ROS C++/Python仿真)

目录 0 专栏介绍1 数值优化&#xff1a;共轭梯度法2 基于共轭梯度法的轨迹优化2.1 障碍约束函数2.2 曲率约束函数2.3 平滑约束函数 3 算法仿真3.1 ROS C实现3.2 Python实现 0 专栏介绍 &#x1f525;课程设计、毕业设计、创新竞赛、学术研究必备&#xff01;本专栏涉及更高阶的…

CAS乐观锁原理

1、什么是CAS&#xff1f; compare and swap也就是比较和交换&#xff0c;他是一条CPU的并发原语。 他在替换内存的某个位置的值时&#xff0c;首先查看内存中的值与预期值是否一致&#xff0c;如果一致&#xff0c;执行替换操作。 这个操作是一个原子性操作。 Java中基于Un…

手机免费恢复照片的软件有哪些?这2个工具来帮忙

照片是我们情感的载体&#xff0c;是记忆的碎片。它们无声地诉说着过去的故事&#xff0c;记录着生活中的点点滴滴。但意外常常是突如其来的&#xff0c;当发现手机照片丢失时&#xff0c;我们往往心痛不已。 不用担心&#xff0c;这场看似绝望的危机&#xff0c;实则有解决之…

Sql Server缓冲池、连接池等基本知识(附Demo)

目录 前言1. 缓存池2. 连接池3. 彩蛋 前言 基本的知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;Mysql优化高级篇&#xff08;全&#xff09;Mysql底层原理详细剖析常见面试题&#xff08;全&#xff09; 1…

【VSCode】安装 【ESP-IDF】插件及【ESP32-S3】新建工程和工程配置

一、搭建基础工程 二、基础工程的文件架构解析 三、调试相关工具介绍 1、串口下载2、JTAG 下载与调试 四、工程的文件架构解析 五、基础工程配置 一、搭建基础工程 在 VS Code 中新建 ESP-IDF 基础工程的步骤如下&#xff1a; 1、启动 VS Code 并打开命令面板 按下“Ctrl…

逆向案例二十八——某高考志愿网异步请求头参数加密,以及webpack

网址&#xff1a;aHR0cDovL3d3dy54aW5nYW9rYW90Yi5jb20vY29sbGVnZXMvc2VhcmNo 抓包分析&#xff0c;发现请求头有参数u-sign是加密的&#xff0c;载荷没有进行加密&#xff0c;直接跟栈分析。 进入第二个栈&#xff0c;打上断点&#xff0c;分析有没有加密位置。 可以看到参数…

Chapter17 表面着色器——Shader入门精要学习

Chapter17 表面着色器 一、编译指令1.表面函数2.光照函数3.其他可选参数 二、两个结构体1.Input 结构体&#xff1a;数据来源2.SurfaceOutput 三、Unity背后做了什么四、表面着色器的缺点 一、编译指令 作用&#xff1a;指明该表面着色器的 表面函数 和 光照函数&#xff0c;并…

DPDK收包流程和Linux内核收包流程对比

DPDK 网卡收包流程-腾讯云开发者社区-腾讯云NIC 在接收到数据包之后&#xff0c;首先需要将数据同步到内核中&#xff0c;这中间的桥梁是 rx ring buffer。它是由 NIC 和驱动程序共享的一片区域&#xff0c;事实上&#xff0c;rx ring buffer 存储的并不是实际的 packet 数据&a…

【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(上)

【Gin】精准应用&#xff1a;Gin框架中工厂模式的现代软件开发策略与实施技巧(上) 大家好 我是寸铁&#x1f44a; 【Gin】精准应用&#xff1a;Gin框架中工厂模式的现代软件开发策略与实施技巧(上)✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 本次文章分为上下两部分&…

【RaspberryPi】树莓派系统UI优化

接上文&#xff0c;如何去定制一个树莓派的桌面系统&#xff0c;还是以CM4为例。 解除CM4上电USB无法使用问题 将烧录好的tf卡通过读卡器插入到电脑上&#xff0c;进入boot磁盘&#xff0c;里面有一个Config文件&#xff0c;双击用记事本打开&#xff0c;在【pi4】一栏里加入一…

uboot 设置bootargs配置内核网络挂载根文件系统

uboot 设置bootargs配置内核网络挂载根文件系统 uboot设置bootargs env set bootargs "mem256M consolettyAMA0,115200 root/dev/nfs init/linuxrc nfsrootnfs主机地址:nfs路径/busybox/rootfs_glibc_arm64,prototcp rw nfsvers3 rootwait ip板子地址:nfs主机地址:网关:2…

C#与C++交互开发系列(六):同一个项目中使用C#和C++进行混合模式开发

欢迎来到C#与C交互开发系列的第六篇。在这篇博客中&#xff0c;我们将探讨混合编程&#xff0c;即在同一个项目中结合使用C#和C。在同一个项目中同时使用C/CLI和P/Invoke来实现C#与C的互操作。C/CLI提供了直接访问托管代码的能力&#xff0c;而P/Invoke则用于调用现有的C库函数…

网络安全防御--加密技术及身份、数据认证

VPN概述 VPN诞生的原因 1&#xff0c;物理专线成本高&#xff0c;在位置不固定的情况下&#xff0c;难以实现 2&#xff0c;直接将服务器开放到公网&#xff0c;不安全 VPN --- 虚拟专用网 --- 是指依靠ISP或者其他NSP或者企业自身&#xff0c;构建的专用的安全的数据通信网络&…

基于YOLO8的目标检测系统:开启智能视觉识别之旅

文章目录 在线体验快速开始一、项目介绍篇1.1 YOLO81.2 ultralytics1.3 模块介绍1.3.1 scan_task1.3.2 scan_taskflow.py1.3.3 target_dec_app.py 二、核心代码介绍篇2.1 target_dec_app.py2.2 scan_taskflow.py 三、结语 在线体验 基于YOLO8的目标检测系统 基于opencv的摄像头…

敏捷CSM认证:精通敏捷Scum估算方法,高效完成项目!

咱们做项目的时候可能都遇到过这种情况&#xff1a;项目一开始信心满满&#xff0c;觉得 deadline 稳了。结果呢&#xff1f;各种意外状况频出&#xff0c;时间好像怎么都不够用了&#xff0c;最后项目只能无奈延期&#xff0c;整个团队都像霜打的茄子。 说到底&#xff0c;还…

谷粒商城实战笔记-44-前端基础-Vue-整合ElementUI快速开发/设置模板代码

文章目录 一&#xff0c;安装导入ElementUI1&#xff0c;安装 element-ui2&#xff0c;导入 element-ui 二&#xff0c;ElementUI 实战1&#xff0c;将 App.vue 改为 element-ui 中的后台布局2&#xff0c;开发导航栏2.1 开发MyTable组件2.2 注册路由2.3 改造App.vue2.4 新增左…

Qt实现简易CAD软件的开发:技术解析与实现

文章目录 简易CAD软件的开发&#xff1a;技术解析与实现引言项目概述程序入口主窗口的实现主窗口类定义&#xff08;mainwindow.h&#xff09;主窗口类实现&#xff08;mainwindow.cpp&#xff09; 自定义绘图视图自定义绘图视图类定义&#xff08;myqgraphicsview.h&#xff0…

深入浅出C语言指针(进阶篇)

深入浅出C语言指针(基础篇) 深入浅出C语言指针(进阶篇) 目录 引言 一、指针和数组 1.数组名的理解 2.指针访问数组 3.一维数组传参的本质 二、二级指针 1.二级指针的概念 2.二级指针的内存表示 3.二级指针的解引用 三、字符指针 1.指针指向单个字符 2.指针指向字…

便携式自动气象站:科技赋能气象观测

便携式自动气象站&#xff0c;顾名思义&#xff0c;就是一款集成了多种气象传感器&#xff0c;能够自动进行气象观测和数据记录的设备。它体积小巧、重量轻&#xff0c;便于携带和快速部署&#xff0c;可以在各种环境下进行气象数据的实时监测。同时&#xff0c;通过内置的无线…

版本更新 | Orillusion 0.8发布,与大家同在!

过了这么久&#xff0c;我们Orillusion引擎的大版本更新终于来啦&#xff01; 这次的版本发布&#xff0c;大部分是更新了引擎底层能力&#xff0c;有兴趣的小伙伴可以直接查看&#xff1a; &#x1f517; https://github.com/Orillusion/orillusion 其实面对社区的小伙伴&…