《JavaScript 高级程序设计》第6章 集合引用类型

304 阅读6分钟

一、Object

构造object对象有两种方式,一种是new 一种是对象字面量(类map)的方式。

访问属性可以用点号· 也可以用中括号

判断属性存不存在可以用typeof o.xxx

       let o = new Object();
        o.name = "doc";
        let o1 = {
            name: "doc"
        }
        console.log(o.name); // doc
        console.log(o1["name"]);// doc11
        console.log(typeof o1.age); // undefined

二、Array

创建:可以用new 或者 数组字面量,还可以使用Array.from(类数组)或者Array.of(一组参数)等静态方法来创建数组。

空位:数组初始化时,可以用一串逗号来创建空位。

索引:可以通过修改length属性,来删除或者添加元素。

检测:一般用instance of就行,但是有专门的判断Array方法,Array.isArray(xxxx);

迭代器:可以有keys(),values(),entries() 等方法。

复制和填充:复制可以用copywithin() 和fill()等方法。

转换方法:toString(),toValueOf(),toLocaleString()等。

栈方法:push()+pop()模拟栈。

队列方法:push()+shift()模拟队列。 unshift()和pop()可以模拟从队头添加元素,数组末尾取得数据。

排序方法:reverse()和sort();

操作:拼接:concat()如果不打平数组,可以用Sysmbol.isConcatSpreadable = false来阻止。

切片:slice(),从原始数组创建指定索引区间的数组。

删除、插入、替换都可以用splice(开始位置,要删除的元素数量,要插入的任意多个元素)

搜索和位置方法:indexOf,lastIndexOf,includes,find,findIndex等。

迭代:every()每一个都true才是true,some()有一个是true就是true。

filter 返回过滤出来为true的数组。 forEach()只是遍历,map就是转换数据之后构成的数组。

归并:reduce和reduceRight,这两方法都是迭代 数组所有项,并在此基础上构建一个最终返回值。

        let c = new Array();
        let c1 = new Array(3);
        let c2 = new Array("red", "green");
        let c3 = ["red", 'green'];
        let b = Array.from(new Map().set(1, 2).set(3, 4));
        let b2 = Array.from(new Set().add(1).add(2));
        let b3 = Array.from({
            *[Symbol.iterator]() {
                yield 1;
                yield 2;
            }
        })
        let a1 = [1, 2, 3, 4];
        let b4 = Array.from(a1, x => x ** 2);
        let b5 = Array.of(1, 2, 3);
        let e = [1, , , 5];
        console.log(e.join('_'));
        let ints = [1, 2, 3, 4, 5];
        console.log(ints.copyWithin(3, 0, 2)); // [1, 2, 3, 1, 2]
        console.log(ints.fill(3));// [3, 3, 3, 3, 3]
        function compare(value1, value2) {
            if (value1 < value2) {
                return -1;
            } else if (value1 > value2) {
                return 1;
            } else {
                return 0;
            }
        }
        // 直接传一个方法名,
        // (method) Array<number>.sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]
        ints.sort(compare);

三、定型数组(typed array)

目的是为了提升向原生库传输数据的效率。比如Float32Array等。

3.1 ArrayBuffer

Float32Array实际是一种”视图“,可以允许js运行时访问一块名为ArrayBuffer的预分配内存。ArrayBuffer是所有定型数组及视图引用的基本单位。

ArrayBuffer() 是一个普通的js构造函数,可用于内存中分配特定数量的字节空间。

ArrayBuffer类似于C++中的malloc(),但是有明显区别:

malloc分配失败会返回null指针,而ArrayBuffer分配失败会抛错误。

malloc可以利用虚拟内存,最大分配尺寸只受寻址系统内存限制。ArrayBuffer分配的内存不能超过N.umber.MAX_SAFE_INTEGER字节。

malloc调用成功不会初始化实际的地址,声明ArrayBuffer则会将所有的二进制初始化为0;

