JS中var、let、const的区别,是否能用let和const取代var

ECMAScript和JavaScript关系:
  ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展。ES6是ECMAScript的简称。
这里先介绍一下块作用域的概念:
JS中作用域有:全局作用域、函数作用域,没有块作用域的概念。块作用域是ES6新增的,是指由{}包括起来的内容,if语句和for语句里面的{}也属于块作用域。

var、let、const的区别

1、var定义的变量,没有块的概念,可以跨块访问,不能跨函数访问;
  let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问;
  const用来定义常量,使用时必须初始化,只能在块作用域里访问,而且不能修改。

<script type="text/javascript">
  //块作用域
  {
     var a = 1;
     let b = 2;
     const c = 3;
     // c = 4; //报错,因为const定义的常量已经初始化了,且不能修改
     var aa;
     let bb;
     // const cc; //报错,因为const定义的时候必须要初始化
     console.log(a); // 1
     console.log(b); // 2
     console.log(c); // 3
     console.log(aa); // undefined,因为没有初始化
     console.log(bb); // undefined,因为没有初始化
  }
  console.log(a); // 1,因为var声明的变量是可以跨块访问的
  console.log(b); // 报错,因为let定义的变量只能在块作用域里访问
  console.log(c); // 报错,因为let定义的变量只能在块作用域里访问
  
  //函数作用域
  (function A(){
      var d = 5;
      let e = 6;
      const f = 7;
      console.log(d); // 5
      console.log(e); // 6(在同一个{}中,也属于同一个块,可以正常访问到)
      console.log(f); // 7(在同一个{}中,也属于同一个块,可以正常访问到)
  })();
  // console.log(d); //报错,var定义的变量不可以跨函数访问
  // console.log(e); //报错,let定义的变量不可以跨函数访问
  // console.log(f); //报错,const定义的常量不可以跨函数访问

2、var存在变量提升,let和const不存在变量提升

“变量提升”即变量可以在声明之前使用,值为undefined.

//var的情况
console.log(foo); // 输出undefined
var foo = 2;

// let的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

// const的情况
console.log(MAX); // 报错ReferenceError
const MAX = 5;

let和const命令声明,不会发生变量提升,这表明在声明之前,bar和MAX时不存在的,这时如果用到它,就会抛出一个错误。

3、暂时性死区

只要块级作用域内存在let、const命令,它所声明的变量/常量就绑定(binding)这个区域,不在受外部的影响。

var tmp = 123;
if(true) {
   tmp = 'abc'; //报错ReferenceError
   let tmp;
}
if(true) {
   console.log(MAX); // 报错ReferenceError
   const MAX = 5;
}

虽然已经存在全局变量tmp,但是在if这个块作用域内let又声明了一个局部变量tmp,所以块里面的tmp = ‘abc’;中的tmp是指let声明的tmp,因为在let声明之前就调用了,所以会报错。const同理。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错,这在语法上称为“暂时性死区”(temporal dead zone,简称TDZ)。

有些“死区”比较隐蔽,不太容易发现。

function bar(x = y, y = 2){
    return [x,y];
}
bar(); //报错,因为将y赋值给x,但是此时y还没有声明,属于“死区”

function bar(x = 2, y = x){
    return [x,y];
}
bar(); // [2,2]

注:下面的代码也会报错,与var的行为不同。

var x = x; // 不报错
let x = x; // 报错ReferenceError:x is not defined

4、不允许重复声明

let和const不允许在相同作用域内重复声明。

// 报错
function func() {
   let a = 10;
   var a = 1;
}

// 报错
function func() {
   let a = 10;
   let a = 1;
}

function func() {
   var message = "Hello!";
   let age = 25;
   
   //以下两行都会报错
   const message = "Goodbye!";
   const age = 30;
}

因此不能在函数内部重新声明参数。

function func(arg) {
   let arg;
}
func(); // 报错

function func(arg) {
   {
      let arg;
   }
}
func(); // 不报错

不能用let和const完全取代var

1、从上面的解析中,我们可以看出他们的作用域和功能各有不同;
2、老版本的浏览器不支持let。不仅如此,而且有些最新的浏览器也还没有支持let。

经典面试题

var callbacks = [];
(function() {
    for(var i = 0; i < 5; i++) {
         callbacks.push(function() { return i; } )
    }
})();
console.log(callbacks.map(function(cb) { return cb(); } ))

这里存在一个“hoisting陷阱”,即声明提前陷阱。这里的i使用var声明的,所以变量声明会被提到函数顶部,变成

var callbacks = [];
(function() {
    var i;
    for(i = 0; i < 5; i++) {
         callbacks.push(function() { return i; } )
    }
})();
console.log(callbacks.map(function(cb) { return cb(); } ))

经过整个for循环,callbacks里面存储的5个函数指向的同一个变量i的值已经是5.所以最终打印出来的是[5, 5, 5, 5, 5], 那么如何使返回的结果是[0, 1, 2, 3, 4, 5]呢?

var callbacks = [];
(function() {
    for(let i = 0; i < 5; i++) {
         callbacks.push(function() { return i; } )
    }
})();
console.log(callbacks.map(function(cb) { return cb(); } ))

使用let声明变量i就可以了,因为let拥有块作用域,不会被提升到函数顶部,所以i每次循环都有独立的值。

参考
JS的块级作用域,var、let、const三者的区别
let和const命令
ES6之let能替代var吗?

猜你喜欢

转载自blog.csdn.net/undytk/article/details/88893695
今日推荐