首先我们看一段概念,感觉很难理解,尽量多读几遍。如果不理解,那也无所谓。
变量或函数的上下文决定它们可以访问哪些数据,以及他们的行为。每个上下文都有一个关联的变量对象,存储了上下文的所有变量和函数(代码无法访问的)。在代码执行完毕后销毁。
全局上下文 最外层的上下文,在浏览器中,全局上下文就是window对象。通过var
定义的全局变量和函数都会成为window对象的属性和方法。let const
的顶级声明不会定义在全局上下文,但在作用域链解析上效果是一样的。
上下文栈 每个函数调用都有自己的上下文,当代码执行流进入函数时,函数的上下文被推倒一个上下文栈上,执行完毕弹出,将控制权返还给之前的执行上下文。
作用域链 上下文代码执行时,会创建变量对象的一个作用域链。决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文变量对象始终位于作用域链的最前端。如果上下文是函数,则其活动对象用作变量对象(最初只有一个定义变量:arguments「全局上下文没有这个变量」),通过包含上下文向作用域链添加变量对象,直到全局上下文。全局上下文的变量对象始终是作用域链的最后一个变量对象
执行上下文主要有全局上下文和函数上下文两种。但也有其他方式来增强作用域链。如:with语句
会向作用域链前段临时添加指定的对象。try/catch语句的catch
会创建一个新的变量对象(包含要抛出的错误对象的声明)
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声明变量时,变量被自动添加到最接近的上下文。在函数中,最接近的上下文就是函数的局部上下文。
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
console.log(name) // undefined
var name ="test"
if(true){
let a;
}
console.log(a) // ReferenceError: a 没有定义
当然就算是使用var也会报错没有被定义
var
声明会被忽略,而重复的let
声明会抛出异常SyntaxError。let适用于循环中的声明迭代
我们来看下面的例子
使用var关键字迭代的变量泄露到循环外部,这种情况需要避免
for(var i=0;i<10;i++){
}
console.log(i) // 10
而使用let关键字,会报错未声明
for(let i=0;i<10;i++){
}
console.log(i) //ReferenceError: i 没有定义
严格来讲,let
也会被提升,但是由于“暂时性死区”,实际上不能在声明之前使用let
变量。所以从代码的角度说,let
的提升和var
不一样。
常量,一经声明,就不能再重新赋值。所以必须初始化赋值。
const b; // SyntaxError 没有初始化赋值
const c = 3;
c = 4; // TypeError 给常量赋值
if(true){
const a = 0;
}
console.log(a) // ReferenceError a没有定义
赋值为对象的const
变量不能再被重新赋值为其他引用值,但对象的键则不受限制。
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()
const obj2 = Object.freeze({})
obj2.name = "ss"
console.log(obj2.name) // undefined