你不知道的js—词法作用域

172 阅读2分钟

词法作用域(静态)

由变量和块作用域的位置决定,在程序执行时,不会改变作用域。下面我们分析两端代码:

  1. 全局只有一个变量:foo;
  2. foo创建的作用域有三个标识符:a、bar、b;
  3. 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会产生动态作用域,执行时会改变作用域,所以性能上有很大的影响。所以在严格模式,会限制使用,尽量别用它们。