🍎 JavaScript 作用域与作用域链
# 概念
# 作用域
作用域:一个代码段所在的区域。它是静态的(相当于上下文对象),在编写代码时就确定了。
作用域决定了代码区块中和其他资源的可见性。
作用域分类:
全局作用域:任何不在函数中或者大括号中声明的变量,都是在全局作用域中,全局作用域定义的变量可以在程序的任何位置中访问。
var greeting = 'hello world' function greet() { console.log(greeting) }
1
2
3
4函数作用域:也叫局部作用域。变量在函数内部声明,这些变量只能在函数内部访问,不能在函数以外的访问。
function greet() { var greeting = 'hello world' console.log(greeting) } greet() console.log(greeting) // Uncaught ReferenceError: greeting is not defined
1
2
3
4
5
6
7块级作用域:ES6 引入了
let
和const
关键字。在大括号中使用let
和const
声明的变量存在于块级作用域,在大括号之外不能访问这些变量。(使用var
声明的不存在块级作用域变量){ // 块级作用域中的变量 let greeting = 'Hello World!'; var lang = 'English'; console.log(greeting); } console.log(lang); // English console.log(greeting); // Uncaught ReferenceError: greeting is not defined
1
2
3
4
5
6
7
8
9
作用:隔离变量,不同作用域下同名变量不会有冲突。
# 词法作用域
又称静态作用域,在变量被创建时就已经确定好了,而非执行阶段确定(也就是说,在写好代码时作用域就确定了),JavaScript 遵循的是词法作用域。
🌰 例子:
var a = 2
function foo() {
console.log(a)
}
function bar() {
var a = 3
foo()
}
bar()
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上面的运行过程可以看作:
由于 JavaScript 遵循的是词法作用域,相同层级的 foo 和 bar 没有办法访问到彼此作用域块的变量,所以输出的是 2
# 作用域链
JavaScript 中使用一个变量时,JavaScript 引擎会尝试在当前作用域下去寻找变量,如果没找到会在上层作用域寻找,以此类推直到找到该变量或者到达全局作用域。如果全局作用域找不到该变量则报错。
如果在全局作用域中找不到该变量,(在非严格模式下)隐式声明该变量或者直接报错。
作用域链是多个上下级关系的作用域形成的链,它的方向时从下向上的(从内到外)。
查找变量就是沿着作用链来查找的。
查找一个变量的查找规则:
- 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入 2.
- 在上一级作用域的执行上下文中查找相应的属性,如果有直接返回,否则进入 3.
- 再次执行 2. 的相同操作,直到全局作用域,如果还找不到就抛出找不到变量的异常。
🌰 例子:
var a = 2
function fn1() {
var b = 3
function fn2() {
var c = 4
console.log(c) // 4
console.log(b) // 3
console.log(a) // 2
console.log(d) // ReferenceError: Can't find variable: d
}
fn2()
}
fn1()
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
🌰 例子:
var fn = function () {
console.log(fn)
}
fn()
var obj = {
fn2: function (){
console.log(fn2) //ReferenceError: Can't find variable: fn2
// console.log(this.fn2)
}
}
obj.fn2()
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 作用域与执行上下文
- 创建时机:
- 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时。
- 全局执行上下文环境是在全局作用域确定之后, JavaScript 代码马上执行之前创建。(词法作用域)
- 函数执行上下文环境是在调用函数时,函数体代码执行之前创建。
- 区别 :
- 作用域是静态的,只要函数定义好了就一直存在并且不会再变化。
- 上下文环境是动态的,调用函数时创建,函数调用结束时上下文环境就会被释放。
- 联系:
- 上下文环境(对象) 是从属于所在的作用域。
- 全局上下文环境从属于全局作用域。
- 函数上下文环境从属于对应的函数使用域。
注意
两个 n+1:
- 在判断执行上下文个数中:n 为调用的函数个数。
- 在判断作用域个数中:n 为定义的函数个数。
编辑 (opens new window)
📢 上次更新: 2022/09/02, 10:18:16