ES6笔记之let声明和const

博主为什么要在繁忙的业务中抽时间写这些最基本的笔记,就是想不断积累自己小的细节,让自己的开发更有效率,不能知其然不知所以然。

博主在实际开发中使用了react全家桶(框架)+typescript(预编译JS)+ES6(JS语法)+webpack(打包工具)+styledcomponent(css),并使用node模拟后台数据。

因为自己不想成为写业务 的机器, 而是致力成为 技术 人员。

ES6分类博文笔记摘自 http://es6.ruanyifeng.com/#docs/let  

博主在这里只写最核心和自己所需要的。

let 命令

let命令 用来声明变量 ,与var的区别就是let所声明 的变量 作用域只在let 命令所在的代码块{}有效,且需要先声明后使用,如果直接使用会报错

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
 a[i]();//这里依次打印0到9 }
a[i]()//这里i从0到9都会打印10

变量ivar声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,

而循环内被赋给数组afunction在运行时,会通过闭包读到这同一个变量i,(其实我这里也不是特别理解,得重新补习一下闭包)导致最后输出的是最后一轮的i的值,也就是10。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6  

变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。 

另一个var和let的区别

var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。(webstorm会自动检测,而不是在编译后检测报错,推荐使用)

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

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

上面代码中,变量foovar命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量barlet命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

暂时性死区

这里原博主说了很多,但是简而言之就是如果在一个代码块中在let申明一个变量前使用了当前申请的变量,及时该变量已在外部声明,即会抛出错误。

那有人会问了,既然抛出错误让代码不能执行,我平时用js都不会有这样的问题,那我用let的意义在哪里呢?

为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。

// 不报错
var x = x;

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

上面代码报错,也是因为暂时性死区。使用let声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。

ES6 规定暂时性死区和letconst语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

不允许重复声明

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

块级作用域

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

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

var tmp = new Date();

function f() {
  console.log(tmp);//由于下面变量提升,即会输出undefined
  if (false) {
    var tmp = 'hello world';//没有块级作用域的限制,这里声明的tmp在ES5中会提升至函数作用域。虽然不会执行里面的赋值,但是会变量提升
  }
}

f(); // undefined

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

var s = 'hello';

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

console.log(i); // 5

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

 

ES6 的块级作用域

let实际上为 JavaScript 新增了块级作用域。

ES6 允许块级作用域的任意嵌套。

块级作用域与函数声明(不理解也没关系,本篇主要讲解let)

if (true) {
  function f() {}
}

// 情况二
try {
  function f() {}
} catch(e) {
  // ...
}

上面两种函数声明,根据 ES5 的规定都是非法的。

但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());

上面代码在 ES5 中运行,会得到“I am inside!”,因为在if内声明的函数f会被提升到函数头部,实际运行的代码如下。

// ES5 环境
function f() { console.log('I am outside!'); }

(function () {
  function f() { console.log('I am inside!'); }
  if (false) {
  }
  f();
}());

而在ES6中,由于f()未声明,所以会报错

原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式

  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部

注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

const 

const也可以用来申明变量 但是声明的是常量。一旦声明,常量的值就不能改变。

当我们尝试去改变用const声明的常量时,浏览器就会报错。const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:

posted @ 2017-06-06 11:04  H5工程师  阅读(1369)  评论(0编辑  收藏  举报