115 lines
3.0 KiB
Markdown
115 lines
3.0 KiB
Markdown
---
|
|
title: 变量的定义提升
|
|
date: 2017-10-14 00:30:18
|
|
tags:
|
|
- JavaScript
|
|
- 函数
|
|
categories:
|
|
- JavaScript
|
|
---
|
|
|
|
|
|
从一道笔试题说起
|
|
```javascript
|
|
function Foo() {
|
|
getName = function () { console.log (1); };
|
|
return this;
|
|
}
|
|
Foo.getName = function () { console.log (2);};
|
|
Foo.prototype.getName = function () { console.log (3);};
|
|
var getName = function () { console.log (4);};
|
|
function getName() { console.log (5);}
|
|
|
|
//请写出以下输出结果:
|
|
Foo.getName();
|
|
getName();
|
|
Foo().getName();
|
|
getName();
|
|
new Foo.getName();
|
|
new Foo().getName();
|
|
new new Foo().getName();
|
|
```
|
|
<!-- more -->
|
|
#### 第一问
|
|
Foo是一个函数对象 , `Foo.getName = … `在其中定义了一个名为getName的属性 , 也是一个函数
|
|
所以第一问当中的调用输出的应该是2
|
|
|
|
#### 第二问
|
|
这里涉及到了变量的定义提升问题 , 如果不了解容易误认为答案是5
|
|
例如如下语句
|
|
```javascript
|
|
console.log("x" in window);
|
|
var x;
|
|
```
|
|
虽然变量的定义是在第二个语句 , 但是输出的结果仍然是true
|
|
因为代码执行时 JS引擎会把变量的声明语句提升到最上方
|
|
但是提升的仅仅是变量声明 , 如果声明变量的时候包含初始化
|
|
则会将初始化作为单纯的一条赋值语句保留在原处
|
|
例如
|
|
```javascript
|
|
console.log("x" in window);
|
|
var x = 10;
|
|
```
|
|
在JS引擎处理之后会改变为
|
|
```javascript
|
|
var x;
|
|
console.log("x" in window);
|
|
x = 10;
|
|
```
|
|
> 补充说明 : 只有显式的变量定义才会被提升
|
|
> 隐式的变量定义并不会
|
|
> 比如
|
|
```
|
|
console.log("x" in window);//false
|
|
x = 10;
|
|
```
|
|
> 输出就是false , 因为x未定义
|
|
|
|
全局函数的定义与全局变量的定义本质是一样的
|
|
所以它也会被提升到顶部
|
|
但是 `var x = function(){}`与`function x(){}`两种定义方式最终效果相同
|
|
执行过程却是不同的
|
|
```javascript
|
|
console.log(x);//function x(){}
|
|
function x(){}
|
|
console.log(x);//function x(){}
|
|
```
|
|
或者
|
|
```javascript
|
|
console.log(x);//undefined
|
|
var x = function(){}
|
|
console.log(x);//function x(){}
|
|
```
|
|
那么具体到这个题目
|
|
代码
|
|
```javascript
|
|
var getName = function () { console.log (4);};
|
|
function getName() { console.log (5);}
|
|
```
|
|
会被JS引擎在解析时修改为
|
|
```javascript
|
|
var getName;
|
|
function getName() { console.log (5);}
|
|
getName = function () { console.log (4);};
|
|
```
|
|
显然 , 第三行的赋值会将第二行的定义覆盖掉
|
|
最终这个全局的getName函数输出的值是4
|
|
|
|
#### 第三问
|
|
Foo() 是在全局的基础上调用的 , 也就是由window对象调用的
|
|
所以return this返回的是window对象
|
|
但是在这个函数的内部 , 修改了全局的getName的值
|
|
现在名为getName的全局函数输出是1了
|
|
所以再调用window.getName() 的输出当然是1
|
|
|
|
#### 第四问
|
|
同样是调用全局的getName函数 , 所以结果和第三问一样 , 也是1
|
|
|
|
#### 第五问
|
|
成员访问的点号( . )优先级高于 new , 所以结果与第一问相同 , 也是2
|
|
|
|
#### 第六问
|
|
括号运算符的优先级高于点号( . )
|
|
实际执行为 `( new Foo( ) ).getName()`
|
|
所以调用的是原型对象上的getName函数 , 输出是3
|