《深入理解ES6》第十章 增强的数组

第十章 增强的数组

《深入理解ES6》—— Nicholas C. Zakas

1. 创建数组

ES6之前创建数组的方式:

  • Array构造器
  • 数组字面量

无法直接将 类数组对象(有数值类型索引和length属性) 转换为真正的数组。

ES6 新增了 Array.of()Array.from() 方法创建数组。

1.1. Array.of() 方法

let items = new Array( 2 );

items.length; //=> 2
items[ 0 ];   //=> undefined
items[ 1 ];   //=> undefined

使用Array构造函数创建数组:

  • 当传入参数是单个数值时,会创建指定长度的空数组
  • 当传入参数是单个非数值时,该参数会成为目标数组的唯一项

Array.of() 就是为了解决这个问题的,传入的每一个参数,都会成为数组的项。

1.2. Array.from() 方法

将类数组对象转换为数组

ES6之前的做法:

// 方式一:写了很多代码
function makeArray( arrayLike ) {
    var result = [];
    for ( var i = 0, len = arrayLike.length; i < len; i++ ) {
        result.push( arrayLike[ i ] );
    }
    return result;
}

// 方式二:目的不清晰
function makeArray( arrayLike ) {
    return Array.prototype.slice.call( arrayLike );
}

ES6:

function doSomething() {
    var args = Array.from( arguments );
    //...
}

映射转换:

// 进一步转换
// 第二个参数为映射函数,处理类数组对象的每一个值
function translate() {
    return Array.from( arguments, value => value + 1 );
}
translate( 1, 2, 3 ); //=> [ 2, 3, 4 ]

// 改变映射函数内部 `this` 值
let helper = {
    diff: 2,
    add( value ) {
        return value + this.diff;
    }
}

function translate() {
    return Array.from( arguments, helper.add, helper );
}

translate( 1, 2, 3 ); //=> [ 3, 4, 5 ]

将可迭代对象转换为数组

可将包含 Symbol.iterator 属性的对象转换为数组

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

2. 新的实例方法

2.1. find() 和 findIndex()

ES5中的 indexOf()lastIndexOf() 只能查找特定值。

ES6中的 find()findIndex() 可以查找符合规则的值。

let numbers = [ 11, 22, 33, 44, 55 ];

// 返回匹配的值
numbers.find( 
    (item, index, items) => item > 2 
); 
//=> 33

// 返回匹配的值的索引
numbers.findIndex( 
    (item, index, items) => item > 2 
); 
//=> 2

2.2. fill() 方法

用特定值填充数组

// 填充全部
let nums = [ 0, 1, 2, 3, 4, 5 ];
nums.fill( "a" );
nums; //=> ["a", "a", "a", "a", "a", "a"]

// 填充部分:fill( target, start )
let nums = [ 0, 1, 2, 3, 4, 5 ];
nums.fill( "a", 1 );
nums; //=> [0, "a", "a", "a", "a", "a"]

// 填充部分:fill( target, start, stop )
// 包头不包尾(包含 start 索引的元素,不包含 stop 索引的元素)
let nums = [ 0, 1, 2, 3, 4, 5 ];
nums.fill( "a", 1, 4 );
nums; //=> [0, "a", "a", "a", 4, 5]

2.3. copyWithin() 方法

复制数组内部指定元素 粘贴到特定位置。

let nums = [ 0, 1, 2, 3, 4, 5 ];

// copyWithin( 开始粘贴的位置, 开始复制的位置 )
nums.copyWithin( 2, 0 );
nums; //=> [0, 1, 0, 1, 2, 3]


let nums = [ 0, 1, 2, 3, 4, 5 ];

// copyWithin( 开始粘贴的位置, 开始复制的位置, 停止复制的位置 )
nums.copyWithin( 2, 0, 1 );
nums; //=> [0, 1, 0, 3, 4, 5]

3. 类型化数组

类型化数组只能处理数值类型的数据,为JS提供了快速的按位运算的能力。

对于WebGL的需求来说,JS原生的数学运算是在太慢了,因为它使用64位浮点格式来存储数值,并在必要时将其转换为32位整数。

引入类型化数组突破了格式限制并带来了更好的数学运算性能,其设计概念是:单个数值可以被视为由“位”构成的数组,并且可以对其使用与JS数组现有方法类似的方法。

3.1. 数值数据类型

JS数值使用 IEEE 754 标准格式存储,使用64位来存储一个数值的浮点表示形式,该格式在JS中被同时用来表示整数和浮点数;当值改变时,可能会频繁发生整数与浮点数之间的格式转换。

