0%

JavaScript作用域

什么是作用域

作用域可以视为一套规则,引用《你不知道的 JavaScript》(上卷)中的解释,如下

这套规则可以用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找

词法作用域

什么是词法作用域

词法作用域是由你写代码时将变量和块作用域写在哪里决定的

思考以下代码

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

function fn1() {
var a = 2;

function fn2() {
console.log(a);
}

fn2();
}

fn1();

它的作用域如图所示

scope

  • 1包含整个全局作用域,其中有afn1

  • 2包含fn1创建的作用域,其中有afn2

  • 3包含fn2创建的作用域

fn2执行的时候,会先找3作用域,没找到,然后向上找到2作用域里面的a,所以这里会输出2

再思考以下代码

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

function fn2() {
console.log(a);
}

function fn1() {
var a = 2;

fn2();
}

fn1();

结果会输出1

没有任何函数的作用域可以同时出现在两个外部作用域中

所以,这里的作用域,如图所示

scope

fn2执行的时候,会先找2作用域,没找到,然后向上找到1作用域里面的a,所以这里会输出1

欺骗词法作用域

词法作用域完全由写代码时函数声明的位置来定义的,那怎么来动态修改或者欺骗词法作用域呢?

eval

eval可以动态插入 JS 代码,在执行eval之后的代码时,引擎就不知道前面代码的是否是动态插入的,是否会修改作用域,引擎只会按照既定的规则进行作用域查找

思考以下代码

1
2
3
4
5
6
7
8
var a = 1;

function fn1(a) {
eval(a);
console.log(a);
}

fn1('var a = 2;');

eval(a)会在fn1创建的作用域中添加一个变量a,所以会输出2

with

with 会根据你传递给它的对象,新建一个全新的作用域

思考以下代码

1
2
3
4
5
6
7
8
9
10
var a = 1;

function fn1(obj) {
with (obj) {
a = 2;
}
console.log(a);
}

fn1({});

当把空对象{}传递给with时,新建作用域其实就是这个对象,然而对象中没有a标志符,就会向上查找,所以会修改全局变量的a,最后会输出2