malloc分配的堆内存除非调用free或程序退出,否则系统不能再使用,而通过生命ArrayBuffer分配的堆内存可以被垃圾回收,不用手动释放。

不能仅通过对ArrayBuffer的引用就能读写内容,要读取和写入ArrayBuffer就必须通过视图,视图有不同类型,但都是引用ArrayBuffer中存储的二进制数据。

3.2 DataView和定型数组

这两个就是视图,DataView主要用于文件I/O或者网络I/O, 定型数组:Int32Array等,特定的一种ElementType且遵循系统原生的字节序。

DataView在操作的使用ElementType来实现js的Number类型到缓冲内二进制格式的转换,且默认大端字节序。

ElementType:8种,Int8 Unit8 Int16 Uint16 Int32 Uint32 Float32 Float64

       const buf = new ArrayBuffer(2);
       const view = new DataView(buf);
       view.setUint8(0,0x80);
       view.setUint8(1,0x01);
       console.log(view.getUint16(0)); // 32769 
       console.log(view.getUint16(0,true));// 384

DataView默认是大端字节序,也就是高位在前,低位在最后。如果启用小端字节序,这里就用true。

0x80:对应的二进制是1000 0000,(每个十六进制数字对应四位二进制【因为2的4次方就是16】,这里的8对应1000,8后的0对应0000,也就是10000000)。

Uint8 是一个数据类型,表示一个 8 位无符号整数(即一个字节)。view.setUint8(0, 0x80) 的作用是将一个 8 位无符号整数(一个字节)写入 DataView 对象的索引 0 处。

0x01: 对应的二进制是0000 0001

view.getUint16(0) 的计算按照大端字节序,也就是高位在左,低位在右,就是:1000 0000 0000 0001,算出来是2^15+2^1=32769;

view.getUint16(0,true) 的计算按照小端字节序,也就是低位在左,高位在右,就是反向拼接,0000 0001 1000 0000,计算出来就是2^8+2^7 = 384;

       // 创建12字节的缓冲 
       const buf = new ArrayBuffer(12);
       // 每一个元素需要4个字节,这里就只有3个元素
       const ints = new Int32Array(buf);
       console.log(ints.length); // 3
       console.log(ints.byteLength);// 12
       // 定型数组特殊的复制数据 set /subarray
       const ints2 = new Int32Array(6);
       ints2.set(Int32Array.of(1,2,3));
       console.log(ints2); // 1 2 3 0 0 0
       ints2.set(Int32Array.of(1,2,3),3);
       console.log(ints2); // 1 2 3 1 2 3
       const ints3 = ints2.subarray(1,2);
       console.log(ints3);// 2

对于定型数组的上溢和下溢出,只会影响到相关位,不会影响到其他索引。

四、Map/WeakMap/Set/WeakSet

      // 直接使用new  
      const m1 = new Map();
      m1.set("sss","1");
      // 嵌套数组初始化映射
      const m2 = new Map([
        ["sss","1"]
      ]);
      // 使用自定义迭代器
      const m3 = new Map({
        [Symbol.iterator]:function*(){
            yield ["sss","1"];
        }
      });
     for(let pair of m1.entries()){
        console.log(pair);//  ['sss', '1']
     }
     for(let pair of m3[Symbol.iterator]()){
        console.log(pair); //  ['sss', '1']
     }
     for(let key  of m1.keys()){
        console.log(key); // sss
     }
     for(let value  of m1.values()){
        console.log(value);// 1
     }
     m1.forEach((val,key)=>console.log(`${val}+${key}`)) // 1+sss

Set也有类似的方法,同上

WeakMap、WeakSet中前者对key必须是Object或者继承Object类型,后者是值必须是Object或者继承Object类型。

4.1.1选择Object还是Map主要看:

同样内存,map可以比object多存50%的键值对。

插入和删除Map更好,查询Object更好一些。


WeakMap和WeakSet不可以迭代。

从es6开始Map和Set都是有序的了。