前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >4.2 执行上下文与作用域

4.2 执行上下文与作用域

作者头像
用户4793865
发布2023-01-12 13:28:50
3250
发布2023-01-12 13:28:50
举报
文章被收录于专栏:前端小菜鸡yym前端小菜鸡yym

执行上下文与作用域

首先我们看一段概念,感觉很难理解,尽量多读几遍。如果不理解,那也无所谓。

变量或函数的上下文决定它们可以访问哪些数据,以及他们的行为。每个上下文都有一个关联的变量对象,存储了上下文的所有变量和函数(代码无法访问的)。在代码执行完毕后销毁。

全局上下文 最外层的上下文,在浏览器中,全局上下文就是window对象。通过var定义的全局变量和函数都会成为window对象的属性和方法。let const的顶级声明不会定义在全局上下文,但在作用域链解析上效果是一样的。

上下文栈 每个函数调用都有自己的上下文,当代码执行流进入函数时,函数的上下文被推倒一个上下文栈上,执行完毕弹出,将控制权返还给之前的执行上下文。

作用域链 上下文代码执行时,会创建变量对象的一个作用域链。决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文变量对象始终位于作用域链的最前端。如果上下文是函数,则其活动对象用作变量对象(最初只有一个定义变量:arguments「全局上下文没有这个变量」),通过包含上下文向作用域链添加变量对象,直到全局上下文。全局上下文的变量对象始终是作用域链的最后一个变量对象

作用域链增强

执行上下文主要有全局上下文和函数上下文两种。但也有其他方式来增强作用域链。如:with语句会向作用域链前段临时添加指定的对象。try/catch语句的catch会创建一个新的变量对象(包含要抛出的错误对象的声明)

代码语言:javascript
复制
function buildUrl(){
    let qs = "?debug=true";
    // with将location对象作文上下文,location被添加到作用域链前段
    width(location){
       //href实际上是loation.href;qs是上面定义的变量,定义在
       // 函数上下文的变量对象上
        let url = href + qs;
    }
    // 因为👆🏻 url使用let声明,被限制在块级作用域中(👇🏻讲)。所以
    // 在with语句外没有定义。使用var则会成为函数上下文的一部分
    return url
} 

变量声明

ES6在var的基础上新增了let 和 const。

var

使用var声明变量时,变量被自动添加到最接近的上下文。在函数中,最接近的上下文就是函数的局部上下文。

代码语言:javascript
复制
function add(num1,num2){
    var sum = num1+num2
    return sum;
}
let result = add(10,20) //  30
console.log(sum)   // 报错:sum不是有效变量

提升

但是省略关键字var,那么sum在add()中被调用之后就可以访问了。 因为在调用add()之后,sum被添加到了全局上下文,在函数退出之后依然存在,所以后面可以访问到。

var声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。

通过在声明之前打印变量,可以验证变量会被提升,也就是会输出undefined 而不是 Reference Error

代码语言:javascript
复制
console.log(name)  // undefined
var name ="test"

let

  • 区别一:与var的最大区别就是let是块级作用域的。最近的一对花括号就是一个块级作用域。 如(if while function块)
代码语言:javascript
复制
if(true){
    let a;
}
console.log(a)  // ReferenceError: a 没有定义

当然就算是使用var也会报错没有被定义

  • 区别二:另一个不同在于同一作用域不能重复声明两次,重复的var声明会被忽略,而重复的let声明会抛出异常SyntaxError。

let适用于循环中的声明迭代

我们来看下面的例子

使用var关键字迭代的变量泄露到循环外部,这种情况需要避免

代码语言:javascript
复制
for(var i=0;i<10;i++){

}
console.log(i)     // 10

而使用let关键字,会报错未声明

代码语言:javascript
复制
for(let i=0;i<10;i++){

}
console.log(i)  //ReferenceError: i 没有定义

严格来讲,let也会被提升,但是由于“暂时性死区”,实际上不能在声明之前使用let变量。所以从代码的角度说,let的提升和var不一样。

const

常量,一经声明,就不能再重新赋值。所以必须初始化赋值。

代码语言:javascript
复制
const b; // SyntaxError 没有初始化赋值

const c = 3;
c = 4; // TypeError 给常量赋值

if(true){
    const a = 0;
}
console.log(a)   // ReferenceError a没有定义

赋值为对象的const变量不能再被重新赋值为其他引用值,但对象的键则不受限制。

代码语言:javascript
复制
const obj ={name:'yyy',hieght:199}
// 添加
obj.add = true   // {name: 'yyy', hieght: 199, add: true}
// 修改
obj.name = "sss" // {name: 'sss', hieght: 199, add: true}
// 重新赋值为其他引用值
obj = {} // TypeError: Assignment to constant variable

如果想让整个对象都不能修改,使用 freeze()

代码语言:javascript
复制
const obj2 = Object.freeze({})
obj2.name = "ss"
console.log(obj2.name)  // undefined
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 执行上下文与作用域
    • 作用域链增强
      • 变量声明
        • var
        • let
        • const
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档