美文网首页
ES6 let 和 const

ES6 let 和 const

作者: _ClariS_ | 来源:发表于2019-11-14 23:01 被阅读0次

一、声明全局变量

a = 1一定是在声明一个全局变量吗?

function fn() {
  a = 1
}
fn()
console.log(a) // 1

上面的代码的确是在声明一个全局变量a,然后我们再来看看下面这种情况

var a
function fn() {
  var a
  function fn2() {
    a = 1
  }
}
fn()
console.log(a) // undefined

在这种情况下很明显就不是在声明全局变量aa = 1会向上寻找离它最近的一个声明,即函数fn里的var a,并没有到达最顶层,因此上面的代码实际上等价于下面这种写法

var a
function fn() {
  var a = 1
  function fn2() {
  }
}
fn()
console.log(a) // undefined

总结一下就是,如果在当前作用域内存在变量a的声明,那么a = 1就会直接去利用这个声明;如果在当前作用域内不存在对a的声明,那么就会按照“作用域链”一级一级地向上寻找所有父对象的变量。只有当找完了所有父对象的变量之后都没有发现有对变量a的声明,这时候才是在声明一个全局变量a(window.a)

二、ES5 作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。(可以使用立即执行函数来解决这个问题)

由于以上var的种种缺陷,因此就有了下文的letconst以及“块级作用域”

三、let 命令

1. 块级作用域

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量n,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是 10。

2. 暂时性死区

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

3. 不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

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

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

四、const 命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。而且必须在声明的时候立刻进行赋值。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

const foo;
// SyntaxError: Missing initializer in const declaration

五、面试题

  1. 下面代码的打印结果一定是1吗?
var a = 1

function fn() {
  console.log(a)
}
/*这里有一行代码*/
fn()

如果加的一行代码是a = 2呢?

var a = 1

function fn() {
  console.log(a) // 2
}
a = 2
fn()
  1. 下面的代码为什么打印出i的值总是6?
for (var i=0; i<6; i++){
  
}
console.log(i) // 6

因为它等价于下面这种写法

var i
for (i=0; i<6; i++){
  
}
console.log(i) // 6
  1. 下面的代码一定会打印出0~5吗?
for (var i=0; i<6; i++){
  function fn(){
    console.log(i) // ?
  }
  /*这里有一行代码*/
}
console.log(i) // 6

如果是直接调用fn,会打印出1~5,我的理解是每进入for循环中一次,就执行一次fn,因此会打印出对应的i的值

for (var i=0; i<6; i++){
  function fn(){
    console.log(i) // 0,1,2,3,4,5
  }
  fn()
}
console.log(i) // 6

但如果加的代码是x.onclick = fn,则直接就打印出6,我的理解是当点击这个按钮的时候,i已经累加到6了

/*<button id="x">x</button>*/
for (var i=0; i<6; i++){
  function fn(){
    console.log(i) // 6
  }
  x.onclick = fn
}
console.log(i) // 6
  1. 一道面试题
<ul>
  <li>导航1</li>
  <li>导航2</li>
  <li>导航3</li>
  <li>导航4</li>
  <li>导航5</li>
  <li>导航6</li>
</ul>
var liTags = document.querySelectorAll('li')

for (var i = 0; i < liTags.length; i++) {
  liTags[i].onclick = function() {
    console.log(i) // 为什么打印出i的值总是6?
  }
}

这就是因为用来计数的循环变量i泄露为全局变量

有以下三种解法:

解法一:用ES5的立即执行函数来创造一个局部作用域

var liTags = document.querySelectorAll('li')

for (var i = 0; i < liTags.length; i++) {
  (function(j) {
    liTags[j].onclick = function() {
      console.log(j) // 0,1,2,3,4,5
    }
  })(i)
}

解法二:在for循环内部用另一个变量来接收i的值

var liTags = document.querySelectorAll('li')

for (var i = 0; i < liTags.length; i++) {
  let j = i // 注意这里依然只能用let,不能用var
  liTags[j].onclick = function() {
    console.log(j) // 0,1,2,3,4,5
  }
}

解法三:直接在for循环的条件中使用let来声明变量

var liTags = document.querySelectorAll('li')

for (let i = 0; i < liTags.length; i++) {
  liTags[i].onclick = function() {
    console.log(i) // 0,1,2,3,4,5
  }
}

注意:上面解法三的这种写法,i的作用域只在for后面的圆括号中,而下面的花括号又是另外一个作用域,可以理解为下面这样

在这个过程中一共产生了7个i,块里面6个,圆括号里面1个

参考:
http://es6.ruanyifeng.com/#docs/let
我用了两个月的时间才理解 let

相关文章

  • (JS)

    ES6 let、const和var的区别 let和const声明变量不存在变量提升 let和const不能重复声明...

  • ES6(let 与 const)

    ES6中引入了let 和 const来声明变量,我们来看下let 和 const 的使用 一、let let定义的...

  • es6总结一

    es6基础 let和const命令 let和const用于声明变量let跟var的区别 1、let定义过的变量不能...

  • 总结一下前端常见javascript基础面试题:

    1.var ,let 和const的区别: let和const是es6新增的语法, let和var都可以定义变量,...

  • ES6之 let 和 const 命令

    ES6 之 let 和 const 命令 1、let 命令 1.1、基本用法 ES6 新增了let命令,用来声明变...

  • TS中 var 和 let 的区别

    let 和 const 是 ES6 中新增的声明对象的类型。与 var 相比,let 和 const 不存在...

  • es6常用语法

    const 和 let 在es6语法规范中,尽量使用let替代var,如果在const 和let中选,优先使用co...

  • es6解读1: let, const 和 smybol

    let const 作用域 作用域概念 如何使用let和const 使用let 形成块级作用域 es6下强制开启...

  • es6解读1: let,const和symbol

    let 和 const 作用域的概念 如何使用let和const 使用let1、形成块级作用域2、es6下强制开启...

  • es6

    [TOC] 参考阮一峰的ECMAScript 6 入门参考深入浅出ES6 let和const let和const都...

网友评论

      本文标题:ES6 let 和 const

      本文链接:https://www.haomeiwen.com/subject/euprictx.html