你不懂的JavaScript: 变量查找方式

73 阅读4分钟

你不懂的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 = avar 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 中要返回ab的值,所以要取ab的值此为两次查找

    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