10、改进的数组功能

第十章、改进的数组功能


1、创建数组
    传统的创建数组的方法:调用Array构造函数和数组字面量语法。
    将一个类数组(具有数值型索引和length属性的对象)转换为数组的方法:Array.of()和Array.from()
    1、Array.of()方法
        因为通过Array构造函数创建数组的时候,传参的类型与数量会影响数组的length:
            传入一个数值型的值,则数组的length就是该值;
            传入一个非数值型的值,则该值会成为数组的元素;
            传入多个值,不管是数值型还是非数值型,都会变成数组的元素。
        Array.of()方法就是用来解决这个问题,不管Array.of()的参数的类型和个数,Array.of()方法总是将所有的参数当做新建数组的元素。

let items = Array.of(1,2);
console.log(items.length);	// 2
console.log(items[0]);		// 1
console.log(items[1]);		// 2
let items = Array.of(1);
console.log(items.length);	// 1
console.log(items[0]);		// 1
let items = Array.of("1");
console.log(items.length);	// 1
console.log(items[0]);		// "1"

        备注:Array.of()方法不通过Symbol.species属性确定返回值的类型,它使用当前构造函数(也就是of方法中的this值)来确定
        正确的返回数据的类型。

    2、Array.from()方法
        JavaScript对象不支持直接将非数组对象转换为数组对象。
        传统方法:

function makeArray(likeArray){
	return Array.prototype.slice.call(likeArray);
}

        新方法:

function makeArray(likeArray){
	return Array.from(likeArray);
}

        Array.from()方法可接受可迭代对象或类数组对象作为第一个参数,返回Array的实例。
        备注:Array.from()方法也是通过this来确定返回的数组的类型的。

        ①、映射转换
            可以提供一个映射函数作为Array.from()的第二个参数,这个函数用来转换类数组中的元素值。

function translate(){
	return Array.from(arguments,value => value + 1);
}

            可以给Array.from()传入第三个参数,表示映射函数的this值。

let helper = {
	differ: 1,
	add(value){
		return value + this.differ;
	}
};
function translate(){
	return Array.from(arguments,helper.add,helper);
}

        ②、用Array.from()转换可迭代对象
            即Array.from()方法可以将所有含有Symbol.iterator属性的对象转换为数组。

let numbers = {
	*[Symbol.iterator](){
		yield 1;
		yield 2;
		yield 3;
	}
};
let numbers2 = Array.from(numbers,value => value + 1);
console.log(numbers2);		// 2,3,4

            备注:如果一个对象既是类数组又是可迭代的,Array.from()方法会根据迭代器来决定转换哪个值。

2、为所有数组添加的新方法
    1、find()方法和findIndex()方法
        传统方法:
                indexOf()方法和lastIndexOf()方法:在数组中查找特定的值。
        ES6方法:
                find()方法和findIndex()方法
                参数:一个回调函数,一个可选参数用来指定回调函数中this的值。
                        回调函数的参数与forEach()方法的相同。
                如果给定的值满足定义的标准,回调函数返回true;一旦回调函数返回true,find()方法和findIndex()方法都会立即停止搜索数组剩下的部分。
        区别:
                find()方法返回查找到的值;
                findIndex()方法返回查找到的值得索引。

let numbers = [25,30,35,40,45];
console.log(numbers.find(n => n > 33));		// 35
console.log(numbers.findIndex(n => n > 33));		// 2

    2、fill()方法
        用指定的值填充一个或多个数组元素;当传入一个值时,fill()方法会用这个值重写数组中的所有值。
        参数:第一个参数表示要填充的值;第二个参数表示开始索引;第三个参数表示结束索引。包前不包后,后两个参数可选。

