4月阅读周·你不知道的JavaScript | 集合,数据的结构化组合和访问的关键

举报
叶一一 发表于 2024/04/21 11:00:15 2024/04/21
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读三个月。4月份的阅读计划有两本,《你不知道的JavaScrip》系列迎来收尾。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《...

背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效,已经坚持阅读三个月。

4月份的阅读计划有两本,《你不知道的JavaScrip》系列迎来收尾。

已读完书籍《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》

当前阅读周书籍《你不知道的JavaScript(下卷)》

集合

TypedArray

名称中的“type(类型)”是指看待一组位序列的“视图”,本质上就是一个映射,比如是把这些位序列映射为8位有符号整型数组还是16位有符号整型数组,等等。

如何构建这样的位集合呢?这称为一个“buffer”,最直接的方法是通过ArrayBuffer(..)构造器来构造:

var buf = new ArrayBuffer(32);
buf.byteLength; // 32

现在buf就是一个二进制buffer,长为32字节(256位),预先初始化全部为0。一个buffer本身除了查看它的byteLength属性外,并不真正支持任何其他交互。

多视图

单个buffer可以关联多个视图,比如:

var buf = new ArrayBuffer(2);
var view8 = new Uint8Array(buf);

var view16 = new Uint16Array(buf);

view16[0] = 3085;
view8[0]; // 13
view8[1]; // 12

view8[0].toString(16); // "d"
view8[1].toString(16); // "c"

// 交换(就像大小端变换一样!)
var tmp = view8[0];
view8[0] = view8[1];
view8[1] = tmp;

view16[0]; // 3340

这个带类数组构造器有多个签名变体。目前我们展示的情况都是传入已经存在的buffer。然而这种形式还接受两个额外参数: byteOffset和length。换句话说,可以从非0的位置开始带类型数组视图,也可以不消耗整个buffer长度。

如果二进制数据的buffer包含的数据格式大小/位置不均匀,这种技术是非常有用的。

带类数组构造器

带类数组构造器还支持以下这些形式:

  • [constructor\](length):在一个新的长度为length字节的buffer上创建一个新的视图。
  • [constructor\](typedArr):创建一个新的视图和buffer,把typeArr视图的内容复制进去。
  • [constructor\](obj):创建一个新的视图和buffer,并在类数组或对象obj上迭代复制其内容。

ES6提供了下面这些带类数组构造器:

  • Int8Array(8位有符号整型), Uint8Array(8位无符号整型)——Uint8ClampedArray(8位无符号整型,每个值会被强制设置为在0-255内);
  • Int16Array(16位有符号整型), Uint16Array(16位无符号整型);
  • Int32Array(32位有符号整型), Uint32Array(32位无符号整型);
  • Float32Array(32位浮点数,IEEE-754);
  • Float64Array(64位浮点数,IEEE-754)。

Map

对象是创建无序键/值对数据结构[也称为映射(map)]的主要机制。但是,对象作为映射的主要缺点是不能使用非字符串值作为键。

ES6使用Map(..):

var m = new Map();

var x = { id: 1 },
  y = { id: 2 };

m.set(x, 'foo');
m.set(y, 'bar');

m.get(x); // "foo"
m.get(y); // "bar"

这里唯一的缺点就是不能使用方括号[ ]语法设置和获取值,但完全可以使用get(..)和set(..)方法完美代替。

要从map中删除一个元素,不要使用delete运算符,而是要使用delete()方法:

m.set(x, 'foo');
m.set(y, 'bar');

m.delete(y);

你可以通过clear()清除整个map的内容。要得到map的长度(也就是键的个数),可以使用size属性(而不是length):

m.set(x, 'foo');
m.set(y, 'bar');
m.size; // 2

m.clear();
m.size; // 0

WeakMap

WeakMap是map的变体,二者的多数外部行为特性都是一样的,区别在于内部内存分配(特别是其GC)的工作方式。

WeakMap(只)接受对象作为键。这些对象是被弱持有的,也就是说如果对象本身被垃圾回收的话,在WeakMap中的这个项目也会被移除。然而我们无法观测到这一点,因为对象被垃圾回收的唯一方式是没有对它的引用了。但是一旦不再有引用,你也就没有对象引用来查看它是否还存在于这个WeakMap中了。

除此之外,WeakMap的API是类似的,尽管要更少一些:

var m = new WeakMap();

var x = { id: 1 },
  y = { id: 2 };

m.set(x, 'foo');

m.has(x); // true
m.has(y); // false

WeakMap没有size属性或clear()方法,也不会暴露任何键、值或项目上的迭代器。所以即使你解除了对x的引用,它将会因GC时这个条目被从m中移除,也没有办法确定这一事实。

Set

set是一个值的集合,其中的值唯一(重复会被忽略)。

set的API和map类似。只是add(..)方法代替了set(..)方法(某种程度上说有点讽刺),没有get(..)方法。

var s = new Set();

var x = { id: 1 },
  y = { id: 2 };

s.add(x);
s.add(y);
s.add(x);

s.size; // 2

s.delete(y);
s.size; // 1

s.clear();
s.size; // 0

Set(..)构造器形式和Map(..)类似,都可以接受一个iterable,比如另外一个set或者仅仅是一个值的数组。但是,和Map(..)接受项目(entry)列表(键/值数组的数组)不同,Set(..)接受的是值(value)列表(值的数组):

var x = { id: 1 },
  y = { id: 2 };

var s = new Set([x, y]);

set不需要get(..)是因为不会从集合中取一个值,而是使用has(..)测试一个值是否存在:

var s = new Set();

var x = { id: 1 },
  y = { id: 2 };

s.add(x);

s.has(x); // true
s.has(y); // false

WeakSet

WeakSet对其值也是弱持有的(这里并没有键):

var s = new WeakSet();

var x = { id: 1 },
  y = { id: 2 };

s.add(x);
s.add(y);

x = null; // x可GC
y = null; // y可GC

WeakSet的值必须是对象,而并不像set一样可以是原生类型值。

总结

我们来总结一下本篇的主要内容:

  • ES6定义了几个有用的集合,这使得对数据的访问更结构化且更高效。
  • TypedArray提供了对二进制数据buffer的各种整型类型“视图”,比如8位无符号整型和32位浮点型。对二进制数据的数组访问使得运算更容易表达和维护,从而可以更容易操纵视频、音频、canvas数据等这样的复杂数据。
  • Map是键-值对,其中的键不只是字符串/原生类型,也可以是对象。
  • Set是成员值(任意类型)唯一的列表。
  • WeakMap也是map,其中的键(对象)是弱持有的,因此当它是对这个对象的最后一个引用的时候,GC(垃圾回收)可以回收这个项目。
  • WeakSet也是set,其中的值是弱持有的,也就是说如果其中的项目是对这个对象最后一个引用的时候,GC可以移除它。

作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏️ | 留言📝

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。