1.什么叫提升?
提升:就是js代码执行之前引擎会进行预编译,预编译期间就会将变量声明和函数声明提升到其对应作用域的最顶端。提升一般是对用var
关键字申明的变量或函数。let、const
关键字申明的没有提升。
来个小例子说明一下:
console.log(a) // undefined
fn() // this is fn
var a = 1
function fn () {
console.log('this is fn')
}
这段代码的执行顺序:
function fn() {
console.log('this is fn')
}
var a
console.log(a)
fn()
a = 1
解释:函数提升的优先级比变量的要高,所以就先声明了函数,再声明变量。由于变量a声明了,但没有赋值,就输出undefined。
再来一个小例子:
var a = true
foo()
var foo = function () {
if(a) {
var a = 10;
}
console.log(a)
}
js执行顺序:
var a
var foo
a = true
foo() // 会报错
foo = function () {
if(a) {
var a = 10;
}
console.log(a)
}
解释:由于foo声明的是一个变量,但是你把他当作一个函数来调用,所以就会出现报错。
最后一个复杂点的例子:
var a= 1
function foo () {
a = 10
console.log(a)
return;
function a() {}
}
foo()
console.log(a)
执行顺序:
function foo () {
var a = function () {}
a = 10
console.log(a) // 10
return;
}
var a
a = 1
foo()
console.log(a) // 1
解释:不用被foo函数中的a变量给迷惑了,它只是一个局部变量,我们最后一个console.log(a)是取不到它的值得,所以还是1。对于function a() {}
变成var a = function () {}
,在js中,函数就属于是一个变量,这里就切换中申明形式而已,它们还是等价的。。
函数、变量提升:就是提前声明函数、变量。但是要注意注意作用域,它只是在他得作用域上提前声明而已。
2.es6的块级作用域
全局作用域:
用var在全局(函数外)声明的所有变量,都具有全局作用域。或者在函数中,没有申明变量,直接赋值,这个变量会自动提升到全局作用域,即全局变量。
var a = 'hello' // 全局变量
function fn () {
b = 'word' // 提升为全局变量
}
局部作用域:
在函数内部申明的变量,叫做局部变量。只能在函数内部访问到。
function fn() {
var a = 'hello'
console.log(a) // hello
}
fn()
console.log(a) // 会报错,没定义
注:在{}
内用var
申明的变量,属于全局变量,不是局部变量。局部变量是在函数内用var
关键字申明的。
例:
if(true) {
var a = 'hello'
}
console.log(a) // hello
块级作用域:
利用let
关键和const
关键来实现块级作用域。用let
和const
申明的变量在{}内有效,在{}之外不能访问。在同一层作用域中,不能用let和const关键字重复定义同一个变量。
let和const申明的变量,是没有变量提升的。js执行顺序就是从上往下执行。
{}:一般是函数、if、循环这些
利用let块级作用域来实现闭包:
for(var i = 0; i < 4; i++) {
setTimeout(()=>{
console.log(i)
},100)
}
这个打印出来的是:4、4、4、4
使用闭包处理:
for(var i = 0; i < 4; i++) {
setTimeout((function (i){
return () => {
console.log(i)
}
})(i),100)
}
使用闭包之后,打印出来的是:0、1、2、3
使用let块级作用域来处理:
for(let i = 0; i < 4; i++) {
setTimeout(()=>{
console.log(i)
},100)
}
这里打印出来的也是:0、1、2、3
解释:let声明模仿IIFE模式,每一次迭代循环都会创建一个新的变量,并在迭代的过程中,把同名变量的值初始化,所以就可以打印出我们想要的结果。