文前留悬念
既然进来了,给你一个检验自己的机会:以下几种情况的输出结果是什么呢?
情况一
console.log(a)a = 'hello'
情况二
a = 'hello'var aconsole.log(a)
情况三
console.log(d)var r = falseif (r) {var d = 'hello'}
情况四
foo()var foo = function() {console.log('hello')}function foo() {console.log('world')}
情况五
function foo() {console.log('hello')}foo()function foo() {console.log('world')}
可以拉到最底看答案,如果都答对了,你就可以关掉该标签页啦!
正文整理
一、声明 vs 赋值
var foo = 'hello'
上面的代码可以拆分为以下两行代码:
var foo // 声明foo = 'hello' // 赋值
虽然在我们善良的前端人眼里,它俩(声明与赋值)亲如兄弟,但在JavaScript 引擎眼里,声明才是亲儿子!为何怎么说?
- 没有声明就赋值?报错!
- 编译环节,无论变量在哪声明,都会请到代码“开头”,并赋以默认值“undefined”,而这就叫变量提升。
- 只有当编译完,换句话说就是当所有声明变量都提升后才会真正进入执行阶段(赋值、判断等逻辑操作)。如下图:

二、函数也会被提升
情况一:完整的函数声明,没有涉及赋值操作
foo()function foo() {console.log('hello')}
对于这种情况,函数体将整个被提升,同下:
function foo() { // 整个函数体被提升至代码“开头”console.log('hello')}foo()
情况二:先声明变量后赋值函数体
foo() // TypeError: foo is not a functionvar foo = function() {console.log('hello')}
这种情况下声明和赋值操作将分开,声明部分将被提升并赋以初始值“undefined”,所以这也就是为什么出现TypeError错误,等同如下代码:
var foo = undefined // 声明部分被提升并赋以初始值“undefined”foo() // TypeError: foo is not a functionfoo = function() {console.log('hello')}
三、函数可是第一公民,对于提升,函数优先,变量其次
那既然变量和函数体声明都会发生提升,那到底哪个先?先看一下代码:
foo()var foo = function() {console.log('hello')}function foo() {console.log('world')}
1)假如变量提升优先,代码等同如下:
var foo = undefinedfunction foo() {console.log('world')}foo()foo = function() {console.log('hello')}
2)假如函数声明提升优先,代码等同如下:
function foo() {console.log('world')}var foo = undefinedfoo()foo = function() {console.log('hello')}
输出结果是:TypeError: foo is not a function
3)结论:变量提升优先!?
4)大反转!非也!
尽管假设变量提升优先的结果是正确的,但其实是函数提升优先,至于为什么,这个就得问JavaScript 引擎,可能真的就是因为“函数是JS第一公民”的原因吧。(网上实在查询不到为什么要函数提升优先)
四、同名变量提升,惨遭忽略,JS变量:“我太难了!”
上面讲了假如函数提升优先,那应该是报错的,但是为什么实际情况却不会呢?原因其实也就是因为“该变量已经被声明了,所以无需再次声明”,所以foo同名变量的提升被忽略了,如下:
function foo() {console.log('world')}var foo = undefined // 因为foo变量已经被声明,所以这次提升操作被忽略了foo()foo = function() {console.log('hello')}
五、同名函数提升,长江后浪推前浪
同名变量声明会被忽略,那同名的函数声明呢?
那倒不会,这反而是“长江后浪推前浪”,后一个函数体声明将直接覆盖之前声明的同名函数。
答案揭秘
情况一:ReferenceError: a is not defined (报错)
情况二:hello
【原因】在js代码编译时a变量的声明会被提升至最前,同下:
var a = undefined // a变量被提升至最前a = 'hello'console.log(a)
所以答案不是 undefined 而是 hello 。具体说明请点击:链接
情况三:undefined
【原因】在js代码编译时判断语句等均未开始工作,无论何处的变量声明都会被提升,同下:
var r = undefined // r变量的声明被提升var d = undefined // d变量的声明被提升,所以d变量相对第三行而言是已经声明了console.log(d)r = falseif (r) {d = 'hello' // 编译时才不理逻辑真假,看到声明了就要揪出来}
所以答案不是 ReferenceError: d is not defined 而是 undefined 。
情况四:world
【原因】函数同变量一样都会提升至最前,但是函数可是Js的第一公民,可伶的foo变量声明会因为和前面的函数同名而惨遭忽略。
function foo() {console.log('world')}var foo = undefined // 因为函数提升优先,后面的同名变量的提升都会被忽略,所以这句等同没有foo()foo = function() {console.log('hello')}
所以答案自然也就是 world 而不是 TypeError: foo is not a function 或者 hello 。具体说明请点击:链接
情况五:world
【原因】函数提升,前面已声明的函数将被后面同名函数覆盖,所以等同:
function foo() { // 被下面的同名函数foo函数覆盖了console.log('hello')}function foo() {console.log('world')}foo()
所以答案是 world 而不是 hello 。