let numbers = [1,2,3,4];
numbers.fill(1,2);
console.log(numbers.toString());		// 1,2,1,1
numbers.fill(0,1,2);
console.log(numbers.toString());		// 1,0,0,2

        备注:如果开始索引和结束索引为负值,那么这些值会与数组的长度相加得到最终位置。
            
    3、copyWithin()方法
        复制数组的部分值再填充到数组。
        参数:第一个参数指定开始填充值得索引位置;第二个参数指定开始复制值得索引位置。
                    第三个参数指定被重写元素的数量。包前不包后。

let numbers = [1,2,3,4];
numbers.copyWithin(2,0,1);
console.log(numbers.toString());		// 1,2,1,4

        备注:如果参数为负值,那么这些值会与数组的长度相加得到最终位置。

3、定型数组
    定型数组是一种用于处理数值类型数据的专用数组。可以为JavaScript提供快速的按位运算。
    在JavaScript中,数字是以64位浮点数格式存储的,并按需转换为32位整数,因此速度很慢。
    所谓定型数组,就是将任何数字转换为一个包含数字比特的数组。
    
    1、数值数据类型
        定型数组支持存储和操作以下8种不同的数值类型:
                ①、有符号的8位整数int8
                ②、无符号的8位整数uint8
                ③、有符号的16位整数int16
                ④、无符号的16位整数uint16
                ⑤、有符号的32位整数int32
                ⑥、无符号的32位整数uint32
                ⑦、32位浮点数float32
                ⑧、64位浮点数float64
        使用这些数据,需要创建一个数组缓冲区存储这些数据。
            
    2、数组缓冲区
        数组缓冲区是一段可以包含特定数量字节的内存地址。创建数组缓冲区不需指明内存块包含的数据类型。
        调用构造函数时传入缓冲区的比特数量即可。

let buffer = new ArrayBuffer(10);
console.log(buffer.byteLength);			// 10
let buffer2 = buffer.slice(4,6);	  // slice()方法包前不包后
console.log(buffer2.byteLength);		// 2

        备注:数组缓冲区包含的实际数量创建时就已经确定,可以修改缓冲区内的数据,但是不能改变缓冲区的尺寸大小。
            
    3、通过视图操作数组缓冲区
        视图是用来操作内存的接口。视图可以操作数组缓冲区或缓冲区的子集,并按照其中一种数值型数据类型来读取和写入数据。
        DataView类型是一种通用的数组缓冲区视图,支持所有八种数值型数据类型。

let buffer = new ArrayBuffer(10);
let view = new DataView(buffer,5,2);

        视图view操作缓冲区buffer位于索引5和索引6的字节。第二个参数和第三个参数可选。
        
        ①、获取视图信息
            可通过以下只读属性获取视图的信息:
                buffer:视图绑定的数组缓冲区
                byteOffset:DataView构造函数的第二个参数,默认为0,只有传入参数时才有值。
                byteLength:DataView构造函数的第三个参数,默认是缓冲区的长度byteLength。

let buffer = new ArrayBuffer(10);
let view1 = new DataView(buffer);
let view2 = new DataView(buffer,5,2);
console.log(view1.buffer === buffer);		// true
console.log(view2.buffer === buffer);		// true
console.log(view1.byteOffset);				// 0
console.log(view2.byteOffset);				// 5
console.log(view1.byteLength);				// 10
console.log(view2.byteLength);				// 2

            由于两个视图都是基于相同的数组缓冲区创建的,因此他们具有相同的buffer属性。
            
        ②、读取和写入数据
            用于读取和写入int8和uint8类型数据的方法:
                getInt8(byteOffset,littleEndian):读取位于byteOffset后的int8类型的数据。
                setInt8(byteOffset,value,littleEndian):在byteOffset处写入int8类型的数据。
                getUint8(byteOffset,littleEndian):读取位于byteOffset后的uint8类型的数据。
                setUint8(byteOffset,value,littleEndian):在byteOffset处写入uint8类型的数据。
            类似的方法还有:
                getInt16(byteOffset,littleEndian)、setInt16(byteOffset,value,littleEndian)、
                getUint16(byteOffset,littleEndian)、setUint16(byteOffset,value,littleEndian)、
                getInt32(byteOffset,littleEndian)、setInt32(byteOffset,value,littleEndian)、
                getUint32(byteOffset,littleEndian)、setUint32(byteOffset,value,littleEndian)
                getFloat32(byteOffset,littleEndian)、setFloat32(byteOffset,value,littleEndian)、
                getFloat64(byteOffset,littleEndian)、setFloat64(byteOffset,value,littleEndian)
            get方法接收两个参数:读取数据时的字节偏移量;一个可选的布尔值,表示是否按照小端排序进行读取。
            set方法接收三个参数:写入数据时的字节偏移量;写入的值;一个可选的布尔值,表示是否按照小端排序进行写入。
            小端排序:指最低有效字节位于字节0的字节顺序。

