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- 表示已存在的一个ArrayBufferbyteOffset- 可选,表示偏移量(默认为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, "niefengjun.cn");
wm2.set(o1, o2); // value可以是任意值,包括一个对象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象
wm1.get(o2); // "niefengjun.cn"
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("niefengjun.cn") == Symbol("niefengjun.cn")); //false
5.2 Symbol对象的使用
每次创建的Symbol值都是唯一的,利用这一特性,可以将其做为标识符使用。将其用于对象属性名时,可以保证对象每一个属性名都是唯一的,不会发生对象属性被覆盖的情况。
使用Symbol值做为对象属性时,不能使用.操作来添加属性:
var sym = Symbol();
var a = {};
a.sym = 'niefengjun.cn';
a[sym] // undefined
a['sym'] // "niefengjun.cn" 以点的形式添加属性名其本质上还是一个字符串
可以使用以下三种方式添加符号的对象属性:
var sym = Symbol();
// 1. 用方括号添回
var a = {};
a[sym] = 'niefengjun.cn';
// 2. 在对象内部定义
var a = {
[sym]: 'niefengjun.cn'
};
// 3. 用defineProperty添加
var a = {};
Object.defineProperty(a, sym, { value: 'niefengjun.cn' });
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对象
