你不懂的JavaScript: 变量查找方式
1. 变量声明与赋值
在JavaScript中,变量的声明和赋值是两个不同的步骤即分为两个阶段:编译阶段和执行阶段。例如:
var a = 1;
这行代码实际上可以分为两个部分:
-
声明:
var a;
------编译阶段 -
赋值:
a = 1;
------执行阶段进阶 :
function foo(){ var a = 1;// var a 会被忽略 var a = 2; console.log(a);//输出2 }
-
存在 → 忽略先前声明保留后面(变量提升机制)
-
不存在 → 在作用域中创建新变量标识符
a
1.1 编译阶段
- 编译阶段:在这个阶段,JavaScript引擎会处理所有的变量声明。例如,
var a
会在编译阶段被处理,变量a
会被声明,但其值为undefined
。
1.2 执行阶段
-
执行阶段:在这个阶段,JavaScript引擎会执行代码中的赋值操作。例如,
a = 1
会在执行阶段被处理,变量a
会被赋值为1
。 -
变量的查找也称为执行阶段,分为两种查找:LHS查找和RHS查找,了解以上查找便可以轻松学习JS语言,成功上岸。
2. LHS 与 RHS 查找
-
LHS查找:查找变量的容器本身,用于赋值操作。简单来说就是等号左边的变量
例如:
a = 1;
在这个例子中,JavaScript引擎会执行LHS查找,找到变量a
的容器,并将其赋值为1
。
-
RHS查找:查找变量的值,用于取值操作。简单来说就是等号右边的变量
例如:
var a = 2;
console.log(a); // 输出2
在这个例子中,JavaScript引擎会执行RHS查找,找到变量a
的值,并将其传递给console.log
函数。
2.1 LHS 与 RHS 区别和辨认
例如:
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
在这个例子中:
-
LHS :执行三次LHS查找
1.
var c = foo(2)
中var c
变量要找到c
的容器并赋值foo(2)
即在等号的左边2.
var b = a
中var b
变量要找到b
的容器并赋值变量a
即在等号的左边3.
var c = foo(2)
中foo(2)
要找到变量函数的参数a
并数值2
也是LHS
查询 -
RHS :执行四次RHS查找
1.
var c = foo(2)
中要取foo(2)
变量的函数值即等号的右边2.
return a + b
中要返回a
和b
的值,所以要取a
和b
的值此为两次查找3.
var b = a
中要找到变量a
的值即等号的右边
2.2 LHS 报错
- LHS查找失败:在非严格模式下,如果LHS查找失败,JavaScript引擎会自动声明一个全局变量。例如:
function foo() {
b = 2; // 自动声明全局变量
}
foo();
console.log(b); // 输出 2
在这个例子中,b
没有被声明,因此在b = 2;
时,JavaScript引擎会自动声明一个全局变量var b
。
在严格模式下,LHS查找失败会抛出ReferenceError
错误。例如:
"use strict";
function foo() {
b = 2; // ReferenceError: b is not defined 缺少var 无法通过编译阶段
}
foo();
console.log(b);
在这个例子中,b
没有被声明,因此在b = 2;
时,JavaScript引擎会抛出ReferenceError
错误。
2.3 RHS 报错
- RHS查找失败:如果RHS查找失败,JavaScript引擎会抛出
ReferenceError
错误。例如:
function foo(a) {
console.log(a + b); // ReferenceError: b is not defined
b = a; // 没有var申明变量即在编译阶段没有申明
}
foo(2);
在这个例子中,b
没有被声明,因此在console.log(a + b);
时,JavaScript引擎会抛出ReferenceError
错误。
3. 作用域
作用域是变量的查找规则。JavaScript中的作用域分为全局作用域和局部作用域。
3.1 作用域链
当JavaScript引擎在当前作用域中找不到某个变量时,它会沿着作用域链向上查找,直到找到该变量或到达全局作用域。如果在全局作用域中仍然找不到该变量,则会抛出ReferenceError
错误。
例如:
var a = 1;
var b = 4;
function foo() {
console.log(a, b);
// a输出 undefined b输出 4
var a = 2;
function bar() {
var a = 3;
return a + b; // 返回 7
}
console.log(bar()); // 输出 7
}
foo();
- 注释:
在编译阶段变量
a
的声明会被提升到foo
函数作用域,此时a
只在函数编译阶段申明变量并没有在执行阶段赋值也就是var a = 2
还没执行只是编译。因此,在foo
函数内部,当执行到console.log(a, b)
时,a
已经被声明但还没有被赋值,所以它的值是undefined
。而变量b
是在全局作用域中声明的,它的值为4
。在foo
函数内部没有重新声明b
,所以b
引用的是全局作用域中的变量。
3.2 作用域嵌套
作用域可以嵌套,内部作用域可以访问外部作用域的变量,但外部作用域不能访问内部作用域的变量。例如:
var a = 1;
function foo() {
var a = 2;
function bar() {
var a = 3;
console.log(a); // 输出 3
}
bar();
console.log(a); // 输出 2
}
foo();
console.log(a); // 输出 1
在这个例子中:
- 在
bar
函数内部,a
的值为3
。 - 在
foo
函数内部,a
的值为2
。 - 在全局作用域中,
a
的值为1
。