ES6小计(3)

10. ProxyReflect

10.1 Proxy

Proxy用于修改目标对象的某些默认行为,在语言层做出改变。Proxy可理解为对目标对象外做一层拦截。了解即可。

let target = {}
let proxy = new Proxy(target, {
    get: function(target, property) {
        return 35
    },
    set: function(target, key, value, receiver) {
        return Reflect.set(target, key, value, receiver)
    }
})

proxy.time // 35
proxy.name // 35

构造函数new Proxy(target, handler)接收两个参数,target目标对象,handler配置操作对象,用于定义代理的动作。要是Proxy有用,使用时需要针对Proxy实例进行操作,而不是target对象。
Proxy支持的拦截操作包含:getsethasdeletePropertyenumeratehasOwnownKeysObject.keys()getOwnPropertyDescriptiordefinPropertypreventExtensionsgetPrototypeOfisExtensiblesetPrototypeOfapplyconstruct

10.2 Reflect

Reflect对象有同Proxy代理的相同的方法,Reflect对象用于获取被代理的对象的默认行为,故一般Proxy代理内,均会用Reflex执行默认行为。

11. 二进制数组

二进制数组由3类对象组成:ArrayBuffer对象、TypedArray视图、DataView视图。
TypedArray视图支持9种数据类型:

|数据类型|字节长度|含义 |对应C语言类型 |
|Int8 |1 |8位带符号整数 |signed char |
|Uint8 |1 |8位无符号整数 |unsigned char |
|Uint8C |1 |8位无符号整数 |unsigned char |
|Int16 |2 |16位带符号整数 |short |
|Uint16 |2 |16位无符号整数 |unsigned char |
|Int32 |4 |32位带符号整数 |int |
|Uint32 |4 |32位无符号整数 |unsigned int |
|Float32 |4 |32位浮点数 |float |
|Float64 |8 |64位浮点数 |double |

11.1 ArrayBuffer对象

ArrayBuffer对象代表存储二进制数据的一段内存,不能直接读写,只能通过视图读写。创建ArrayBuffer对象用new ArrayBuffer(length)构造函数创建,lengt代表分配的内存字节空间长度。

let buffer = new ArrayBuffer(12)
let x1 = new Int32Array(buffer)
x1[0] = 1
let x2 = new Uint8Array(buffer) // x1,x2均是代表同一段二进制空间,只是表示的视图不同,故内存空间变化后,两个都变。
x2[0] = 2
x1[0] // 2

node的Buffer对象其实就是Uint8Array视图对象的另一种封装。在node里,使用Buffer对象更为方便。

TypedArray视图除了接收ArrayType作为实例外,也可以直接接收普通数组作为参数,生成底层ArrayBuffer实例,完成对内存的赋值。

let typedArray = new Uint8Array([0, 1, 2])
typedArray.length // 3
typedArray[0] = 5
typedArray // [5, 1, 2]

ArrayBuffer.prototype.byteLength,ArrayBuffer实例的byteLength属性返回所分配内存空间的字节长度。注意若ArrayBuffer分配的内存空间很大,有可能分配失败,故可以用byteLength检查内存分配是否如逾期,是否成功。

ArrayBuffer.prototype.slice(startIndex, endIndex),ArrayBuffer实例所在的内存空间的复制,返回一个新的ArrayBuffer实例,新的内存空间。
类似于String.prototype.slice()。除slice方法外,ArrayBuffer对象不提供任何其他直接读写内存的方法,只能通过视图对象。

ArrayBuffer.isView(),判断传入对象是否是一个视图对象。是,返回true;不是,返回false

11.2 TypedArray视图

TypedArray视图的数组成员为同一数据类型。目前TypedArray视图一共9种,每种均是一种构造函数。
Int8Array:8位有符号整数,1个字节长度;
Uint8Array:8位无符号整数,1个字节长度;
Uint8ClampedArray:8位无符号整数,1个字节长度,溢出处理不同;
Int16Array:16位有符号整数,2个字节长度;
Uint8Array:16位无符号整数,2个字节长度;
Int32Array:32位有符号整数,4个字节长度;
Uint32Array:32位无符号整数,4个字节长度;
Float32Array:32位浮点数,4个字节长度;
Float32Array:32位浮点数,4个字节长度;

用这9个构造函数生成的数组,统称为TypedArray视图。该数组成员类型唯一,连续,不会有空位,默认值为0,数组存储的是指向buffer内存的指针。

字节序:二进制数据低位在前为小端字节序,高位在前为大端字节序。这直接影响读数。TypedArray只能处理小端字节序。大端字节序需要用DataArray视图去定义处理。

11.3 DataView视图

DataView视图可以根据读取内容的方式不同而获取不同数据展示方式。

