准确的叫法为:变量声明提升,函数声明提升
一、变量提升与函数提升
1、变量提升
var a = 1;
function print() {
console.log(a); // undefined
var a = 2;
}
print();
undefined:表示未赋值 null:赋值了,只不过赋的是 null
原因是代码的实际执行顺序与书写顺序是不同的,实际执行顺序如下
function print() {
var a = null;
console.log(a); // undefined
a = 2;
}
2、函数提升
a();
function a() {};
// 即可以先调用,再声明
区分以下代码:
b(); // error
var b = function() {};
此时 var b
为一个变量,但内容为函数,其遵守变量提升的规则,因此内容为 null
,因此报错
3、优先级
- 先变量提升
- 再函数提升
若函数提升在前,则var a = function(){}; var a; console.log(typeof a); // 'function'
var a
会覆盖前者,导致输出为undefined
,然而实际输出却不是这样,说明变量提升在前
4、易混淆的例子
var a = 1;
function a() {
console.log(123);
}
a(); // error
执行后会报错,是由于其实际的执行顺序如下
var a;
function a() {
console.log(123);
}
a = 1;
a(); // error
其中,前四行代码:
var a;
function a() {
console.log(123);
}
属于变量提升与函数提升,因此实际执行代码过程中,这四行代码不会执行(在代码执行前,这四行就已经被编译了),因此程序的入口其实是第5行代码 a = 1
,此时a不再是函数了,因此会报错
二、执行上下文的概念
1、概念
- 分为
全局上下文
和函数上下文
全局上下文(window)
永远处于栈底,不会被删除函数上下文
只有在函数被调用时才会产生,调用完毕后自动删除,若只定义了函数而未调用,则不会产生该函数的上下文- 上下文的执行顺序严格按照栈,先进后出
如下,共有三个上下文:
// 分别为 window, a, b
function a() {...};
function b() {...};
a();
b();
2、和作用域的区别
- 作用域分为
全局作用域
和函数作用域
,ES6 后出现了块级作用域(if语句)
作用域
在代码编译时自动生成(无论是什么作用域),而执行上下文
在代码执行时才生成(即使是全局上下文,也要等代码执行时才能生成)- 作用域是静态的,永远不会改变,如下代码 ```javascript var a = 10;
function way1() { console.log(a); }
function way2(f) { var a = 20; f(); } way2(way1); // 10
` way1() `的作用域早已确定,不会因其在 ` way2() `内被调用而改变,因此其仍会输出10
<a name="gvsum"></a>
#### 3、作用域与对象属性的关系
```javascript
var a = {
way: function() { // 1、首先在内部 function 中寻找,失败
console.log(way);
}
}
void 0; // 2、随后来外部作用域中找 way,仍然失败
a.way(); // error
作用域只有 函数作用域
和 全局作用域,当 function
中找不到 way 时,就会去外面(全局作用域)找,仍找不到,报错
正确的写法如下
way:function() {
console.log(this.way);
}
三、上下文与栈的关系
当前执行的程序,永远处于栈顶,如下
void 0; // 1、进入全局上下文
var i = 1;
var a = function() {
console.log('a()');
b(); // 3、进入 b 函数上下文
}
var b = function() {
console.log('b()');
}
a(); // 2、进入 a 函数上下文
输出结果为
a()
b()
更复杂的例子
console.log("i-begin: " + i);
var i = 1;
print(i);
function print(j) {
if(j == 4) return;
console.log("j-begin: " + j);
print(j + 1);
console.log("j-end: " + j);
}
console.log("i-end: " + i);
其共产生了5个上下文,输出顺序为
j-begin: 1
j-begin: 2
j-begin: 3
j-end: 3
j-end: 2
j-end: 1
i-end: 1