1. 变量提升
例子:
console.log(a); //打印undefine
var a = 3;
上面例子中的打印结果是undefine,并没有报错。我们通常写的var a=0; 这一句代码实际是包含了两个操作过程,首先是声明了一个变量a,然后是对a进行了赋值操作。JS中对于使用var声明的变量具有变量提升的作用,所以变量a的声明被提升到了当前作用域的最顶端。最终经过编译后的代码顺序实际是这样的:
var a;//undefine
console.log(a);
a = 3;
JS在ES6之前,只有全局作用与和函数作用域,上面的例子是在全局作用域中,在函数作用域中需要注意的是函数内部的变量提升只是将变量的声明提升到了函数作用域的最顶端,并没有提升到全局作用域中:
function f1() {
if (true) {
var a = 0; //这里的声明会提升的当前函数作用于的最顶部
a = 3;
}
console.log(a); //打印3
}
f1();
console.log(a); //报错 “a is not defined”,因为变量a的声明只是提升到了函数作用域的最顶部,并不在全局作用域中
JS在ES6中提出了块级作用域,使用let关键字声明的变量只能在块级作用域中访问,并且let声明的变量没有变量提升的特性,所以在声明之前使用会报错。并且不可以重复进行声明。还有一点需要注意,使用let关键字在全局作用域中声明的变量,并不会挂载到window身上。
function f1() {
if (true) {
console.log(b); //报错 “Cannot access 'b' before initialization”
let b = 0;
}
console.log(b); //报错 “b is not defined”
}
f1();
<script>
let a = 0;
console.log(window.a); //undefined 使用let关键字在全局作用域中声明的变量,并不会挂载到window身上。
</script>
2. 函数提升
JS中不光是变量声明具有提升,而且函数声明同样具有提升(具名函数可以提升函数声明和函数实现,字面量函数只可以提升函数声明,不能提升函数实现)。
例子1(具名函数):
console.log(f1); //打印该函数
f1(); //打印"这是函数1"
function f1() {
console.log('这是函数1');
}
例子2:(字面量函数):
console.log(f2); //undefine
f2(); //报错:“f2 is not a function”
var f2 = function () {
console.log('这是函数2');
};
3. 闭包
闭包的概念(个人理解):闭包就是将一个函数作为参数返回,并且这个函数能够在外部访问其他函数的内部变量,形成的这种状态或过程就叫闭包。
例子(计数器实现):
/**
* @name: 创建计数器
* @msg:
* @param {*}
* @return {*}
*/
function createCounter() {
var sum = 0;
return function () {
console.log(++sum);
};
}
var c1 = createCounter();
c1(); //1
c1(); //2
c1(); //3