2019-8-2
第十章 增强的数组功能
(一)创建数组
ES5创建数组有两种方式:Array构造器和数组字面量
ES6新增了两种方式:Array.of() 和 Array.from()
1. Array.of()
Array.of():解决了使用Array构造器创建数组参数数量和类型不同时导致的不同结果的问题。Array.of()总会创建一个包含全部传入参数的数组,而不管参数数量和类型。
var a = new Array(1);
console.log(a[0]); // undefined
console.log(a.length); // 1
var b = Array.of(1);
console.log(b[0]); // 1
console.log(b.length); // 1
在向函数传递参数时,使用Array.of()而不是Array来确保行为一致,如:
let value = 1;
function CreateArray(arrayCreator, value){
return arrayCreator(value);
}
// 如果传递的是Array构造器,就会创建出一个长度为1,值为undefined的数组,与预期行为不一致
let items = CreateArray(Array.of, value);
console.log(items); //[1]
Array.of()没有使用Symbol.species属性来决定返回值的类型,而是使用了当前的构造器(即of()方法内部的this)来做决定。
class MyArray extends Array{
static get [Symbol.species]() {
return Array;
}
}
let items = MyArray.of(1,2)
console.log(items instanceof MyArray); // true
console.log(items instanceof Array); // true
console.log(items);
2. Array.from()
Array.from():解决了将类数组对象和可迭代对象转换为数组的问题。
类数组对象:要有length属性;
可迭代对象:要有Symbol.iterator属性。
如果一个对象既是可迭代对象又是类数组对象,Array.from()方法会使用迭代器来决定需要转换的值。
ES5中将一个类数组对象转换为数组:
// 法1
function makeArray1(arrayLike) {
var result = [];
for(var i = 0; i < arrayLike.length; i++) {
result.push(arrayLike[i]);
}
return result;
}
function doSomething() {
var args = makeArray1(arguments);
// 使用args
}
// 法2:利用slice()方法特性:只需要有数值类型的索引和长度属性就能正常工作
function makeArray2(arrayLike) {
return Array.prototype.slice.call(arrayLike); // 不能明确体现出"要将类数组对象转换为数组"的目的
}
ES6:
Array.from(obj, function, thisObj);
Obj — 要转换的对象;
function — 类似数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组;
thisObj — 指定第二个参数的函数里用到的this关键字绑定
function doSomething() {
var args = Array.from(arguments);
// 使用args
}
function translate() {
return Array.from(arguments, (value)=> value+1);
}
let nums = translate(1,2,3)
console.log(nums); // [2,3,4]
function translate2() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate2(1,2,3);
console.log(numbers); // [2,3,4]
Array.from()在可迭代对象上的应用:
let numbers = {
*[Symbol.iterator](){
yield 1;
yield 2;
yield 3;
}
};
let numbers2 = Array.from(numbers, (value) => value + 1);
console.log('numbers2: ', numbers2); // numbers2: [2, 3, 4
(二)数组上的新方法
1.find()和findIndex()
ES5检索数组的方法:indexOf() 和 lastIndexOf(), 缺点:若想在一系列数值中间查找第一个偶数,必须自己写代码来实现。
find和findIndex唯一的区别是:find()方法返回匹配的值(若没有则返回undefined),findIndex()返回匹配位置的索引(若没有则返回-1)。
array.find(function(currentValue, index, arr),thisValue)
currentValue -- 必须,当前元素
index -- 可选,当前元素的索引值
arr -- 可选,当前元素所属的数组对象
thisValue --可选,回调函数中的this值
find和findIndex会在回调函数第一次返回true时停止执行
let numbers = [2, 35, 32, 23, 53];
console.log(numbers.find(item => item > 33)); // 35
console.log(numbers.findIndex(item => item > 33)); // 1
查找满足特定条件的数组元素,使用find()和findIndex();
查找特定值,使用indexOf() 和 lastIndexOf()
2. fill()
fill():使用特定值填充数组中的部分或全部元素,当只使用一个参数时,会用该参数的值填充整个数组。
array.fill(value, start, end)
value -- 必须,填充的值
start -- 可选,开始填充的位置,如果为负值,表示倒数。
end -- 可选,停止填充的位置(默认为array.length),开区间
let numbers = [1,2,3,4,5];
numbers.fill(1);
console.log('numbers: ', numbers); // [1,1,1,1,1]
numbers.fill(2,2);
console.log('numbers: ', numbers); // [1,1,2,2,2]
numbers.fill(3,1,3);
console.log('numbers: ', numbers); //[1,3,3,2,2]
numbers.fill(-1);
console.log('numbers: ', numbers); //[-1, -1, -1, -1, -1]
numbers.fill(2, -1);
console.log('numbers: ', numbers); //[-1, -1, -1, -1, 2]
numbers.fill(3, -4, -1);
console.log('numbers: ', numbers); //[-1, 3, 3, 3, 2]
3. copyWithin()
copyWithin():从数组的指定位置拷贝元素到数组的另一个指定位置中。
array.copyWithin(target, start, end)
| 参数 | 描述 |
|---|---|
| target | 必需。复制到指定目标索引位置。 |
| start | 可选。元素复制的起始位置。(默认为0) |
| end | 可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。 |
let numbers = [1,2,3,4,5];
numbers.copyWithin(2);
console.log('numbers: ', numbers); // [1,2,1,2,3]
numbers = [1,2,3,4,5];
numbers.copyWithin(3,0);
console.log('numbers: ', numbers); // [1,2,3,1,2]
numbers = [1,2,3,4,5];
numbers.copyWithin(2,1,3);
console.log('numbers: ', numbers); // [1,2,2,3,5]
(三)类型化数组
1. 数组缓冲区和视图
数组缓冲区:array buffer是内存中包含一定数量字节的区域。使用ArrayBuffer构造器创建数组缓冲区;
DataView:视图,操作数组缓冲区
new DataView(buffer, byteOffset, byteLength);
-- buffer :该视图所绑定的数组缓冲区
-- byteOffset: 字节偏移量
-- byteLength: 字节数
let buffer = new ArrayBuffer(10); // 分配了10个字节
console.log(buffer.byteLength); //10
//使用slice()方法创建一个包含已有缓冲区部分内容的新数组缓冲区
let buffer2 = buffer.slice(4,6);
console.log(buffer2.byteLength); //2
//使用视图view操作数组缓冲区
let view1 = new DataView(buffer), // 包含所有字节
view2 = new DataView(buffer, 5, 2); //包含位置5和位置6的字节
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
读写数据:get和set方法,
读写整数:以int8和uint8为例(可将8换成16,32)
getInt8(byteOffset)- Read an int8 starting atbyteOffsetsetInt8(byteOffset, value)- Write an int8 starting atbyteOffsetgetUint8(byteOffset)- Read an uint8 starting atbyteOffsetsetUint8(byteOffset, value)- Write an uint8 starting atbyteOffset
读写浮点数:
getFloat32(byteOffset, littleEndian)- Read a float32 starting atbyteOffsetsetFloat32(byteOffset, value, littleEndian)- Write a float32 starting atbyteOffsetgetFloat64(byteOffset, littleEndian)- Read a float64 starting atbyteOffsetsetFloat64(byteOffset, value, littleEndian)- Write a float64 starting atbyteOffset
比整数多了个参数littleEndian,来表示是否采用小端模式,默认值为false
let buffer = new ArrayBuffer(10),
view = new DataView(buffer);
view.setInt8(0,5);
view.setInt8(1,-1);
console.log(view.getInt8(0)); // 5
console.log(view.getInt8(1)); // -1
//视图允许使用任意格式对任意位置进行读写,而无需考虑这些数据此前使用的是什么格式存储的
console.log(view.getInt16(0)); // 1535
new ArrayBuffer(2) 00000000 00000000
view.setInt8(0, 5); 00000101 00000000
view.setInt8(1, -1); 00000101 11111111
2. 类型化数组
类型化数组即视图,可以使用类型化数组来处理特定的数据类型,而不必使用通用的DataView对象,类型化数组只能在特定的一种数据类型上工作,如Int8Array的所有操作都只能处理int8值。
BYTES_PER_ELEMENT属性:表示类型化数组的元素大小
类型化数组的构造器可接受多种类型的参数,所以创建类型化数组的方式也有多种:
- 同DataView,传递buffer, byteOffset, byteLength
let buffer = new ArrayBuffer(10),
view1 = new Int8Array(buffer),
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
- 同数组Array构造器,传递单个数值参数,表示该数组包含的元素数量(而不是分配的字节数),可用length获取这个元素的数量
let ints = new Int8Array(2),
floats = new Float32Array(5);
console.log(ints.byteLength); // 2 byteLength = length * BYTES_PER_ELEMENT
console.log(ints.length); // 2
console.log(ints.BYTES_PER_ELEMENT); // 1
console.log(floats.byteLength); // 20 byteLength = length * BYTES_PER_ELEMENT
console.log(floats.length); // 5
console.log(floats.BYTES_PER_ELEMENT); //4
- 传递单个对象(类型化数组,可迭代对象, 数组, 类数组对象)参数
let ints1 = new Int16Array([25, 50]),
ints2 = new Int32Array(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.log(ints2.byteLength); // 8
console.log(ints2.length); // 2
console.log(ints2[0]); // 25
console.log(ints2[1]); // 50
(四)类型化数组与常规数组的相似点
-
都可以使用length属性获取元素数量;(注意:类型化数组的length值是不可写的,非严格模式下写入操作会被忽略,严格模式下回抛出错误)
-
都可以使用数值类型的索引值来直接访问元素;
-
公共方法:
copyWithin()entries()fill()filter()find()findIndex()forEach()indexOf()join()keys()lastIndexOf()map()reduce()reduceRight()reverse()slice()some()sort()values()
类型化数组的方法会进行额外的类型检查,并且返回值会根据
Symbol.species属性来确定,会是某种类型化数组,而不是常规数组。- 相同的迭代器:entries(), keys(), values(),可以对类型化数组使用扩展运算符(…)或者for-of循环
of()和from()方法:所有类型化数组都包含静态的of()和from()方法,作用类似于Array.of()和Array.from()方法
let ints = Int16Array.of(1,2,3,4); console.log(ints instanceof Int16Array); console.log(ints.length); console.log(ints[0]); console.log(ints[1]);let ints = Int16Array.from({ 0: 1, 1: 2, length: 2 }) console.log(ints[0]); console.log(ints[1]);
(五)类型化数组与常规数组的区别
- 最大区别:类型书数组不是常规数组,不是从
Array对象派生的,使用Array.isArray()会返回false
let ints = Int16Array.of(1,2,3,'4','h');
console.log(ints instanceof Array); // false
console.log(Array.isArray(ints)); // false
- 类型化数组大小固定不变,不能通过改变length值改变,也不能通过给一个大于长度的索引值赋值来改变大小;
let ints = Int16Array.of(1,2,3,4);
ints[4] = 5;
console.log(ints[4]); // undefined
- 类型化数组会对数据类型进行检查以保证只使用有效的值,当传入无效值时,会被替换为0;
let ints = Int16Array.of(1,2,3,'4','h');
console.log(ints[3]); // 4
console.log(ints[4]); // 0
-
遗漏的方法: 会改变数组大小的方法:
pop(),push(),shift(),unshift(),splice()以及
concat(),因为连接两个类型化数组的结果是不确定的。 -
增加的方法:
set(array, start) — 将其他数组中的元素复制到当前的类型化数组。
subarray(start, end) — 将当前类型化数组的部分元素提取到新的类型化数组,类似slice()方法
let ints = new Int16Array(4);
ints.set([1,2]);
console.log(ints); // [1,2,0,0]
ints.set([3,4],1);
console.log(ints); // [1,3,4,0]
let ints = new Int16Array(4);
ints.set([1,2,3,4]);
let ints2 = ints.subarray();
console.log(ints2); // [1,2,3,4]
ints2 = ints.subarray(2);
console.log(ints2); // [3,4]
ints2 = ints.subarray(1,3);
console.log(ints2); //[2,3]