let buffer = new ArrayBuffer(2);
let view = new DataView(buffer);
view.setInt8(0,5);
view.setInt8(1,-1);
console.log(view.getInt8(0);		// 5
console.log(view.getInt8(1);		// -1

            视图是独立的,无论数据之前是通过何种方式存储的,你都可以在任意时刻读取或写入任意格式的数据。

let buffer = new ArrayBuffer(2);
let view = new DataView(buffer);
view.setInt8(0,5);
view.setInt8(1,-1);
console.log(view.getInt16(0);		// 1535
console.log(view.getInt8(0);		// 5
console.log(view.getInt8(1);		// -1

        ③、定型数组是视图
            定型数组实际上是用于数组缓冲区的特定类型的视图。
            8个特定类型的视图对应于8中不同数值型数据类型。
            ES6中的特定类型视图:
                构造函数名称            元素尺寸(字节)        说明
                Int8Array                    1                    8位二进制补码有符号整数
                Uint8Array                    1                    8位无符号整数
                Uint8ClampedArray            1                    8位无符号整数(强制类型转换)
                Int16Array                    2                    16位二进制补码有符号整数
                Uint16Array                    2                    16位无符号整数
                Int32Array                    4                    32位二进制补码有符号整数
                Uint32Array                    4                    32位无符号整数
                Float32Array                4                    32位IEEE浮点数
                Float64Array                8                    64位IEEE浮点数
            Uint8ClampedArray与Uint8Array大致相同,区别是当数组缓冲区中的值如果小于0或大于255,Uint8ClampedArray会强制分别将其转换为0或255
                
        ④、创建特定类型的视图        
            第一种创建定型数组的方法:

let buffer = new ArrayBuffer(10);
let view1 = new Int8Array(buffer);
let view2 = new Int8Array(buffer,5,2);
console.log(view1.buffer === buffer);			// true
console.log(view2.buffer === buffer);			// true
console.log(view1.byteOffset);					// 0
console.log(view2.byteOffset);					// 5
console.log(view1.byteLength);					// 10
console.log(view2.byteLength);					// 2

            第二种创建定型数组的方法:

let ints = new Int16Array(2);
let floats = new Float32Array(5);
console.log(ints.byteLength);					// 4
console.log(ints.length);						// 2
console.let(floats.byteLength);					// 20
console.let(floats.length);						// 5

                备注:调用定型数组的构造函数时如果不传参数,会按照传入0来处理,因此缓冲区没有分配到任何比特,因此创建的定型数组不能用来保存数据。
            第三种创建定型数组的方法:
                调用构造函数时,将以下任意对象作为唯一的参数传入:
                    * 一个定型数组:该数组中的每个元素会作为新的元素被复制到新的定型数组中。
                    * 一个可迭代对象:对象的迭代器被调用,通过检索所有条目来选取插入到定型数组的元素,如果所有元素都是不符合用于该视图类型的无效类型,构造函数将会抛出一个错误。
                    * 一个数组或一个类数组对象:数组中的元素会被复制到一个新的定型数组中,如果所有类型都是不适合该视图类型的无效类型,构造函数将会抛出错误。

let ints1 = new Int16Array([25,50]);
let ints2 = new Float32Array(ints1);
console.log(ints1.buffer === ints2.buffer);		// false
console.log(ints1.byteLength);					// 4
console.log(ints1.length);						// 2
console.log(ints1[0]);							// 25
console.log(ints1[1]);							// 50
console.let(ints2.byteLength);					// 8
console.let(ints2.length);						// 2
console.log(ints2[0]);							// 25
console.log(ints2[1]);							// 50

        备注:元素大小指的是每种定型数组中每个元素表示的字节数。该值存储在每个构造函数和每个实例的BYTES_PER_ELEMENT属性中。

4、定型数组与普通数组的相似之处        
    可以修改length属性来改变普通数组的大小,但定型数组的length属性是一个不可写属性,因此不能修改定型数组的大小。
    1、通用方法
        以下方法均可用于定型数组:
            copyWithin()、findIndex()、lastIndexof()、slice()、entries()、forEach()、map()、some()、fill()、indexOf()、
            reduce()、sort()、filter()、join()、reduceRight()、values()、find()、keys()、reverse()
            
        定型数组中的方法会额外检查数值类型是否安全,也会通过Symbol.species确认方法的返回值是定型数组而不是普通数组。

let ints = new Int16Array([25,50]),
	mapped = ints.map(v=>v*2);
console.log(mapped.length);			// 2
console.log(mapped[0]);				// 50
console.log(mapped[1]);				// 100
console.log(mapped instanceof Int16Array);		// true

    2、相同的迭代器
        定型数组与普通数组有3个相同的迭代器:entries()、keys()、values()
        可以把定型数组当做普通数组一样来使用展开运算符、for-of循环

let ints = new Int16Array([25,50]);
let intsArray = [...ints];
console.log(intsArray instanceof Array);		// true
console.log(intsArray[0]);						// 25
console.log(intsArray[1]);						// 50

        展开运算符能够将可迭代对象转换为普通数组,也可以将定型数组转换为普通数组。

    3、of()方法和from()方法
        所有定型数组都含有静态of()方法和from()方法,运行效果与Array.of()方法和Array.from()方法相似。
        区别:定型数组的方法返回定型数组,普通数组的方法返回普通数组。
        
5、定型数组与普通数组的差别
    定型数组与普通数组的最重要的区别:定型数组不是普通数组。
    定型数组不继承自Array,通过Array.isArray()方法检查定型数组返回的是false
    1、行为差异
        ①、定型数组始终保持相同的尺寸,其length属性不可变。
            给定型数组中不存在的索引赋值会被忽略,但在普通数组中是可以的。
        ②、定型数组同样会检查数据类型的合法性,0不能被用于代替所有非法值。
            所有修改定型数组的方法都会受到此限制。

let ints = new Int16Array(['hi']);
console.log(ints.length);
console.log(ints[0]);		// 0

    2、缺失的方法
        以下方法在定型数组中不存在:
            concat()、shift()、pop()、splice()、push()、unshift()
    
    3、附加方法
        set():将其他数组复制到已有的定型数组
            参数:一个是数组(定型数组和普通数组都支持);一个是可选的偏移量,表示开始插入数据的位置,默认为0.

let ints = new Int16Array(4);
ints.set([25,50]);
ints.set([75,100],2);
console.log(ints.toString());		// 25,50,75,100

        subarray():提取已有定型数组的一部分作为一个新的定型数组
            参数:一个是可选的开始位置;一个是可选的结束位置(不包含);最后返回一个新的定型数组。

let ints = new Int16Array([25,50,75,100]),
	subints1 = ints.subarray(),
	subints2 = ints.subarray(2),
	subints3 = ints.subarray(1,3);
console.log(subints1.toString());		// 25,50,75,100
console.log(subints2.toString());		// 75,100
console.log(subints3.toString());		// 50,75
发布了247 篇原创文章 · 获赞 23 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/LiyangBai/article/details/103227424
今日推荐