ES6 语言标准中扩展很多新对象,如:将用于异步处理的Promise规范纳入语言标准,做为原生对象提供;增加了Map
和Set
对象及其weak
版本;Symbol
对象可以用来创建独一无二的标识符,还可以访问 ES5 中没有暴露给开发者的符号。
1. 类型数组
类型数组(Typed Arrays)用于赋予了JavaScript处理二进制数据的能力,类型数组并不是单一的对象,而是对一系列对象的统称。二进制数组由以下三类对象组成:
ArrayBuffer
是一个是一个用于表示二进制缓冲区(buffer)的对象,它并没有能力去访问和操作它自身的数据内容,要操作ArrayBuffer
中的数据,需要创建一个Typed Array View
或DataView
来读写缓存区内容。Typed Array View
(Typed Array Object)即类型数组视图对象,是一组具有描述性的名字的不同类型的数组视图对象。如:Uint8Array
、Int8Array
、Int32Array
、Float32Array
等DataView
提供了一些底层的API,通过这些API可以从ArrayBuffer
中读/写数据。
1.1 ArrayBuffer
对象
ArrayBuffer
(缓冲数组)是一段二进制的内存空间,它用于呈现通用、固定长度的二进制数据的类型。它不能直接构造并填充其内容,而应该先创建一个DataView
或是通过TypedArray
来读写具体类型的二进制数组内容。
如,创建一个ArrayBuffer
并使用Typed Array View
或DataView
读写数据:
var buffer = new ArrayBuffer(8); // 使用 Typed Array View 读写 var int32 = new Int32Array(buffer); int32[0] = 42; console.log(int32[0]); // 42 // 使用 DataView 读写 var dv = new DataView(buffer, 0); dv.setInt16(1, 42); dv.getInt16(1);
Typed Array View
也可以独立使用:
// 创建一个指定长度的 TypedArray var uint8 = new Uint8Array(2); uint8[0] = 42; console.log(uint8[0]); // 42
1.2 TypedArray
视图对象
TypedArray
对象是一个描述二进制缓存数据(ArrayBuffer)的类似数组的视图对象。TypedArray
并不是一个单独的对象,而是由一组子对象构成,我们可以根据缓存区内容类型的不同而建立不同类型的数据视图,每种类型的视图都可以从不同的索引位置开始为缓存建立内容视图。
我们可以像下面这样构造一个TypedArray
对象:
new TypedArray(length); new TypedArray(typedArray); new TypedArray(object); new TypedArray(buffer [, byteOffset [, length]]);
构造参数说明:
length
- 表示创建一个长度为length
的类型数组typedArray
- 从类型数组typedArray
创建一个新的类型数组,原类型数组中的每个值都会被转化为和构造器到对应的新数组里。object
- 当传入一个object
时,相当于使用TypedArray.from()
方法创建一个新的类型数组。buffer [, byteOffset [, length]]
- 从一个指定的buffer
和可选参数byteOffset
、length
创建一个类型数组
TypedArray
可以是以下对象的构造的函数之一:
Int8Array
:8位有符号整数,长度1个字节。Uint8Array
:8位无符号整数,长度1个字节。Uint8ClampedArray
:8位无符号整数,长度1个字节,溢出处理不同。Int16Array
:16位有符号整数,长度2字节。Uint16Array
:16位无符号整数,长度2字节。Int32Array
:32位有符号整数,长度4字节。Uint32Array
:32位无符号整数,长度4字节。Float32Array
:32位浮点数,长度4字节。Float64Array
:64位浮点数,长度8字节。
1.3 DataView
对象
DataView
提供了一个用于读写ArrayBuffer
的,更加底层的访问接口。通过该对象提供的接口,我们可以读写多个ArrayBuffer
,而且这些访问接口是与平台无关的格式类型。
可以像下面这样创建DataView
对象:
new DataView(buffer [, byteOffset [, byteLength]])
其中:
buffer
- 表示已存在的一个ArrayBuffer
byteOffset
- 可选,表示偏移量(默认为0)byteLength
- 可选,表示读取数据的长度,默认为缓存的数据总数
var buffer = new ArrayBuffer(16); var dv = new DataView(buffer, 0); dv.setInt16(1, 42); dv.getInt16(1); //42
2. Map
对象
Map
对象是一个简单的键/值映射。其键和值都可以是任意值(对象或原始值)。
我们可以像下面这样创建一个Map
对象:
new Map([iterable])
-
iterable
- 可选参数,一个可迭代对象,是一个包含 键-值 对的数组,数组中所有的键-值对集合对会被添加到新的Map
集合中。
迭代Map
对象中的元素时,for…of循环会返回每一个迭代对象的[key, value]
数组。
2.1 Object
与Map
的比较
Object
和Map
都是按键
存取值
的结构类型,二者有相似之处。但,二者也有以下几点区别:
-
对象通常都有自己的原型
,也就是说一个对象总有一个"prototype"
键。不过现在你可以使用,map = Object.create(null)
来创建一个没有原型的对象。 -
一个对象的键只能是String和Symbol,而
Map
的键可以是任意值。 -
Map
的键值对个数很容易获取,而获取一个Object
键值对个数确相对复杂。
Map
的键可以是任意值,我们可以像下面这样使用Map
对象:
var myMap = new Map(); var keyString = "a string", keyObj = {}, keyFunc = function () {}; // 设置各种类型的key myMap.set(keyString, "String类型key的值"); myMap.set(keyObj, "Object类型key的值"); myMap.set(keyFunc, "Function类型key的值"); myMap.size; // 3 // 获取值 myMap.get(keyString); myMap.get(keyObj); myMap.get(keyFunc); myMap.get("a string"); // "String类型key的值" // 因为 keyString === 'a string' myMap.get({}); // undefined, 因为 keyObj !== {} myMap.get(function() {}) // undefined, 因为 keyFunc !== function () {}
Map
对象可以使用for…of语句进行迭代:
var myMap = new Map(); myMap.set(0, "zero"); myMap.set(1, "one"); for (var [key, value] of myMap) { console.log(key + " = " + value); } // 0 = zero // 1 = one for (var key of myMap.keys()) { console.log(key); } // 0 // 1 for (var value of myMap.values()) { console.log(value); } // zero // one for (var [key, value] of myMap.entries()) { console.log(key + " = " + value); } // 0 = zero // 1 = one
2.2 WeakMap
WeakMap
是一种键/值对的集合类型,与Map
不同,其键
只能是对象类型,而值
可以是任意类型。WeakMap
的键是“弱引用”的,这意味着,如果没有其它引用和该键引用同一个对象,该对象将会被当作垃圾回收。
可以像下面这样创建一个WeakMap
对象:
new WeakMap([iterable])
其中:
Iterable
是一个2元数组或者可遍历的且其元素是键/值对的对象。每个键/值对会被添加新的WeakMap
中。
WeakMap
的一些使用示例:
var wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); var o1 = {}, o2 = function(){}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "itbilu.com"); wm2.set(o1, o2); // value可以是任意值,包括一个对象 wm2.set(o3, undefined); wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象 wm1.get(o2); // "itbilu.com" wm2.get(o2); // undefined,wm2中没有o2这个键 wm2.get(o3); // undefined,值就是undefined wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (即使值是undefined) wm3.set(o1, 37); wm3.get(o1); // 37 wm3.clear(); wm3.get(o1); // undefined,wm3已被清空 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false
3. Set
对象
3.1 Set
的构造
Set
也是 ES6 中新增的一集合类型,该类型类似于数组,但要求集合中的成员值都是唯一的,其存储的值可以是任意类型,可以是原始值或对象引用。
可以像下面这样构造一个Set
对象:
new Set([iterable]);
Set
对象的使用:
var mySet = new Set(); mySet.add(1); mySet.add(5); mySet.add("ITbilu.com"); var o = {a: 1, b: 2}; mySet.add(o); mySet.has(1); // true mySet.has(3); // false, 3没有添加到集合中 mySet.has(5); // true mySet.has(Math.sqrt(25)); // true mySet.has("ITbilu.com".toLowerCase()); // true mySet.has(o); // true mySet.size; // 4 mySet.delete(5); mySet.has(5); // false, 5 已经被移除 mySet.size; // 3
3.2 WeakSet
Set
类型同样存在一个Weak
版本,WeakSet
对象是一个无序集合,可以用它来存储任意的对象值,并且对这些对象值保持弱引用关系。
WeakSet
与Set
类型一样,同样用于存储对象值的,并且其中的每个对象值都只能出现一次。但两者也有以下两点不同:
WeakSet
对象中只能存放对象值, 不能存放原始值,而Set
对象都可以。WeakSet
对象中存储的对象值都是被弱引用的,如果没有其他的变量或属性引用这个对象值,这个对象值会被当成垃圾回收。也因此,WeakSet
对象是无法被枚举的,也就没有办法拿到它包含的所有元素。
WeakSet
对象的使用:
var ws = new WeakSet(); var obj = {}; var foo = {}; ws.add(window); ws.add(obj); ws.has(window); // true ws.has(foo); // false, 对象 foo 并没有被添加进 ws 中 ws.delete(window); // 从集合中删除 window 对象 ws.has(window); // false, window 对象已经被删除了 ws.clear(); // 清空整个 WeakSet 对象
4. Promise
对象
4.1 Promise/A+
规范
ES6 中新增的用于异步处理的Promise
对象,该对象来源于Promise/A+
规范。在Promise/A+
规范中规定:
-
一个promise有一到三种状态:
pending
(等待)、fulfilled或resolved
(成功-已完成)、rejected
(失败-已拒绝) -
一个promise的状态只可能从
等待
到完成
或者拒绝
状态,不能逆向转换,同时完成
和拒绝
状态不能相互转换 -
promise
实例必须实现then
方法,而且then
必须返回一个promise
,同一个promise
的then
可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致 -
then
方法接受两个参数:第一个参数是成功时的回调,在promise
由等待
养成转换到完成
态时调用。另一个是失败时的回调,在promise
由等待
状态转换到拒绝
状态时调用。同时,then
可以接受另一个promise
传入,也接受一个类似then
的对象或方法,即:thenable
对象
4.2 Promise
对象
ES6 将Promise
对象纳入语言标准,并做为语言标准的原生对象提供。在 ES6 语言标准中,Promise
包括三部分:构造函数、实例方法、静态方法。
构造函数
构造函数用于创建一个Promise
,结构如下:
new Promise( /* executor */ function(resolve, reject) { ... } );
创建Promise
对象时,需要向构造函数传入一个executor
参数,它是一个执行函数,会在创建Promise
对象的时候立即执行。这个函数通常被用来执行一些异步操作,包含以下两个参数:
resolve
- 执行成功时的回调函数,操作完成以后通过这个函数来触发promise
的成功状态reject
- 执行失败时的回调函数,操作完成以后通过这个函数来触发promise
的失败状态
var promise = new Promise(function(resolve, reject) { // 异步处理回调函数 // 处理结束后调用resolve 或 reject方法 if (操作成功){ resolve(value); } else { reject(error); } });
4.3 Promise
对象方法
对象方法即静态方法,也就是可以通过Promise
调用的方法。Promise
包含以下对象方法:
Promise.all(iterable)
- 该方法会返回一个新的promise
对象。iterable
是一个可以包含多个promise
对象的可迭代对象,iterable
对象中的所有promise
对象执行成功后会触发成功状态;而其中的任何一个对象执行失败后,会触发失败状态,并将第一个触发失败的promise
对象的错误信息作为它的失败错误信息。Promise.all()
方法通常用于处理多个promise
对象集合。Promise.race(iterable)
- 当iterable
中任意一个子promise
执行成功或失败后,父promise
也会返回该promise
对象的成功或失败信息,并调用父promise
相应的处理函数。Promise.reject(reason)
- 调用父promise
对象失败状态的处理函数,并返回指定失败原因reason
。Promise.resolve(value)
- 调用父promise
对象成功状态的处理函数,并返回指定值value
。
如,Promise.all
的使用:
var p1 = Promise.resolve(1), p2 = Promise.resolve(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { console.log(results); // [1, 2, 3] });
4.4 Promise
实例方法
创建Promise
实例后,可以通过实例方法为实例添加在resolve(成功) / reject(失败)时调用的回调函数:
Promise.prototype.then(onFulfilled, onRejected)
-.then
方法可以用于设置resolve(成功) / reject(失败)两种状态时的回调函数,两处回调方法都是可选的。其中:-
resolve(成功)时,
onFulfilled
方法会被调用 -
reject(失败)时,
onRejected
方法会被调用
-
resolve(成功)时,
Promise.prototype.catch(onRejected)
- 该方法用于设置reject(失败)时被调用的回调函数,其相当于promise.then(undefined, onRejected)
。
如,使用promise.then
定义成功和失败两种回调:
promise.then(function(result) { // 操作成功 }, function(err) { // 操作失败 });
使用promise.then
和promise.catch
分别定义:
promise.then(function(result) { // 操作成功 }).catch(function(err) { // 操作失败 });
更多关于Promise
的参考:
- Promise简介
- Promise对象的使用
- 使用ECMAScript 6 的Promise对象实现JavaScript深度嵌套回调的顺序链式调用
- bluebird与原生Promise对象及bluebird模块的中文API文档
5. Symbol
类型
Symbol
(符号)是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用。在ES6 之前,对象中的方法和属性的键都是字符串形式,如果有同名的方法或属性就会被覆盖。使用Symbol
可以解决这一问题,Symbol
创建的标识符都是独一无二的。
可以像下面这样,使用Symbol()
函数来创建一个符号:
Symbol([description])
号对象是一个对的符号原始数据类型的隐式对象包装器。其参数description
是一个可选的字符串描述,这个描述值一般用于调试而不是访问符号本身。
5.1 关于Symbol
类型
符号对象
是JavaScript中继:Boolean
、Null
、Undefined
、String
、Number
、Object
之后,第七种数据类型。创建一个符号类型使用Symbol()
函数:
var sym = Symbol(); console.log(typeof sym); //symbol
创建一个新的原始符号,可以使用Symbol()
和一个可选字符串参数作为它的描述:
var sym1 = Symbol(); var sym2 = Symbol("foo"); var sym3 = Symbol("foo");
上面的代码创建三个新的符号。注意,符号类型的描述'foo'
唯一的作用就是在调试时,我们可以更好的区分所创始的符号。即使描述值一样,也不是同一个符号,符号类型每次都会创建一个新的符号:
console.log(Symbol("foo") === Symbol("foo")); //false console.log(Symbol("itbilu.com") == Symbol("itbilu.com")); //false
5.2 Symbol
对象的使用
每次创建的Symbol
值都是唯一的,利用这一特性,可以将其做为标识符使用。将其用于对象属性名时,可以保证对象每一个属性名都是唯一的,不会发生对象属性被覆盖的情况。
使用Symbol
值做为对象属性时,不能使用.
操作来添加属性:
var sym = Symbol(); var a = {}; a.sym = 'itbilu.com'; a[sym] // undefined a['sym'] // "itbilu.com" 以点的形式添加属性名其本质上还是一个字符串
可以使用以下三种方式添加符号的对象属性:
var sym = Symbol(); // 1. 用方括号添回 var a = {}; a[sym] = 'itbilu.com'; // 2. 在对象内部定义 var a = { [sym]: 'itbilu.com' }; // 3. 用defineProperty添加 var a = {}; Object.defineProperty(a, sym, { value: 'itbilu.com' });
6. Proxy
对象
Proxy
(代理)对象用于自定义JavaScript语言的形为(如:属性查找、赋值、枚举、函数调用等),通过该对象使我们有了对JavaScript语言层面修改的能力。
创建一个Proxy
语法如下:
var p = new Proxy(target, handler);
其中:
target
-被代理的一个目标对象(可以是任何类型的对象、数组、函数,甚至是另一个代理)handler
-处理对象,其属性是定义代理时的行为函数,在目标对象上执行的代理操作都会被些函数处理返回值
-代理对象实例
Proxy
即“代理”,我们通过new Proxy()
构造函数,来对target
设置处理对象handler
。初始化后会返回一个代理实例对象p
,该对象是target
的代理器,访问target
对象的形为都会首先经过该代理器处理。代理器会判断这种形为有没有在handler
中定义,如果已定义则由代理形为处理,如果未定义则由target
处理。
6.1 术语
在使用Proxy
时,应该理解以下术语:
traps
-提供访问的属性,与操作系统中的trap
概念类似target
-被代理虚拟化的对象,这个对象常常用作代理的存储后端。handler
-包含traps
的占位符对象
6.2 Proxy
的使用
可以通过Proxy
重写对象的get
访问器,当对象属性不存在时返回33
:
var handler = { get: function(target, name){ return name in target? target[name]:33; } }; var p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 33
还可以定义一个 JavaScript 对象,代理会将所有应用到它的操作转发到这个对象上:
var target = {}; var p = new Proxy(target, {}); p.a = 37; // 被转发到代理的操作 console.log(target.a); // 37. 操作已经被正确地转发
7. Reflect
对象
Reflect
对象通常与Proxy
对象一块使用,Proxy
通过一些陷入指令来修改对象的默认形为;而Reflect
提供了一些静态方法,这些方法与Proxy
对象处理器中的方法一一对应,用于获取对象的默认形为、操作对象属性等。
Reflect
对象提供了 14 个静态方法,它们的名字与Proxy
听代理方法handler
中的名字相同,其中有几个方法在Object
对象上也存在同名方法,虽然它们功能类似,但也存在细微差异。
Reflect
对象的详细介绍请参考:JavaScript ES6 Reflect对象