类型化数组则允许存储并操作八种不同的数值类型:

  1. 8位有符号整数(int8)
  2. 8位无符号整数(uint8)
  3. 16位有符号整数(int16)
  4. 16位无符号整数(uint16)
  5. 32位有符号整数(int32)
  6. 32位无符号整数(uint32)
  7. 64位有符号整数(int64)
  8. 64位无符号整数(uint64)

如果将一个 int8 范围内的数表示为常规的JS数值,
就浪费了56个位。有效利用“位”是类型化数组的应用场景之一。

为了使用那八种数据类型,需要创建一个数组缓冲区来存储数据。

3.2. 数组缓冲区

数组缓冲区(array buffer)是内存中包含一定数量字节的区域,
类型化数组都基于缓冲区。

// 分配了10字节
let buffer = new ArrayBuffer( 10 );

// 获取分配的字节数
buffer.byteLength; //=> 10

// 截取部分,作为新的缓冲区
let buffer2 = buffer.slice(4, 6);
buffer2.byteLength; //=> 2

数组缓存区一旦创建,则不可修改其大小。

为了往数组缓冲区写数据,你需要创建一个视图(views)。

数组缓冲区代表了一块内存区域,而视图(views)则是你操作这块区域的接口。

3.3. 通用视图

DataView 类型是数组缓冲区的通用视图,允许你对前述所有八种数值数据类型进行操作。

在混用不同的数据类型时,使用 DataView 对象是一种完美方式。

// 创建 10 个字节的数组缓冲区
const buffer = new ArrayBuffer(10);

// 创建视图,操作数组缓冲区
// new DataView(buffer, byteOffset, byteLength)
const view = new DataView(buffer);

// 获取视图信息
view.buffer === buffer; // true
view.byteOffset; // 0  : 视图在数组缓冲区的偏移量
view.byteLength; // 10 : 视图的大小

// 写入数据
view.setInt8(0, 1);
view.setUint8(1, 2);

view.setInt32(2, 3);
view.setFloat32(6, 4);

// 读取数据
view.getInt8(0);    // 1
view.getUint8(1);   // 2
view.getInt32(2);   // 3
view.getFloat32(6); // 4

3.4. 类型化数组即为视图

类型化数组只能在特定的一种数据类型上工作

不必先创建数组缓冲区,再在数组缓冲区上创建视图,而是直接创建读写特定类型的视图

3.4.1. 构造器
构造器名称元素大小(字节)描述等价的 C 语 言类型
Int8Array18 位有符号整数,采用补码signed char
Uint8Array18 位无符号整数unsigned char
Uint8ClampedArray18 位无符号整数 (clamped conversion,无溢出转换)unsignedchar
Int16Array216 位有符号整数,采用补码short
Uint16Array216 位无符号整数 unsignedshort
Int32Array432 位有符号整数,采用补码int
Uint32Array432 位无符号整数int
Float32Array432 位 IEEE 浮点数float
Float64Array864 位 IEEE 浮点数double
3.4.2. 查看元素大小(字节数)
Int8Array.BYTES_PER_ELEMENT; // 1
Float64Array.BYTES_PER_ELEMENT; // 8
3.4.3. 创建特定类型视图

方式一, 类似 DataView:

const buffer = new ArrayBuffer(10);

view = new Int8Array(buffer, 0, 10);

方式二, 指定元素数量:

const int16s = new Int16Array(2);

int16s.length;     // 2
int16s.byteLength; // 4

方式三, 将(类型化数组、可迭代对象、数组、类数组)转换 类型化数组:

// 两个数组使用了全然不同的缓冲区
const int8s = new Int8Array([1, 2]);
const float64s = new Float64Array(int8s);

// 读
console.log( int8s[0] );     // 1
console.log( float64s[1] );  // 2

// 写
int8s[0] = 11;
float64s[1] = 22;

console.log( int8s[0] );     // 11
console.log( int8s[1] );     // 2
console.log( float64s[0] );  // 1
console.log( float64s[1] );  // 22

3.5. 类型化数组与常规数组的区别

是类型化数组并不是常规数组,类型化数组并不是从 Array 对象派生的,使用 Array.isArray() 去检测会返回 false

常规数组可以被伸展或是收缩,然而类型化数组却会始终保持自身大小不变

类型化数组还有两个常规数组所不具备的方法( set()、subarray() ):

/*
set(arr, startIndex) : 从另一个数组中复制元素到当前的类型化数组
    arr : 常规数组/类型化数组
    startIndex : 开始位置
*/
const int16s = new Int16Array( 4) ;

int16s.set( [11, 22] );
int16s.set( new Int8Array([33, 44]), 2 );

console.log( int16s.toString() ); // 11,22,33,44


/*
subarray(startIndex, stopIndex) : 并会返回一个新的类型化数组, 类似 slice()
*/

const subInt16s = int16s.subarray( 1, 3 );

console.log( subInt16s.toString() ); // 22,33
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值