var buffer = new ArrayBuffer(24)
var dv = new DataView(buffer)
var v1 = dv.getUint16(1, true) // 从第二个字节读取一个16位无符号整数,小端字节序
var v2 = dv.getUint16(3, false) // 从第四个字节读取一个16位无符号整数,大端字节序
var v2 = dv.getUint16(3) // 从第四个字节读取一个16位无符号整数,大端字节序
dv.setInt32(0, 25, false) // 在第一个字节,以大端字节序写入值为25的32位整数

DataView实例读取内存方法:
getInt8, getUint8, getInt16, getUint16, getInt32, getUint32, getFloat32, getFloat64

DataView实例写内存的方法:
setInt8, setUint8, setInt16, setUint16, setInt32, setUint32, setFloat32, setFloat64

12. Set和Map数据结构

12.1 Set

Set是一个类似于数组的解构,其实可以理解为一个具有Iterator接口的对象。Set成员值是唯一的,没有重复。

var s = new Set()
[2, 3, 4, 5, 4, 2, 2].map(x => s.add(x))
for (i of s) { console.log(i) } // 2 3 4 5

var set = new Set([1, 2, 3, 4, 4])
[...set] // [1, 2, 3, 4]
set.size // 4
Array.from(set) // [1, 2, 3, 4]

向Set对象添加成员时,不会发生类型转换,但NaN会判断为相等。Set对象方法如下:
add(value) 添加成员,返回成员总数。
delete(value) 删除某个成员,返回boolean值,是否删除成功。
has(value) 返回boolean,是否为Set成员。
clear() 清除所有成员,无返回值。

Set对象的遍历方法:
keys() 返回一个键名的遍历器。
values() 返回一个键值的遍历器。
entries() 返回一个键值对的遍历器,意义不大。
forEach() 使用回调遍历每个成员。
因Set解构的键名和键值相同,故keys()values()返回值一样。entries()每次输出一个数组[key, value],key和value值一样。
注意使用数组与Set对象的相互转换,可以让Set解构使用数组的各种方法。

let set =  new Set(['red', 'green', 'blue'])
for (let item of set.keys()) { console.log(item) }
for (let item of set.values()) { console.log(item) }
for (let item of set) { console.log(item) }
// 上诉3种方法结果一致

用Set解构可以很容易实现并集、交集、差集。

let a = new Set([1, 2, 3])
let b = new Set([4, 2, 3])

let union = new Set([...a, ...b]) // 并集
let intersect = new Set([...a].filter(x => b.has(x))) // 交集
let difference = new Set([...a].filter(x => !b.has(x))) // 差集,a-b

12.2 WeakSet

WeakSet解构也是不重复的值的结合,不过值只能为对象。内部对象的引用为若引用,垃圾回收机制不会计算WeakSet对成员对象的应用。不能遍历。没有size属性。感觉意义不是很大。

12.3 Map结构

js中对象的键名只能为字符串或者Symbol(es6新增),Map结构与普通对象类似,但是可以以对象作为键名。

var m = new Map()
var o = {p: 'hello'}
m.set(o, 'content')
m.get(o) // content
m.has(o) // true
m.delete(o) // true
m.has(o) // false

Map()构造函数也可以接收一个数组,数组成员是键名和键值的数组。

var map = new Map([['name', '张三'], ['title', 'author']])
map.size // 2
map.has('name') // true
map.get('name') // 张三
map.has('title') // true
map.get('title') // author

重复设置键时,后面的会覆盖前面的;以对象为键时,注意对象引用为同一个时,才会认为是同一个键。+0和-0视为同一个,NaN和NaN视为同一个键。

Map实例let m = new Map()的属性和方法:
m.size 返回成员数目。
m.set(key, value) 设置key对应的键值,返回整个Map实例。
m.get(key) 返回key的键值,若没有此键,则返回undefined
m.has(key) 返回boolean,表示该键是否在Map中。
m.delete(key) 删除key键,返回boolean,表示操作成功或失败。

Map遍历同Set遍历很相似,只不过键名和键值一般不一致:
keys() 返回一个键名的遍历器。
values() 返回一个键值的遍历器。
entries() 返回一个键值对的遍历器,意义不大。
forEach() 使用回调遍历每个成员。

Map相关数据结构转换:
Map转数组:用扩展运算符

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc'])
[...myMap] // [[true, 7], [{foo: 3}, ['abc']]]

数组转Map,构造函数传入数组即可。

Map转对象,遍历一遍赋值即可,同理对象转Map也是。

12.4 WeakMap

WeakMap结构与Map结构基本类似,不过它只接收对象作为键名,键名指向的对象不计入引用次数,不会对垃圾回收造成影响。

TO BE CONTINUED!