阅读 121

ES6 let和const

1. let

ES6新增的let关键字,作用类似于var,但是let声明的变量只在let所在代码块中有效

{
  let m = 100;
  var n = 10;
}

m // ReferenceError: a is not defined.
n // 10
复制代码

在声明循环index变量时,很适合使用let使变量i只能在循环体中使用,如果使用var, 变量i会泄漏到外部范围

for (let i = 0; i < 10; i++) {
    //todo
}
复制代码

下面的代码如果使用var,最后输出的是10。

var arr = [];
for (var i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  };
}
arr[6](); // 10
复制代码

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是6。 下面代码中,变量i是let声明的,每一轮循环其实都是一个新的块作用域,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。

var arr = [];
for (let i = 0; i < 10; i++) {
  arr[i] = function () {
    console.log(i);
  };
}
arr[6](); // 6
复制代码

var声明的变量可以使用写在声明前面, 但是let不允许

console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError

var foo = 2;
let bar = 2;
复制代码

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

// 报错
function () {
  let foo = 10;
  let foo = 1;
}
function fn(foo) {
  let foo; // 报错
}
复制代码

let为javascript新增了块级作用域,可直接执行

{{{{let greetting = 'Hello World'}}}};
复制代码

在javascript中常常有一种应用场景:直接执行一个匿名的过程,普遍的实现是给一个匿名函数传参实现

(function () {
  var temp = ...;
  ...
}());
复制代码

使用块作用域,可以更简洁地实现

{
  let temp = ...;
  ...
}
复制代码

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

{
  let foo = fn();
  foo = foo * foo;
}
复制代码

现在有一个提案,还没有通过,在块级作用域之前加上do,使它能返回值(do{}while()的do???)。

let a = do {
  let foo = fn();
  foo = foo * foo;
};
复制代码

ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域如if, for, try等中声明,但是浏览器没有选择遵循这个规定, 所以下面代码不会报错,但是在strict模式下依然会报错。

ES6明确规定,函数可以在块级作用域声明,类似于let声明的变量,该函数在块作用域外不可访问

if (true) {
  function fn() {}
}
复制代码

2. const

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

if (true) {
  const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined

复制代码

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.
复制代码

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const foo;
// SyntaxError: Missing initializer in const declaration
复制代码

对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。

const foo = {};
foo.prop = 123;

foo.prop
// 123

foo = {}; // TypeError: "foo" is read-only
复制代码

const 声明的复合类型地址保证不变,但是复合类型的值是可以增删改的,如果真的不允许修改该复合类型,应该使用Object.freeze()

const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
复制代码

3. 顶层对象的属性

顶层对象,在浏览器环境中是指window, 在node指的是global, ES5的顶层对象属性和全局变量是等价的,这是Javascript设计的很大一个不足, 增加了程序中错误的产生,也增加了发现问题的难度, 而且window对象有实体含义指窗体本身,顶层对象设计为有实体含义的对象,也是不合理的。

window.x = 1;
x // 1

x = 2;
window.x // 2
复制代码

ES6为了修正这个问题,一是为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;二是规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。

var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1

let b = 1;
window.b // undefined
复制代码

4. 顶层对象

ES5的顶层对象,本身也有个问题,在各个Javascript环境里是不一样的

浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。

浏览器和 Web Worker 里面,self也指向顶层对象,但是Node没有self。

Node 里面,顶层对象是global,但其他环境都不支持。

现在有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。

文章分类
阅读