var 用法
var 语句 用于声明一个函数范围或全局范围的变量,并可将其初始化为一个值(可选)。如以下代码:
var v = 1;
function fun(){
var v = 2;
console.log(v);
}
fun();
console.log(v);
因为var在全局作用域和函数作用域都定义了变量v,所以上面控制台会依次输出2,1。
应注意var声明的变量只有全局作用域和函数作用域,因此我们在全局for循环中用var声明变量实际上就是在全局上声明变量。如
for(var i =1;i<=10;i++){
setTimeout(()=>{
console.log(i)
})
}
控制台会在循环结束后,一次性输出10次11。这是因为
- 上面代码相当于
var i = 1;
for(;i<=10;i++){
setTimeout(()=>{
console.log(i)
})
}
- setTimeout会创建一个“宏事件”,js事件循环中,同步代码for执行过程中,在宏队列中添加了10个事件,每个事件都是输出i。由于同步代码会在输出i之前执行完,在输出i时,i已经累加到11,所以一次性输出10次11.
let用法
let 语句声明一个块级作用域的局部变量,并可以初始化为一个值(可选)。应注意的是var声明的变量并没有块级作用域,即上面说的var声明的变量只有全局作用域或函数作用域。
如何理解块级作用域?
在js中,块级作用域是指变量或常量只在它们所在的块(通常是由花括号 {} 包围的代码块)中可见的作用域。这种作用域只在块内部起作用,块外部无法访问。常见的有if(){}等。
对比var和let在块级作用域的区别
- var
var v = 1;
{
var v = 2;
}
console.log(v); //2
- let
let v = 1;
{
let v = 2;
}
console.log(v) // 1
利用let为定义块级作用域变量特性,让上面例子输出1到10,如
for(let i =1;i<=10;i++){
setTimeout(()=>{
console.log(i)
})
}
控制台会输出1到10。这是因为每次循环i都会创建一个块级变量。 上面代码相当于
for(var i = 1;i <= 10;i++){
{
let i2 = i;
setTimeout(()=>{
console.log(i2)
})
}
}
可以使用闭包来模拟块级作用域的效果
上面代码可以改成
for(var i = 1;i <= 10;i++){
(function(j){
setTimeout(()=>{
console.log(j)
})
})(i)//这里立刻执行函数引用了外部变量i,形成了闭包,因此在每次循环之后i的作用域被延长,存在于内存中
}
什么是闭包?
在js中,闭包指的是在内部函数中,可以访问外部函数的参数或者变量,并且这些参数和变量在外部函数调用结束后,仍然能够被访问。解释以下就是,一个函数内部定义了另一个函数,并且这个内部函数引用了外部函数的变量或参数,此时就形成了闭包,在外部函数调用结束后,这些变量和参数会一直存在于内存当中,直到闭包被销毁。如:
function add(x){
return function(y){
return x + y
}
}
const add1 = add(1); //这里add外部函数执行结束后,其参数x并没有被销毁,而是被内部匿名函数所引用,此时就形成了闭包。
用闭包主要用来干什么?
上面的例子只是为了解释闭包是如何产生的,但在现实开发中,闭包的作用主要是:
- 延长变量的作用域
- 避免变量污染
考虑如下代码
function createCounter(){
let count = 0;
return function(){
return count++;
}
}
const counter = createCounter();
console.log(counter()) //0
console.log(counter()) //1
- 在createCounter函数运行结束后,我们仍然可以访问到count变量,也就是所谓的延长了变量作用域
- count变量是定义在createCounter函数中,可以防止在函数外部直接修改,防止了变量污染
变量提升
在js中,用var,function,class定义的变量,函数和类,其定义会被提升到他们作用域的顶部,这种行为被称为变量提升。应注意只有其定义会被提升,如
i = 1;
console.log(i) // 1
var i = 2;
相当于
var i = 1;
console.log(i) // 1
i = 2