最近在吃饭的时候看到一道关于函数声明提升的问题
var a = 1;
function b() {
a = 10;
return;
function a() {
}
}
b();
console.log(a) //1
很多人在看第一眼的时候会认为结果为10,我作为一个小白第一反应也是认为函数内部的a是全局变量,所以在执行b后a的值变为10,之后再看了后面的分析后才恍然大悟,在b函数执行后a依然是1,于是琢磨这写了人生中第一篇博客,写的不好请见谅,若有技术问题,会在第一时间解决
1.变量声明
使用var声明的变量,如果在声明时变量没有赋值,系统则默认赋值为undefined
2.没有块级作用域
在ES6的let,const出现之前,JavaScript是没有块级作用域的,也就是自己的执行环境,在《JavaScript高级程序设计》中提到
if (true) {
var color = "blue";
}
alert(color); //"blue"
在JavaScript中,if语句中的变量声明会将变量加到当前的执行环境(这里指的是全局环境)中相当于以下代码,而var声明的变量是在JavaScript预编译阶段就执行了的
//预编译阶段
var color;
//执行阶段
if (true) {
color = "blue";
}
console.log(color); //"blue"
还有一个非常经典的问题
for (var i = 0; i < 10; i++) {
//这里做任意事情
}
alert(i); //10
这里弹出i的值为10,可以理解为for循环开始的时候定义了i变量,然后在for循环完成后,i变量依然存在于全局环境中,并且在alert(i)的时候for循环已经结束,i的值等于10,所以alert(i)的结果就是10了
3.变量声明提升
再来回到第一个例子,如果它的条件为false
if (false) {
var color = "blue";
}
alert(color); //undefined
经过变量声明提升后实质与以下代码是一样的
//预编译阶段
var color;
//执行阶段
if (false) {
color = "blue";
}
console.log(color); //undefined
因为color定义了,但是if的条件为false,所以只声明了color变量却没有赋值,最后显示undefined
JavaScript编译器会把变量声明看成两个部分分别是声明操作(var color)和赋值操作(color="blue")
声明操作在编译阶段进行,声明操作会被提升到执行环境的顶部,值是undefined(表示未初始化)
赋值操作会被留在原地等待执行阶段
注释:声明提前是在JavaScript引擎的预编译时进行,是在代码开始运行之前。
4.函数声明提升
函数的两种创建方式
1. 函数声明
2. 函数表达式
函数声明:
function sum(num1, num2) {
return num1 + num2;
};
函数声明提升会在编译阶段把函数和函数体整体都提前到执行环境顶部,所以我们可以在函数声明之前调用这个函数
sum(10, 10); //20
function sum(num1, num2) {
return num1 + num2;
};
JavaScript编译器在预编译阶段把代码编译后其实是这样的
var sum;
sum = function (num1, num2) {
return num1 + num2;
};
sum(10, 10); //20
函数声明会把先声明一个变量sum,同时把函数赋值给变量sum
函数表达式:
var sum = function (num1, num2) {
return num1 + num2;
}
通过把匿名函数赋值给变量的定义函数的方法叫做函数表达式,而通过函数表达式创建的函数和函数声明不一样,函数表达式没有函数声明提升,只有定义了之后才能调用,上述代码实质上是这样的
var sum;
sum = function (num1, num2) {
return num1 + num2;
}
在函数表达式创建的函数之前执行sum函数
sum(10, 10);//报错 Uncaught TypeError: sum is not a function
var sum = function (num1, num2) {
return num1 + num2;
}
因为通过函数表达式创建的函数没有函数提升,所以执行sum(10,10)时根本没有创建函数,但是没有创建函数应该会显示undefined,为什么会报错呢,实质上上述代码是这样的
//预编译阶段
var sum;
//执行阶段
sum(10, 10);
sum = function (num1, num2) {
return num1 + num2;
};
可以把函数表达式看作2部分,第一部分声明变量sum,第二部分给变量sum赋于这个匿名函数,而通过var声明的sum存在变量提升,在预编译阶段会提升到执行环境顶部,然后在执行sum(10,10)就会报错,提示sum不是一个函数,undefined当然不是一个函数啦,第三行执行完毕后,这时sum才指向了这个函数
函数声明提升>形参>变量声明提升
当同时存在函数声明和变量声明时,来看这个例子
getName(); //1
var getName = function () {
console.log(2);
}
function getName() {
console.log(1);
}
getName(); //2
当存在函数声明和变量声明时,函数声明在前,变量声明在后,所以上述代码是这样的
//预编译阶段
//使用函数声明创建的函数函数声明提升
var getName;
getName = function () {
console.log(1);
};
//变量声明提升,因为之前已经声明过getName,所以再次声明var会被忽略
var getName;
//执行阶段
getName(); //1
getName = function () {
console.log(2);
};
getName(); //2
再来看一道面试题
var a = 1;
function b() {
a = 10;
return;
function a() {
}
}
b();
console.log(a); //1
在b函数内部,因为存在函数变量声明提升,导致return后面的function a () {}被提升到了b函数内部的顶部,等同于在b函数内部声明了局部变量a(与函数外部的全局变量a不同),并且给变量a赋值给一个匿名函数,最后再给局部变量a赋值为10,最后return退出函数,局部变量a在b函数执行完后被销毁,console.log(a)打印变量a的时候会搜索变量a,最后只在最后的全局变量中发现了a,随即打印了a的值,即数字1(第一行声明的变量a)
var a = 1;
function b() {
var a;
a = function () {
};
a = 10;
return;
}
b();
console.log(a); //1