JavaScript中的变量提升与函数提升

我们先来看下面这一段代码

console.log(foo)  //undefined
var foo = ”test“

很奇怪对吧,分明foo字符串声明在输出语句之后,为什么输出的结果只是“未找到值”而不是“未找到该变量”呢?别急,我们看接下来的这个例子。

var x = new Date()

function foo (){
    console.log(x)  //undefined
    if(0){
        var x = 10  
    }
    console.log(x) //undefined
}

foo()
console.log(x) //2017-10-13T11:53:30.772Z

是不是觉得更奇怪了?不应该是输出时间信息么?怎么还是找不到值?

这里首先要引入作用域的概念。在JavaScript中是没有块级作用域这个概念的,就是一对花括号“{}”括起来的区域,只有全局作用域和函数作用域。然后我们在引入变量提升的的定义了,“函数声明和变量声明总是被JavaScript解释器隐式地提升(hoist)到包含他们的作用域的最顶端。”

什么意思呢?我们按这段话的意思来改写一下上面那段代码,看看它的真正面目。

var x
x = new Date()

function foo(){
    var x
    console.log(x)  //undefined
    if(0){
        x = 10
    }
    console.log(x)  //undefined
}

foo()
console.log(x)  //2017-10-13T11:53:30.772Z

由于没有块级作用域只有函数作用域,所以无论变量被声明在函数内的什么地方,变量的声明都会被提升到作用域的最顶端,但是请注意仅仅是声明提前了,并没有赋值。所以在这个例子中,if语句内声明的变量x被提前到函数顶部声明,这样就覆盖了全局作用域中的x变量,但由于没有赋值,所以第一个输出语句的值只能是"undefined",又由于“if(0)”,所以if语句内的代码不会被执行,也就是说x变量不会被赋值,所以第二个输出语句依旧输出“undefined”。而第三个输出语句则是正常输出了全局作用域中的x变量。

同理,那么我给的第一段代码中出现的情况就好理解了。就是先声明却没赋值,所以输出语句找不到foo变量的值就只能输出“undefined”了。


其实,不仅仅是变量。函数也一样有提升的情况,刚刚给出定义的时候也提到了。我们来看一段代码。

console.log(f)  //[Function: f]
console.log(x)  //undefined

function f(){
    console.log("Hello")
}

var x = function(){
    console.log("world")
}

要注意JavaScript中函数是一等公民,所以函数永远先被提升,然后才是变量。跟上面的一样,x变量先被提升到全局域顶部声明,此时并没有赋值(它的值是个匿名函数)。其实函数提升的初衷是为了,函数之间的相互递归,不然的话按顺序执行,总会有一个函数出现未声明的情况。

注:如果在同一个作用域中存在多个同名函数声明,后面出现的将会覆盖前面的函数声明。


在ES6中引入了let,const关键字,使用这两个关键字声明的变量是不会上面出现的变量提升的情况的,但是....

'use strict'

var i = 'test'

function foo(){
	console.log(i)
	let i = 10
}

foo() // i is not defined

事实上,使用let声明的变量还是提升了的,不然的话,就应该直接输出“test”。ES6中规定,如果区块中存在let,const关键字,这个区块对这些命令声明的变量会从一开始形成一个封闭作用域,只要在声明之前使用该变量就会报错。

猜你喜欢

转载自blog.csdn.net/hjc256/article/details/80825351