JS深入系列之作用域

JS深入系列之作用域

五月 19, 2021 本文共计: 679 字 预计阅读时长: 2分钟

近期做了一个比较有意义的决定,深入学习总结前端知识的每一个系列,然后形成知识树。

希望也能为正在阅读的你带来一定的温习或者提升~

作用域

什么是作用域?

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。
—— 《你不知道的JavaScript》(上卷)p12

从引擎、编译器、作用域三个纬度认识作用域

1
var args = 'scope';
  1. 引擎拿到这段代码后开始编译;

  2. 引擎借助编译器开始编译,在编译时,编译器会在当前所在的作用域内(全局作用域)声明一个args的变量(从未声明过);

  3. 引擎开始执行代码,在当前作用域中继续寻找这个变量,如果找到了,那么就进行赋值操作;


以上用步骤拆分就是:先编译后执行;

1
2
3
4
5
6
7
# Step 1
var args = undefined;

# Step 2

args = 'scope';


从编译运行维度来讲:

作用域分类:词法作用域 & 动态作用域

从空间范围来讲:

作用域分类:

  • 全局作用域
  • 函数作用域
  • 块作用域
  • 模块作用域(ES6 Module & CommonJS Module)

什么是词法作用域?

以下示例分析来自于:https://github.com/mqyqingfeng/Blog/issues/3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var value = 1;

function foo() {
console.log(value);
}

function bar() {
var value = 2;
foo();
}

bar();

// 结果是 ???

假设JavaScript采用静态作用域,让我们分析下执行过程:

  1. 执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

  2. 假设JavaScript采用动态作用域,让我们分析下执行过程:

  3. 执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。

我们可以欺骗词法吗?

答案:可以!

可以借助eval&with,可以看demo:

1
2
3
4
5
6
function foo(str) {
eval(str);
console.log(a);
}

foo("var a = 2");

这里的欺骗词法也可以理解为动态的作用域

1
2
3
4
5
6
7
8
9
10
const obj = { a: 1, b: 2 };

with (obj) {
a = 3;
b = 4;
}

console.log(obj);

// { a: 3, b: 4 }

with可以为obj开辟一片新的词法空间去修改,但是会有隐式声明的诟病(泄漏变量到全局);

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj = {};

with (obj) {
a = 123;
}

console.log(obj);

//{}

console.log(a);

// 123