变量提升
JavaScript 的变量提升有两种, 用 var 声明的变量以及用 function 声明的变量.
用 var 声明的变量
我们先来看下面这段代码, a 的值是多少
代码 1
- console.log(a);
- var a;
按照以往编程语言的思路来看, 代码自上而下运行, 按这种思路, 会报错, 因为执行到第 2 行时, 变量 a 还没有定义, 所以会报错 a is not defined
然而事实上答案是 undefined
好, 抱着疑惑, 我们看下面的代码
- var a;
- console.log(a);
我们发现, 这两段代码是一样的, 那么又有一个新的问题, 是不是有没有 var a 都无所谓, 它的答案始终是 undefined, 才造成了以为变量会提升的错觉, 于是我写了代码 3
代码 3
console.log(a);
好, 它终于报错了, 所以这证明了 JavaScript 代码并不是自上而下执行的, 至少从表面看上面是这样的.
于是我们再看代码 4
代码 4
- console.log(a);
- var a = 2;
因为变量提升嘛, 所以答案是 2, 然而事实上, 它依然是 undefined,why?
这时候我们有请编译器这位负责语法分析及代码生成等脏活累活的大佬.
编译器在看到 var a = 2;, 它会将其看做两个声明, var a; 和 a = 2, 第一个声明在编译阶段进行, 第二个声明会被原地等待执行阶段.
也就是说上面代码, 会变成下面的这段代码
- var a;
- console.log(a);
- a = 2;
所以最终会是 undefined
好, 我在啰嗦一下, 看这段代码 5
代码 5
- a = 2;
- var a;
- console.log(a);
我想大家应该已经知道这段代码执行时的真正顺序及其答案了, 没错, 答案是 2, 但我想说的是把第 2 行给注释掉, 答案依然是 2, 但这个和变量提升没啥关系了, 是严格模式与非严格模式的锅, 在非严格模式下允许开发者可以不使用声明变量的关键字, 但在严格模式下是不可以的, 它会报错的.
用 function 声明的变量
与 var 一样, function 声明的变量依然会提升.
- log(5);
- function log(mes){
- console.log(mes)
- }
按照之前的变量提升的理解, 这段代码的真正顺序是这样的,
- function log(mes){
- console.log(mes)
- }
- log(5);
很好, 很正确, 那么再看下一段代码
- log(5);
- var log = function(mes){
- console.log(mes)
- }
它报错了, log is not a function, 从这里我们可以看出, 这种函数表达式是不会被提升的, 只有函数声明才会被提升, 试着在最前面新增一行代码 console.log(log), 会先输出 undefined.
所以这里的真正顺序是
- var log;
- log(); // 这时候只是声明了 log 这个变量, 并不是函数, 却用函数的方法调用它, 所以会报错, 说这不是一个函数.
- log = function(mes){
- console.log(mes)
- }
在 function 里用 var 声明变量
我们虽然知道, var 声明的变量会提升, 但并不知道会提升到哪个程度.
在此之前来看一段代码
- var a = 4;
- function foo(){
- var a = 5;
- console.log(a);
- }
- foo();
- console.log(a)
答案是 5,4, 先输出 5, 再输出 4.
用 var 声明的变量是有函数作用域的, 所以 foo 里的 a 和 foo 外面的 a 没有任何关系, 这种情况正是我想要的.
再改下代码
- function foo(){
- a = 5
- console.log(a);
- var a;
- }
- foo();
- console.log(a)
答案是 5,a is not defined
第 4 行代码输出 5, 第 9 行报错.
这种情况就是变量提升只会提升到变量所在的 作用域的顶部, 不会提升到父级作用域.
因此可以得出一个结论: 变量提升只会将变量提升到自己所在的作用域的顶部
函数优先
既然用 var 和 function 的变量都有提升的功能, 那如果同一个变量用这两种都声明会怎样, 好吧, 看标题就知道了, 函数优先.
具体看下代码
- foo();
- var foo;
- function foo(){
- console.log(1)
- }
- foo = function(){
- console.log(2)
- }
答案是 1
这段代码其实这样子的
- function foo(){
- console.log(1)
- }
- foo();// 1
- foo = function(){
- console.log(2)
- }
仔细一看, var foo; 没了, 没错, 它被引擎忽略了, 认为重复声明所以把它抛弃了.
好, 既然 var 声明的变量比不了函数声明, 那就用函数声明, 多次声明同个变量.
- foo()
- function foo(){
- console.log(1);
- }
- foo()
- function foo(){
- console.log(2);
- }
- foo()
- function foo(){
- console.log(3);
- }
- foo()
foo 声明了三次, 调用了四次, 每次调用的结果都是 3, 所以最后的函数声明会覆盖之前的函数声明
但是 var 还想挣扎一下, 觉得还是有必要证明自己的存在感的.
- foo()
- function foo(){
- console.log(1);
- }
- var foo;
- foo()
- foo = function(){
- console.log(2);
- }
- foo()
- function foo(){
- console.log(3);
- }
- foo()
仔细看, 中间那部分代码改了, 依次输出 3,3,2,2
虽然 var foo 被忽略了, 但下面的函数还是有用的, 这段代码可以看成是这样的
- function foo(){
- console.log(3);
- }
- foo();//3
- foo();//3
- foo = function(){
- console.log(2);
- }
- foo();//2
- foo();//2
在普通块内部声明函数
之前是在作用域声明函数, 现在来块里面声明函数
- function foo(){
- console.log(b); // undefined
- b(); //TypeError: b is not a function
- var a = true;
- if(a){
- function b(){
- console.log(2)
- }
- // 下面这段代码和上面的结果一样
- // var b = function(){
- // console.log(2)
- // }
- }
- //b() --> 这里会被执行
- }
- foo()
从上面看上去, b 是 undefined, 证明这个变量还是有的, 只不过它并不是一个函数, 这情况和用函数表达式差不多.
总结
提升分为函数声明提升和变量声明提升
声明变量用 var, 声明函数用 function
变量提升会将变量提升到自己所在作用域的顶部
函数表达式不存在提升的机制.
函数声明和变量声明同时声明同一个标识符时, 函数声明优先
多个函数声明同一个标识符时, 最后一个声明覆盖先前的声明
来源: https://www.cnblogs.com/tourey-fatty/p/12104583.html