js中let,var,闭包以及变量提升

155 阅读4分钟

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。这是因为

  1. 上面代码相当于
var i = 1;
for(;i<=10;i++){
   setTimeout(()=>{
    console.log(i)
   })
}
  1. setTimeout会创建一个“宏事件”,js事件循环中,同步代码for执行过程中,在宏队列中添加了10个事件,每个事件都是输出i。由于同步代码会在输出i之前执行完,在输出i时,i已经累加到11,所以一次性输出10次11.

let用法

let 语句声明一个块级作用域的局部变量,并可以初始化为一个值(可选)。应注意的是var声明的变量并没有块级作用域,即上面说的var声明的变量只有全局作用域或函数作用域。

如何理解块级作用域?

在js中,块级作用域是指变量或常量只在它们所在的块(通常是由花括号 {} 包围的代码块)中可见的作用域。这种作用域只在块内部起作用,块外部无法访问。常见的有if(){}等。

对比var和let在块级作用域的区别

  1. var
var v = 1;
{
 var v = 2;
}
console.log(v); //2
  1. 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并没有被销毁,而是被内部匿名函数所引用,此时就形成了闭包。

用闭包主要用来干什么?

上面的例子只是为了解释闭包是如何产生的,但在现实开发中,闭包的作用主要是:

  1. 延长变量的作用域
  2. 避免变量污染

考虑如下代码

function createCounter(){
 let count = 0;
 return function(){
    return count++;
 }
}
const counter = createCounter();
console.log(counter()) //0
console.log(counter()) //1

  1. 在createCounter函数运行结束后,我们仍然可以访问到count变量,也就是所谓的延长了变量作用域
  2. count变量是定义在createCounter函数中,可以防止在函数外部直接修改,防止了变量污染

变量提升

在js中,用var,function,class定义的变量,函数和类,其定义会被提升到他们作用域的顶部,这种行为被称为变量提升。应注意只有其定义会被提升,如

i = 1;
console.log(i) // 1
var i = 2;

相当于

var i = 1;
console.log(i) // 1
i = 2