💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
js中变量的处理机制,你是否想了解下,想的话上车!!! 在聊到变量提升的时候,我们先聊下`LHS`和`RHS` ## 1、`LHS`、`RHS` >`javascript`中两种查找类型,含义是赋值操作的左侧和右侧 * `LHS`: 对那个赋值对那个进行`LHS`引用,可以理解为赋值操作的目标 如:`a = 2`,如果找不到在非严格模式会声明一个全局变量 * `RHS`: 需要获取那个变量的值,就对哪个变量进行`RHS`引用,理解为赋值操作的源头,如果找不到对应的标识符,就会抛出异常:`ReferenceError` **简单的讲:赋值就是`LHS`,找值就是`RHS`** 来个例子分析分析: ``` function foo(a) { var b = a; return b + a; } var c= foo() ``` 以上代码有3个`LHS`和4个`RHS`,分析如下: * 1、`var c`中需要被赋值,在赋值操作的左侧,所以对c进行`LHS`引用 * 2、变量`c`需要被赋值,它的值是`foo(2)`,那么`foo(2)`的值是多少呢?,需要查找`foo(2)`的值,在赋值操作的右侧,所以对`foo(2)`进行`RHS`引用 * 3、隐含赋值操作,将`2`传递给`function foo(a){...}`函数的参数`a`,`a`在赋值操作的左侧,对`a`进行`LHS`引用 * 4、`var b = a`,`b`需要被赋值,处于赋值操作的左侧,所以对`b`进行`LHS`引用,`b`的值是`a`,那么`a`是多少呢,需要对`a`进行`RHS`引用 * 5、`return b + a`中,需要找到`a`和`b`的来源,`a`和`b`都在赋值操作的右侧,对`b + a`进行`RHS`引用 ## 2、变量提升 >`js`中变量提升到当前作用域的顶部,而赋值操作在原处不变 举个例子: `console.log(a) ;var a = 2;`输出的值是什么? 很多人可能会报错`ReferenceError`,但是不是,后面定义了`a`,所以变量会提升,就是在`console.log(a)`前面多写了一行代码`var a`,如果是`console.log(a); a = 2;`没有`var`的情况下,会报`//ReferenceError: a is not defined` 再来一个例子: ``` var name="Bob"; (function(){     if(typeof name=== 'undefined'){         var name='Jack';//此处增加name声明 // name = 'Jack' // 输出:hello,Bob         console.log('Goodbye'+name);     }else{         console.log('hello'+name);     } })() // 输出:Goodbye Bob ``` 修改一下: ``` (function(){            name='Jack';            if(typeof name=== 'undefined'){                 console.log('Goodbye'+name);             }else{                 console.log('hello'+name);             }     })(); // hello Jack ```  console.log("name:"+name);// name:Jack (未使用var,默认声明为全局变量) 注意:多个`script`不可以跨越 ``` <script> console.log(a) </script> <script> var a = 1; </script><br> // 控制台报错`ReferenceError: a is not defined ``` ## 3、函数提升 变量提升了,那么函数的声明也会提升。 **函数的声明有两种方式:** * 1、命名函数式 ``` // 声明式 function foo() { ..... } ``` * 2、函数字面量式(即函数表达式) ``` // 字面量式 var foo = function() { ..... } ``` 那么重点来了,先看例子: ``` console.log(bar); // f bar() { console.log(123) } console.log(bar()); // undefined var bar = 456; function bar() { console.log(123); // 123 } console.log(bar); // 456 bar = 789; console.log(bar); // 789 console.log(bar()) // bar is not a function ``` 相当于: ``` // js执行步骤 // 函数提升,函数提升优先级高于变量提升 var bar = function() { console.log(123) }; // 变量提升,变量提升不会覆盖(同名)函数提升,只有变量再次赋值时,才会被覆盖 var bar; console.log(bar); console.log(bar()); // 变量赋值,覆盖同名函数字面量 bar = 456; console.log(bar); // 再次赋值 bar = 789 console.log(bar); console.log(bar()); ``` 两个例子得到的结果: ``` // js执行结果 f bar() { console.log(123) } 123 // 执行bar()里的console.log undefined // bar()的返回值,如果函数没有返回值,默认为:undefined 456 789 [typeError]:bar is not a function ``` **得出结论:** >函数的提升优先级高于变量的提升,且不会被同名变量声明时覆盖,但是会被变量赋值后覆盖 ### ???那么命名函数和字面量式函数的提升有什么不同呢? * 1、先看命名函数式 ``` foo() // 1 function foo() { console.log('1') } ``` 说明命名函数式的函数本身也会提升到当前作用域的最前面 * 2、再看下字面量式(表达式): ``` foo() // Uncaught TypeError: foo is not a function, 此时的foo为undefined var foo = function() { console.log('1') } <=>相当与以下代码 var foo foo() foo = function() { console.log('1') } ``` 注意:同名函数声明,则覆盖之前的函数声明 ``` foo() // 4 function foo() { console.log('3') } function foo() { console.log('4') } ``` 但是同名不同形式的声明方式是不一样的 ``` foo() // 3 函数提前不覆盖 function foo() { console.log('3') } var foo = function() { console.log('4') } foo() //4 覆盖前面的方法 ``` 最后用一个面试题结尾: ``` function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //请写出以下输出结果: Foo.getName(); // 2 => Foo的静态属性getName getName(); // 4 => 执行全局环境下的getName Foo().getName(); // 1 <=>window.getName() getName(); // 1 => 执行全局环境下的getName new Foo.getName(); // 2 => 执行Foo的静态属性getName的构造函数 new Foo().getName(); // 3 相当于Foo实例原型上的getName new new Foo().getName(); // 3 相当于Foo实例原型上的getName的构造函数 ```