一,变量的类型
ES5将变量分为两种类型,全局变量和局部变量(私有变量)、自由变量。
自由变量:在局部作用域中没用被声明,但是引用的变量(因此他可能是局部变量,也可能是全局变量)
二,变量的生命周期
局部变量:在函数中被声明时开始,在函数运行后被删除
全局变量:在全局中被声明时开始,在页面关闭后被删除
自由变量:在所在的作用域中声明时开始,在所在的作用域中销毁后被删除
三,三种变量区别对比
全局变量:相当于window的一个属性,在全局任何地方都可以访问
局部变量:只能在当前所在作用域中访问(除闭包外)
自由变量:在函数作用域中引用的自由变量, 会按照作用域链去查找。如果找到全局,那此变量就是全局变量;如果找到外层的局部作用域,那此变量还是局部变量
四,var声明的变量和不使用var声明的变量区别
全局作用域中,带var和不带var声明的变量效果一样,只是带var更像一种规范的写法,不带var更像是给window设置一个属性。
函数作用域中,带var声明的变量即为局部变量;不带var声明的变量为全局变量。
五,变量声明提升
5.1 概念
变量声明提升是只当栈内存作用域形成时,js代码执行之前,浏览器会先把带有var和function关键字的变量提前声明,这种预先处理的机制就叫做变量提升机制也叫预定义。
变量提升只发生在当前作用域。例如,在页面加载时,只有全局作用域的变量会声明提升。
5.2 变量声明提升原理
js引擎会在代码执行之前进行一次预编译,将带有var关键字的变量提升到最顶部,默认值为undefined。然后在按顺序执行代码。
eg: console.log(a) js执行为==> var a = undefined
var a = 12 console.log(a)
a = 12
六,函数声明提升
函数声明提升与变量声明提升的本质上基本一致,但函数声明提升,也会在堆内存开辟一个空间,他的值是函数体的字符串。所以再预编译阶段,将函数提升到顶部声明,并将默认值赋值为一个引用,引用的值是堆内存的函数体字符串。
eg: foo(); js执行为===> var foo = 0xcc124 // 堆内存的引用
function foo() { foo();
console.log(window) // 输出 f foo() {
} // console.log(window)
// }
七,带等号的匿名函数声明提升
print()
var print = function() {
console.log('哈喽,世界')
}
print()
/*输出
Uncaught TypeError: print is not a function
/
这种匿名函数会被当做变量赋值,因此等号左边会被进行提升,而函数体不会被提升。原理如下:
var print = undefined;
undefined()
八,条件判断里的变量声明提升
第一种情况:
if-else代码块中声明了变量。这样的变量无论if条件是否成立,都会被提升。
eg: if (false) {
var a = 12; // a变量会被提升
}
第二种情况:
if 判断条件()中的表达式不会被提升。
eg: if (function () {}) { // 表达式function() {} 不会被提升
// ... ...
}
九,重名下的变量和函数声明提升
- 如果同时使用var和function定义了a变量和a函数,那么会先进行变量提升,再进行函数提升,而函数会覆盖变量的值,因此输出的值永远是函数。
var a = 12;
var a = function() {
console.log(window)
}
console.log(a); // f function() { console.log(window) }
- 如果同时使用function定义了多个重名的a函数,那么提升阶段,后面的会替换前面的声明。
var a = function() {
console.log(1)
}
var a = function() {
console.log(2)
}
var a = function() {
console.log(3)
}
console.log(a) // function() { console.log(3) }
十,函数形参的变量提升
函数的形参也会在当前函数执行时进行变量提升。
function a(b){
console.log(b);
}
a(12);
// 等价于
// function a(b) {
// var b = undefined;
// b = 12;
// }
但函数内部,声明提升只执行一次,如果函数内部再次使用var xx进行声明,则不会执行。
function a(b) {
console.log(b);
var b
console.log(b+1);
}
a(1);
// 在函数执行时,会先提升形参b的声明 var b = undefined; b = 1;
// 所以第二行声明var b,不会再次进行声明
十一,IIFE自执行函数的声明提升
全局作用域下:IIFE自执行函数,无论是否匿名,都不会进行声明提升。
匿名的IIFE,因为他没有var 和 function定义的名称。
非匿名的IIFE,因为他有自己的作用域,所以也不会进行声明提升。
IIFE自己的作用域下:都会进行声明提升。
且,非匿名的IIFE声明提升,修改函数名称是无效的。例如:
var a = 10;
(function a(){
console.log(a)
a = 20 // 函数名称a无法被修改
console.log(a)
})()
// ƒ a(){a = 20 console.log(a)} ƒ a(){a = 20 console.log(a)}
十二,变量声明提升的应用实践
变量声明提升主要是为了了解js的运行机制,主要的应用是在开发中尽量避免使用这个特性,做到规范编写代码,先声明再使用,或者尽量使用let、const代替var。