作用域
- 可访问变量,对象,函数的集合
- 块级作用域(let\const)
- 全局作用域(变量在函数外定义)
- 函数作用域(局部作用域,只能在函数内部访问)
作用域链
从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。
变量生命周期
变量生命周期在它声明时初始化。
局部变量在函数执行完毕后销毁。
全局变量在页面关闭后销毁。
作用域是分层的
//内层作用域可以访问外层作用域的变量,反之则不行
var a=1
function aa(){
var b=2
console.log(a)
}
console.log(b)
输出1 b报错未定义
块语句{}
没有作用域
if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域
if (true) {
// 'if' 条件语句块不会创建一个新的作用域
var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'
var let const
var在全局范围内都有效,每一次循环,变量的值都会发生改变,里面的i指向的就是全局的i,结果都是10
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 10
变量是let声明,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,就会循环输出
var a = []
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 6
//JavaScript 引擎内部会记住上一轮循环的值,
//初始化本轮的变量i时,
//就在上一轮循环的基础上进行计算。
for循环
还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc
这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域
作用域与执行上下文
JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段
- 词法分析
- 语法分析
- 作用域规则确定
执行阶段
- 创建执行上下文
- 执行函数代码
- 垃圾回收
JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别
执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变