前端开发『巨坑』系列—变量提升详解

前言


在经过了一轮面试挫败以后,我发现了一个问题:
通常大公司对你的底层基本功非常感兴趣,他们根本不在乎你能够使用多少个前端框架。因为真正的好公司都是自己开发框架的。

今天我们要像discovery节目一样深入前端开发底层的知识,揭露js es5中那些常见的疑难杂症以及解决办法。让你搞清楚为什么这些大厂喜欢考你这些问题,然而这些问题背后又隐藏了什么历史巨坑,让龙哥给你层层揭开真相!

历史谜案!变量提升


不知道你又没有发现,在js中var是一个非常有意思的关键字。
有意思到,它根本都不按照编程语言的逻辑去执行—从上到下执行。什么意思,我TM还没有声明这个变量的时候,我居然就可以用了。

console.log(name); //dangyunlong
var name = "dangyunlong"

这是非常残酷的一个事实。说明es5中通过var来声明变量,这个变量无论在当前作用域的什么位置,它都会被提到作用域的最顶部。

console.log(name);//报错
let name = 1;
console.log(name);//1

当然,这个问题在es6中已经通过let关键字,解决了。

那么,仅仅就这样而已吗?当然不是!

我们再更进一步,把这个例子写的稍微复杂一点,给它增加一个作用域。

var name = 2;
(function(){
    console.log(name);//2
})()

首先我们普及两个知识点:

1.js中没有块作用域,只有全局和函数作用域。
2.自执行匿名函数,也叫做立即执行函数,无论你写在页面的任何位置,它都会先于所有代码执行。

ok,然后我们再回来看这个例子。自执行匿名函数先于代码执行,可是,var的name还是存在。这说明var的权限是更高,这里打印出来的是全局的name。

懵逼的开始!


看完了上面的例子,咱们再来看这个:

(function(){
    console.log(num);//undefined
    var num = 1;
})()

这里你感觉num应该打印出来1吗,那你就错了,这里打印出来undefined。

但是我们上面说,var出来的值,再任何位置都可以用啊?为什么在函数体中不能先使用再声明呢?

龙哥猜测,这可能跟我们在函数体内声明变量,该变量成为局部变量有关系,

(function(){
    console.log(num);//undefined
    var num = 1;
    console.log(num);//1
})()

放到下面就可以打印出来了。

来道面试题做做

var name = "tom";
(function(){
    if(typeof name==="undefined"){
        name = "jack";
        console.log("goodbye"+name);
    }else{
        console.log("hello"+name);
    }
})()

这道题应该打印什么?
看过前面的例子我们就知道了,应该打印hello tom。

我们来解释一下,首先,全局里面有var的变量,全局的肯定最大,无论在哪都能访问到。所以typeof name肯定就不是undefined。于是就进入了判断的else环节,所以打印出来hello tom。

然后龙哥把这道题升级了:

var name = "dangyunlong";
(function(){
    console.log(name); //第一个
    var name = "lilei";
    name = "gaoyuanyuan"
})()
console.log(name); //第二个

function fun(){
    name = "shiliujie"
}
fun();
console.log(name); //第三个

请问这三个console.log分别打印什么。

解:
1.第一个我们跟前面一题的道理一样,打印undefined。
2.第二个打印dangyunlong。有的同学可能会问,第二个不是打印gaoyuanyuan吗?注意,如果没有它上面的var name = “lilei”它确实会打印gaoyuanyuan。但是有了var,表示当前的name已经指函数体的局部变量name了,而不是全局那个。
3.第三个打印shiliujie。道理跟第二个一样,因为这次函数体内修改的是全局变量的值,并没有声明局部变量!

历史谜案!函数声明提升!


先来补充一个小知识:
什么是函数声明和函数表达式。

//函数表达式
var fun1 = function(){
    console.log("我是fun1");
}

//函数声明
function fun2(){
    console.log("我是fun2");
}

来看这俩哥们。

咱们也不卖关子了,这俩在执行上面没有任何区别。是一模一样的。

那么问题就来了,难道这个也存在提升的问题吗?是的。

//函数表达式
var fun1 = function(){
    console.log("我是fun1");
}
fun1(); //我是fun1

//函数声明
function fun2(){
    console.log("我是fun2");
}
fun2(); //我是fun2

这样运行是没问题的,但是,请看这种写法:

fun1(); //fun1 is not a function
//函数表达式
var fun1 = function(){
    console.log("我是fun1");
}

fun2(); //我是fun2
//函数声明
function fun2(){
    console.log("我是fun2");
}

函数表达式直接报错,但是,函数声明可以运行。

这TM就太厉害了。我们使用var的关键字出来的函数没有被提升,但是直接function却提升了,这是怎么回事?

其实这里面有一个误区,就是除了变量会提升以外,函数也会被提升。

如果我们直接声明一个函数,他也一样会提升到顶部。但是,如果我们以变量形式声明一个函数表达式,它其实走的是变量提升:

fun1(); //fun1 is not a function
//函数表达式
var fun1 = function(){
    console.log("我是fun1");
}

//执行顺序
var fun1;
fun1();
fun1 = function(){
    console.log("我是fun1");
}

这样的话,会报fun1不是function,因为变量提上去了以后,变量并没有被赋值。然后我们又执行了fun1();
所以这里就直接报错了。fun1这时候还不是函数,所以肯定是报这个错误了。

最后总结


所以龙哥帮大家总结除了以下结论,以后再做题的时候,一定要注意:

1.如果题中有函数,先看是否在函数内部使用了var。如果使用了,这里面的赋值就是指局部变量。局部变量没有提升的问题,如果先打印,会报undefined。
2.函数中没有var直接赋值的,修改的是全局的值。
3.全局变量无论在什么地方声明,都会提升到作用域的顶部。所以即使在开头就打印,打印出来的也是var的值。
4.函数声明有提升的问题,函数表达式走的是变量提升,所以提前运行会报这个方法不是一个function。

到此,提升问题全部浮出水面,最好的解决办法当然是通过let。这个提升的问题一定要注意,否则很有可能你无意之中修改了全局数据,导致溢出污染。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容