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的构造函数
```
- git-第一天
- Git-第二天
- git-第三天
- http-基础
- HTTP构成和状态码
- 浏览器输入URL,经历的过程
- TCP/IP 详解三次握手 四次挥手
- http-DNS系统
- http与https之间的区别
- HTTPS握手和HTTP握手
- HTTP小试牛刀
- Tcp初探
- TCP报文格式
- HTML5
- HTML基础
- Mock
- css 选择器
- css 动画
- css 定位
- position/display/float/z-index第一课时
- 行内、块、脱标 三种状态下的元素如何实现、水平、垂直居中
- clientHeight/offsetHeight/scrollHeight
- js 数据类型
- 变量提升
- 堆栈关系