Open yuqingc opened 4 years ago
Key 可以是 String 和 Number,Number 类型会自动转为 String 类型
let person = { name: 'Matt', 5: true // 这里的 key 写的 number 5,会自动转为 String "5" };
良好代码规范:尽量使用 . 获取对象属性,除非不得不用到 []
.
[]
const age = person.age const age = person['age'] // 这样不好,多此一举 const age = person['real age'] // 这样ok,因为 key 有空格,无法使用 . 点语法
Array.from()
let arr = [,,,,]
ES6 之后新增的语法,空的项目视为 undefined。但是有 ES6 方法会忽略这些项,比如 map
undefined
map
for (const option of options) { alert(option === undefined); } // false // true // true // true // false // for in 因为是 ES6 之前的语法,所以啥都不会输出 for (const index in options) { console.log(index); } const options = [1,,,,5] alert(options.map(() => 6)); // 6,,,,6
为了避免被同事骂,请勿使用此类骚操作,如果一定要空,请显式赋值 undefined
arr instanceof Array
推荐使用: Array.isArray(arr)
Array.isArray(arr)
fill()
copyWithin()
JS 普通数组的数字是浮点数,在有些场景例如 WebGL 中,需要使用二进制数据等,于是就引入了 Typed Array
用来存储内存中的数据
不可直接通过索引访问,但是可以通过 view 来访问或操作数据
大小不可变
与 C++ 的 malloc() 的比较
malloc()
如果调用失败,会抛错,malloc 会返回 null 指针
大小限制在 Number.MAX_SAFE_INTEGER,而 malloc 只受系统可分配内存大小限制
Number.MAX_SAFE_INTEGER
会把所有位设置为 0,malloc 不会改变原来的内存数据
可以被垃圾回收,malloc 需要手动通过 free() 释放或者等程序退出
free()
可以高度控制操作 ArrayBuffer 的内容
一般用于文件和网络 IO 操作
可以使用整个 buffer 或部分 buffer
维护 buffer 的引用
第一个参数传入需要操作的 ArrayBuffer
第二个参数传入操作数据的起始位置,默认从头开始(0)
第三个参数传入限定 view 的长度,默认取从起始位置到结尾
buffer 属性永远指向传入的 buffer
const buf = new ArrayBUffer(16); const view = new DataView(buf, 0 , 8); view.byteLength; view.byteOffset; view.buffer === buf; // true
offset
ElementType:元素类型,用来把 buffer 中存储的二进制数据和 JS Number 类型进行类型相互转换
字节顺序:默认大端字节序(大内存地址存放低位字节)
DataView 并不知道 buffer 的数据类型,读写的时候需要指定数据类型,DataView 负责读写时的类型转换
DataView 对每种类型都暴露了对应的 set 和 get 方法
// 参数是 offset view.getInt8(1); // 把第一个字节的所有的位设置位 1 // 255 是二进制的 11111111,也是 16 进制的 0xFF view.setUint8(0, 255);
默认大端字节序(大内存地址存放低位字节,注意是字节不是位)
大端字节序又叫网络字节序
假设下列数据,八位为一个字节,一共 2 字节,内存地址递增 | 1000 0000 | 0000 0001 | 大端字节序就和书写的方式一样 1000 0000 0000 0001 如果是小端字节序,则上述字节在内存中的排列会被读取为 0000 0001 1000 0000
set 和 get 方法的最后一个参数,如果传入 true,则会以小端字节序进行读写
true
view.getUint16(0, true); view.setUint16(0, 0x0002, true);
越界读取和写入会抛错 RageError
RageError
写入数据时,DataView 会尽量去做类型转换(会降级为 0),实在转不了,就会抛错 TypeError
TypeError
1.5 -> 1 [4] -> 4 'f' -> 0 Symbol() -> 抛错
底层仍指向一个 ArrayBuffer
强制某种 ElementType
使用系统的字节序
性能更好
用来高效的把二进制数据和原生数据进行交换,运用场景比如 WebGL
可以使 JS 引擎可以更快处理需要大量对二进制数据进行操作的场景
构造函数,可以传入
ArrayBuffer
数字,长度(这里的长度是元素的个数,而不是字节数)
普通数字数组
另一个 Typed Array
from 方法,可以传入一个普通数组
of 方法,传入参数,比如 Float32Array.of(3.14, 2.718, 1.618)
Float32Array.of(3.14, 2.718, 1.618)
length: 元素数量
length
byteLength: 字节数量
byteLength
BYTES_PER_ELEMENT 每个元素占用字节的数量
BYTES_PER_ELEMENT
支持下表索引
支持 Array 的大部分方法
map 等方法仍然会返回一个 Typed Array
可迭代(拥有 Symbol.iterator 迭代器,支持 for of 遍历)
Symbol.iterator
for of
因为 ArrayBuffer 大小不可变不支持改变数组的操作,比如 push, pop, shift, unshift, splice
push
pop
shift
unshift
splice
不支持 concat
concat
set() 用来把一个数组的值拷进一个 Typed Array,需要指定索引,默认从 0 开始拷贝
set()
subarray() 指定索引,拷贝一份新的
subarray()
注意 slice 和 subarray 的区别:slice() 会完全复制一份新的,subarray 和原数组仍共用一个 ArrayBuffer
slice
subarray
slice()
溢出的数值不会占用其他的索引,但是会做一定的处理
只会截取使用有效位,会造成数字存储不对
Uint8ClampedArray 会把数值限定在 0 ~ 255 之间,超过或不足的数字会取为 0 或 255
Uint8ClampedArray
构造函数必须传入一个可迭代对象(键值对数组或迭代器函数)
Map 的 key 可以是任何类型,Object 的 key 只能是数字、字符串和 Symbol
Value 可以是任何类型
Key 的相等是通过 SameValueZero 来判断的,和 === 判断结果类似。有两个需要额外注意
===
+0 和 -0 视为相同
+0
-0
NaN 也视为相同
NaN
关于值的相等性比较,参考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
按照插入的顺序
可以通过 entries() 或 Symbol.iterator 属性获取其迭代器,是一个键值对数组
entries()
[...mapInstance] 展开操作符可以将 map 转换为迭代器数组
[...mapInstance]
其他 API 请参考文档
【内存】不同浏览器对内存管理不一样,但是总体而言,随着键值对的增加,内存是线性增长的。相同内存下,Map 比 Object 可以存储大约 50% 的键值对
【插入】插入效率不随键值对线性增长,总体差别不大。但是如果需要插入大量数据,Map 的插入速度更快一点
【查找】性能差不多,但是如果索引是类似数组递增的数字,浏览器对 Object 会做一些性能优化,这种场景,Object 会更胜一筹
【删除】删除性能 Map 更好
WeakMap 对 API 是 Map 的严格子集
与 Key 的垃圾回收有关
Key 必须是继承了 Object 的类型,不可把基本类型当作 key
有 get, set, has 方法
get
set
has
Key 是弱引用,不会阻止垃圾回收
Value 不是弱引用,只要 Key 没有被回收,Value 就会一直存在
不可迭代,没有 clear() 方法
clear()
思考为什么不能用基本类型作为 key?如果用基本类型作为 key,那么就无法区分这个 key 是原来的 key 还是一个新的 key,因为基本类型是按值拷贝的
const User = (() => { const wm = new WeakMap(); class User { constructor(id) { this.idProperty = Symbol('id'); this.setId(id); } setPrivate(property, value) { const privateMembers = wm.get(this) || {}; privateMembers[property] = value; wm.set(this, privateMembers); } getPrivate(property) { return wm.get(this)[property]; } setId(id) { this.setPrivate(this.idProperty, id); } getId(id) { return this.getPrivate(this.idProperty); } } return User; })(); const user = new User(123); alert(user.getId()); // 123 user.setId(456); alert(user.getId()); // 456
基本 API 见文档
迭代按照插入顺序
通过 values()(别名 keys())或者 Symbol.iterator 属性获取迭代器
values()
keys()
可以通过 [...set] 来把一个 Set 转换为数组
[...set]
entries() 返回一个每个元素是两个相同值元素的数组
WeakSet 只能存储 Object 类型的子类型,不能存储基本类型
支持 add, delete, has 方法
add
delete
不影响垃圾回收
值不可迭代
...
06 集合引用类型
Object 类型
Key 可以是 String 和 Number,Number 类型会自动转为 String 类型
良好代码规范:尽量使用
.
获取对象属性,除非不得不用到[]
Array 类型
Array.from()
创建 Array,详情参考文档数组空洞(Array Holes)
ES6 之后新增的语法,空的项目视为
undefined
。但是有 ES6 方法会忽略这些项,比如map
判断是否是数组
arr instanceof Array
推荐使用:
Array.isArray(arr)
填充数组的两个方法
fill()
copyWithin()
Typed Array
JS 普通数组的数字是浮点数,在有些场景例如 WebGL 中,需要使用二进制数据等,于是就引入了 Typed Array
ArrayBuffer
用来存储内存中的数据
不可直接通过索引访问,但是可以通过 view 来访问或操作数据
大小不可变
与 C++ 的
malloc()
的比较如果调用失败,会抛错,malloc 会返回 null 指针
大小限制在
Number.MAX_SAFE_INTEGER
,而 malloc 只受系统可分配内存大小限制会把所有位设置为 0,malloc 不会改变原来的内存数据
可以被垃圾回收,malloc 需要手动通过
free()
释放或者等程序退出DataView
可以高度控制操作 ArrayBuffer 的内容
一般用于文件和网络 IO 操作
可以使用整个 buffer 或部分 buffer
维护 buffer 的引用
构造函数
第一个参数传入需要操作的 ArrayBuffer
第二个参数传入操作数据的起始位置,默认从头开始(0)
第三个参数传入限定 view 的长度,默认取从起始位置到结尾
buffer 属性永远指向传入的 buffer
操作 buffer 需要使用到的 DataView 的要素
offset
ElementType:元素类型,用来把 buffer 中存储的二进制数据和 JS Number 类型进行类型相互转换
字节顺序:默认大端字节序(大内存地址存放低位字节)
ElementType
DataView 并不知道 buffer 的数据类型,读写的时候需要指定数据类型,DataView 负责读写时的类型转换
DataView 对每种类型都暴露了对应的 set 和 get 方法
大端字节序和小端字节序
默认大端字节序(大内存地址存放低位字节,注意是字节不是位)
大端字节序又叫网络字节序
set 和 get 方法的最后一个参数,如果传入
true
,则会以小端字节序进行读写边界情况
越界读取和写入会抛错
RageError
写入数据时,DataView 会尽量去做类型转换(会降级为 0),实在转不了,就会抛错
TypeError
Typed Arrays
底层仍指向一个 ArrayBuffer
强制某种 ElementType
使用系统的字节序
性能更好
用来高效的把二进制数据和原生数据进行交换,运用场景比如 WebGL
可以使 JS 引擎可以更快处理需要大量对二进制数据进行操作的场景
构建
构造函数,可以传入
ArrayBuffer
数字,长度(这里的长度是元素的个数,而不是字节数)
普通数字数组
另一个 Typed Array
from 方法,可以传入一个普通数组
of 方法,传入参数,比如
Float32Array.of(3.14, 2.718, 1.618)
属性
length
: 元素数量byteLength
: 字节数量BYTES_PER_ELEMENT
每个元素占用字节的数量操作
支持下表索引
支持 Array 的大部分方法
map
等方法仍然会返回一个 Typed Array可迭代(拥有
Symbol.iterator
迭代器,支持for of
遍历)其他操作
因为 ArrayBuffer 大小不可变不支持改变数组的操作,比如
push
,pop
,shift
,unshift
,splice
不支持
concat
set()
用来把一个数组的值拷进一个 Typed Array,需要指定索引,默认从 0 开始拷贝subarray()
指定索引,拷贝一份新的数值溢出
溢出的数值不会占用其他的索引,但是会做一定的处理
只会截取使用有效位,会造成数字存储不对
Uint8ClampedArray
会把数值限定在 0 ~ 255 之间,超过或不足的数字会取为 0 或 255Map
构造函数必须传入一个可迭代对象(键值对数组或迭代器函数)
Map 的 key 可以是任何类型,Object 的 key 只能是数字、字符串和 Symbol
Value 可以是任何类型
Key 的相等是通过 SameValueZero 来判断的,和
===
判断结果类似。有两个需要额外注意+0
和-0
视为相同NaN
也视为相同关于值的相等性比较,参考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
Map 的遍历顺序
按照插入的顺序
可以通过
entries()
或Symbol.iterator
属性获取其迭代器,是一个键值对数组[...mapInstance]
展开操作符可以将 map 转换为迭代器数组Map 和 Object 的性能比较
【内存】不同浏览器对内存管理不一样,但是总体而言,随着键值对的增加,内存是线性增长的。相同内存下,Map 比 Object 可以存储大约 50% 的键值对
【插入】插入效率不随键值对线性增长,总体差别不大。但是如果需要插入大量数据,Map 的插入速度更快一点
【查找】性能差不多,但是如果索引是类似数组递增的数字,浏览器对 Object 会做一些性能优化,这种场景,Object 会更胜一筹
【删除】删除性能 Map 更好
WeakMap
WeakMap 对 API 是 Map 的严格子集
与 Key 的垃圾回收有关
基本 API
Key 必须是继承了 Object 的类型,不可把基本类型当作 key
有
get
,set
,has
方法弱引用
Key 是弱引用,不会阻止垃圾回收
Value 不是弱引用,只要 Key 没有被回收,Value 就会一直存在
不可迭代,没有
clear()
方法思考为什么不能用基本类型作为 key?如果用基本类型作为 key,那么就无法区分这个 key 是原来的 key 还是一个新的 key,因为基本类型是按值拷贝的
使用场景
创造真正的私有属性
用来存储 DOM 节点的一些自定义属性
Set
API
基本 API 见文档
迭代
迭代按照插入顺序
通过
values()
(别名keys()
)或者Symbol.iterator
属性获取迭代器可以通过
[...set]
来把一个 Set 转换为数组entries()
返回一个每个元素是两个相同值元素的数组WeakSet
WeakSet 只能存储 Object 类型的子类型,不能存储基本类型
支持
add
,delete
,has
方法存放弱引用
不影响垃圾回收
值不可迭代
用途
展开操作符
...