词法作用域(静态)
由变量和块作用域的位置决定,在程序执行时,不会改变作用域。下面我们分析两端代码:
- 全局只有一个变量:foo;
- foo创建的作用域有三个标识符:a、bar、b;
- bar创建的作用域有一个标识符:c; 执行时,a会先在bar作用域找,找不到再去foo作用域找,找到了然后使用;b、c同理
function foo(a) {
var b = a * 2;
function bar(c) {
console.log(a, b, c)
}
bar(a * 2)
}
foo(2)
我们在分析下面这段代码:
会报错:a、b未定义。bar在foo函数中执行为什么获取不到a、b?
因为是静态作用域,作用域取决于函数声明时的位置,而不是执行时的位置。
function foo() {
var a = 2;
var b = a * 2;
bar(a * 2)
}
function bar(c) {
console.log(a, b, c)
}
foo()
动态作用域
eval解析字符串类型的代码
打印1、3,eval在解析时对eval(...)所在的作用域进行修改。
严格模式下,eval只会声明变量在其参数内,不会修改eval(...)所在的作用域,所以b会是2
function foo(str, a) {
eval(str)
console.log(a, b)
}
var b = 2;
foo('var b = 3', 1)
with
打印出1、2,为啥不是a、b呢?
with内的作用域执行它的参数。
严格模式下会报错。
const obj = {
a: 1,
b: 2
}
const a = 'a';
const b = 'b';
with(obj) {
console.log(a, b) // 1 2
}
性能
js引擎会在编译阶段进行数项性能优化。会进行静态分析,确定所有变量和函数的定义位置,在执行时才能尽快找到标识符。
而eval和with会产生动态作用域,执行时会改变作用域,所以性能上有很大的影响。所以在严格模式,会限制使用,尽量别用它们。