ES5 只有全局作用域和函数作用域,没有块级作用域,在es6中let相当于添加了块级作用域;
let声明的变量只在当前代码块中有效。
const声明一个只读的常量。一旦声明,常量的值就不能改变。
let,const声明的变量不存在变量提升。
尝试修改const声明的变量会报错,这意味着const一旦声明变量,就必须立即初始化,不能留到以后赋值。
顶层对象属性
顶层对象
在es6中,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性。
全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
let [a, b, c] = [1, 2, 3];
不完全解构
,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
如果解构不成功,变量的值就等于undefined。
解构赋值允许指定默认值。只有当一个数组成员严格等于undefined,默认值才会生效。
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
解构赋值必须是最后一个参数,否则会报错。
解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
如果变量名与属性名不一致:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
js采用\uxxxx
形式表示一个字符,其中xxxx表示字符的 Unicode 码点。这种表示法只限于码点在\u0000~\uFFFF
之间的字符。超出这个范围的字符,要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}"
// "?"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true
ES6 为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for...of循环遍历。
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
模板字符串
``表示,变量用${ }
包裹起来
声明一个独一无二的值,不会与其他属性名产生冲突,symbol 值通过symbol函数生成。Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述
let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
可通过属性description
,直接返回 Symbol 的描述。
const sym = Symbol('foo');
sym.description // "foo"
Symbol 值作为对象属性名时,不能用点运算符
Symbol 作为属性名,该属性不会出现在for...in、for...of循环中
const mySymbol = Symbol();
const a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
//正确写法
let a = {};
a[mySymbol] = 'Hello!';
类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
Set 结构的键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的。
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true#
m.has(o) // false
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
需要注意的是扩展运算符对对象实例的拷贝属于一种浅拷贝。
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for…of循环(详见下文)。当使用for…of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。