前言
变量是前端日常中应用的最频繁的语法。在开发过程,尽管晓得怎么用,然而却不晓得为什么,比方变量晋升。
变量晋升和函数晋升在前端面试中常常会考查到,上面介绍它们是如何造成的以及有什么区别。
前置常识
在理解变量晋升前,须要先理解一下 js 的执行过程,js 执行分为两个过程:
- 词法分析阶段:词法剖析次要包含:剖析形参、剖析变量申明、剖析函数申明三个局部。通过词法剖析将咱们写的 js 代码转成能够执行的代码,接下来才是执行。
- 执行阶段。
一般来说变量和函数会在应用之前就会申明,然而也能够在应用之后再进行申明,这是因为函数申明和变量申明总是会被解释器悄悄地被 ” 晋升 ” 到办法体的最顶部。被晋升的操作就处于第一步的词法分析阶段。
变量晋升(Hoisting)
- 只有申明被晋升,初始化不会被晋升
- 申明会被晋升到以后作用域的顶端
例子 1
console.log(num); // undefined
var num;
num = 6;
预编译后
var num;
console.log(num); // undefined
num = 6; // 初始化不会被晋升
例子 2
num = 6;
console.log(num); // 6 =》晋升了 var sum
var num;
预编译后
var num;
num = 6;
console.log(num); // 6
例子 3
console.log(num); // undefined
var num = 6;
预编译后
var num;
console.log(num);
num = 6;
例子 4
function hoistVariable() {if (!foo) {var foo = 5;}
console.log(foo); // 5
}
hoistVariable();
预编译后
function hoistVariable() {
var foo // 将 if 语句内的申明晋升
if (!foo) { // !undefined = true
foo = 5;
}
console.log(foo); // 5
}
hoistVariable();
例子 5
var foo = 3;
function hoistVariable() {
var foo = foo || 5;
console.log(foo); // 5
}
hoistVariable();
预编译后
var foo = 3;
function hoistVariable() {
var foo
foo = foo || 5; // 此时 等号右侧 foo 为 undefined
console.log(foo); // 5
}
hoistVariable();
函数晋升
- 函数申明和初始化都会被晋升
- 函数表达式不会被晋升
例子 1:函数申明可被晋升
console.log(square(5)); // 25
function square(n) {return n * n;}
预编译后
function square = (n) {return n * n;}
console.log(square(5)); // 25
例子 2:函数表达式不可被晋升
console.log(square); // undefined
console.log(square(5)); // square is not a function =》初始化并未晋升,此时 square 值为 undefined
var square = function (n) {return n * n;}
预编译后
var square
console.log(square); // undefined =》赋值没有被晋升
console.log(square(5)); // square is not a function =》square 值为 undefined 故报错
square = function (n) {return n * n;}
例子 3
function hoistFunction() {foo(); // 2
var foo = function() {console.log(1);
};
foo(); // 1
function foo() {console.log(2);
}
foo(); // 1}
hoistFunction();
预编译后
function hoistFunction() {
var foo
foo = function foo() {console.log(2);
}
foo(); // 2
foo = function() {console.log(1);
};
foo(); // 1
foo(); // 1}
hoistFunction();
优先级
- 函数晋升在变量晋升之前
- 变量的问题,莫过于申明和赋值两个步骤,而这两个步骤是离开的。
- 函数申明被晋升时,申明和赋值两个步骤都会被晋升,而一般变量却只能晋升申明步骤,而不能晋升赋值步骤。
- 变量被晋升过后,先对晋升上来的所有对象对立执行一遍申明步骤,而后再对变量执行一次赋值步骤。而执行赋值步骤时,会 优先执行函数变量的赋值步骤,再执行一般变量的赋值步骤。
先解析,再按程序执行
例子 1
typeof a; // function
function a () {}
var a;
// typeof a; // function =》无论放在后面还是前面,解析后执行程序都是一样
预编译后
function a // => 申明一个 function a
var a // =》申明一个变量 a
a = () {} // => function a 初始化
typeof a; // function
例子 2
function b(){};
var b = 11;
typeof b; // number
预编译后
function b; // => 申明一个 function b
var b; // =》申明一个变量 b
b = (){}; // =》function b 初始化
b = 11; // =》变量 b 初始化 =》变量初始化没有被晋升,还在原位
typeof b; // number
例子 3:联合自执行函数
var foo = 'hello';
(function(foo){console.log(foo);
var foo = foo || 'world';
console.log(foo);
})(foo);
console.log(foo);
// 顺次输入 hello hello hello
预编译后
var foo = 'hello';
(function (foo) {
var foo; // undefined;
foo= 'hello'; // 传入的 foo 的值
console.log(foo); // hello
foo = foo || 'world';// 因为 foo 有值所以没有赋值 world
console.log(foo); //hello
})(foo);
console.log(foo);// hello,打印的是 var foo = 'hello' 的值(变量作用域)