思维导图
回顾作用域
在编译的代码生成阶段,编译器会创立在对应作用域创立变量,将2者关联起来。
先申明后赋值
讲到var
申明的变量会进行“变量晋升”,大家可能都感觉不稀奇,然而我这里还是写进去。
a = 2;var a;console.log(a); // 2
console.log( a ); // undefinedvar a = 2;
var
申明的变量会进行“变量晋升”
下面的代码会变成这样
var a;a = 2;console.log(a); // 2
var a; // 申明未赋值默认值是undefinedconsole.log( a ); // undefineda = 2;
编译器先“入手”
总结: 变量和函数在内的所有申明都会在任何代码被执行前首先被解决。
var a = 2;console.log( a ); // 2
下面代码能够看出
var a; // 编译器申明(先,不论在源代码的哪个地位)a = 2;// 代码执行赋值console.log( a ); // 2
函数晋升
总结: 函数申明会被晋升,然而函数表达式却不会被晋升。
foo();function foo() { console.log(a); // undefined var a = 2;}
下面代码能够写成上面这样
function foo() { var a; console.log(a); // undefined a = 2;}foo();
函数表达式不必晋升,函数表达式就是把一个函数赋值给一个变量,既然都是赋值,那么天然不会晋升,只有申明才会晋升。即便是具名的函数表达式,名称标识符在赋值之前也无奈在所在作用域中应用。
foo(); // TypeError 这里foo是默认值undefined(var的起因)bar(); // ReferenceErrorvar foo = function bar() { console.log(a); // undefined var a = 2;}
函数优先
函数申明和变量申明都会被晋升。然而函数会首先被晋升,而后才是变量
foo(); // 1var foo;function foo() { console.log(1);}foo = function () { console.log(2);};
下面代码foo()
是1不是TypeError。所以函数申明是比变量申明更优先。
foo(); // TypeError: foo is not a functionfunction foo() { console.log(2);};var foo; // 被疏忽
函数申明还是能够笼罩
foo(); // 3function foo() { console.log(1);}var foo = function () { console.log(2);};function foo() { console.log(3);}
总结 申明自身会被晋升,而包含函数表达式的赋值在内的赋值操作并不会晋升。