JavaScript 执行上下文与作用域

JavaScript 执行上下文与作用域

全局上下文是最外层的上下文。

根据ECMAScript实现的宿主环境,表示全局上下文的对象可能不一样。

在浏览器中,全局上下文就是我们常说的window对象,因此所有通过var定义的全局变量和函数都会成为window对象的属性和方法。

使用let和const的顶级声明不会定义在全局上下文中,但在作用域链解析上效果是一样的。

上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)。

每个函数调用都有自己的上下文。

当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。在函数执行完之后,上下文栈会弹出该函数上下文,将控制权返还给之前的执行上下文。ECMAScript程序的执行流就是通过这个上下文栈进行控制的。

内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。

上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。

注意 函数参数被认为是当前上下文中的变量,因此也跟上下文中的其他变量遵循相同的访问规则。

1)作用域链增强

虽然执行上下文主要有全局上下文和函数上下文两种(eval()调用内部存在第三种上下文),但有其他方式来增强作用域链。某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除。

通常在两种情况下会出现这个现象,即代码执行到下面任意一种情况时:

  • try/catch语句的catch块
  • with语句

这两种情况下,都会在作用域链前端添加一个变量对象。对with语句来说,会向作用域链前端添加指定的对象;对catch语句而言,则会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明。

2)变量声明

①使用var的函数作用域声明

在使用var声明变量时,变量会被自动添加到最接近的上下文。

在函数中,最接近的上下文就是函数的局部上下文。

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

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

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

②使用let的块级作用域声明

S6新增的let关键字跟var很相似,但它的作用域是块级的,这也是JavaScript中的新概念。块级作用域由最近的一对包含花括号{}界定。

let与var的另一个不同之处是在同一作用域内不能声明两次。重复的var声明会被忽略,而重复的let声明会抛出SyntaxError。

let的行为非常适合在循环中声明迭代变量。使用var声明的迭代变量会泄漏到循环外部,这种情况应该避免。

③使用const的常量声明

使用const声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任何时候都不能再重新赋予新值。

④标识符查找

当在特定上下文中为读取或写入而引用一个标识符时,必须通过搜索确定这个标识符表示什么。

搜索开始于作用域链前端,以给定的名称搜索对应的标识符。

如果在局部上下文中找到该标识符,则搜索停止,变量确定;如果没有找到变量名,则继续沿作用域链搜索。(注意,作用域链中的对象也有一个原型链,因此搜索可能涉及每个对象的原型链。)这个过程一直持续到搜索至全局上下文的变量对象。如果仍然没有找到标识符,则说明其未声明。

注意 标识符查找并非没有代价。访问局部变量比访问全局变量要快,因为不用切换作用域。不过,JavaScript引擎在优化标识符查找上做了很多工作,将来这个差异可能就微不足道了。

相关推荐
江城开朗的豌豆4 分钟前
聊聊useEffect:谁说副作用不能“优雅”?
前端·javascript·react.js
!执行5 分钟前
开发electron时候Chromium 报 Not allowed to load local resource → 空白页。
前端·javascript·electron
Evand J6 分钟前
【MATLAB例程】水下机器人长基线(LBL)定位,用于三维轨迹,使用EKF滤波,融合LBL和IMU,4个锚点(长基线基站数=4),附下载链接
开发语言·matlab·机器人
林内克思6 分钟前
inline内联函数
java·开发语言·算法
博睿谷IT99_9 分钟前
OSPF 的工作过程、Router ID 机制、报文结构
开发语言·网络·华为·智能路由器·网络工程师·华为认证·数据通信
piikee23 分钟前
php内存缓存插件yac的安装配置--平替apcu,多进程共享内存
开发语言·缓存·php·yac·php扩展·php内存缓存·apcu平替
赛博切图仔24 分钟前
面试手写 Promise:链式 + 静态方法全实现
前端·javascript·面试
多吃蔬菜!!!31 分钟前
VsCode 上的Opencv(C++)环境配置(Linux)
开发语言·c++
Moment32 分钟前
面试官:用户访问到一个不存在的路由,如何重定向到404 Not Found的页面 ❓❓❓
前端·javascript·面试
澡点睡觉41 分钟前
【golang长途旅行第32站】反射
开发语言·后端·golang