1.创建数组
// 1.通过构造函数
const people = new Array(); // []
const people1 = new Array(10); // [empty × 10]
const people2 = new Array('starry','alex'); // ["starry", "alex"]
// 2.对象字面量(与对象一样,使用对象字面量创建数组不会调用数组的构造函数)
const people3 = []; // []
const people4 = ['starry','alex']; // ["starry", "alex"]
const people5 = [,,,,]; // [,,,,]
console.log(people5.length) // 4 不过这些都是站位,值为undefined,执行的时候会跳过
//3.通过转换
// Array 构造函数中 ES6 新增的方法:from() 和 of()
// from() 是将参数(类数组结构的数据,或有length属性和可索引元素的结构)转换成数组实例。
// of() 是将参数转换成数组实例
// 我的理解,区别就是 from 从本质上将数据变成数组,而of只是在最外层套一层[]?
// 参数是字符串
Array.from('formatt'); // ['f','o','r','m','a','t','t'];
Array.of('formatt'); // ['formatt'];
// set
// Map ==== 后面补
// =============================================================== 下面是详细介绍Array.from
// 如果是现有数组,是深拷贝
const a1 = [1,2,3];
const a2 = Array.from(a1);
a1 === a1 // false 引用地址不同
a1[0] = 9;
console.log(a1); // [9,2,3]
console.log(a2); // [1,2,3]
// from() 的第二个参数,是一个可选的映射函数,示例如下
const b1 = [1,2,3];
const b2 = Array.from(b1,el => el*10 );
console.log(b2) // [10,20,30];
2.数组 length
数组的 length 不只是只读的,还可以通过修改 length 来增加或者删除数组最后一个元素,示例如下:
const arr = [1,2,3];
arr.length = 2 ; // 通过修改数组的长度来删除最后一个元素
console.log(arr) // [1,2]
arr[arr.length] = 6; // 往数组最后添加一个元素
console.log(arr) // [1,2,6]
arr[99] = 100;
console.log(arr) // [1,2,6,empty*96,100]
3.检测数组
判断一个对象是不是数组,通常的方法是通过 instanceof 判断是不是 Array 构造函数的实例。
// 判断 value 是不是数组类型
const result = value instanceof Array // true or false
但是这中方法有个缺陷,如果只有一个全局执行上下文,不会有问题,因为只会出现一个版本的 Array 构造函数。但是如果存在多个全局上下文,每个版本的 Array 构造函数不一致,就会出现问题(这种情况我还没遇到过)。ECMAScript 提供了一个不用考虑这个问题的方法,目的就是确定一个值是不是数组的,示例如下:
// 判断 value 是不是数组类型
const result = Array.isArray(value); // true or false
// ===========================
// Array.isArray() 的实现原理
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
// 而且
Array.isArray(Array.prototype); // true
4.迭代器方法
Array 的原型中提供了三个检索内容的方法,keys()、values() 、entries()。
keys()返回数组索引的迭代器。
const str = "starry"
const arr = Array.from(str);
console.log(arr.keys()) // Object [Array Iterator] {} 返回迭代器
values()返回数组元素的迭代器。
const str = "starry"
const arr = Array.from(str);
console.log(arr.values()) // Object [Array Iterator] {} 返回迭代器
entries()返回的是索引/值对的迭代器。
const str = "starry"
const arr = Array.from(str);
console.log(arr.entries()) // Object [Array Iterator] {} 返回迭代器
通过上面示例我们知道,这三个方法返回的都是迭代器,我们并不能直接看到,使用。
迭代器就是一个一次性的对象,用于迭代与其关联的可迭代对象。
我们可以上面提到的 Array.from() 方法将迭代器转换成数组。
Array.from(arr.keys()) // [0, 1, 2, 3, 4, 5]
Array.from(arr.values()) // ["s", "t", "a", "r", "r", "y"]
Array.from(arr.entries())(6) [[0, "s"],[1, "t"],[2, "a"],(2) [3, "r"],[4, "r"],[5, "y"]]
5.数组方法
改变原数组的方法:pop(),push(),shift(),unshift(),reverse(),splice()。
不会改变原数组的方法:slice(),concat(),join(),flat(),find(),filter。
- reduce 按升序执行每一个元素,将结果累计返回。
reduce((累计值,currentValue,当前索引,源数组),initialValue)
// 注:当前索引表示正在处理的数据索引,如果设置 initialValue ,那么索引从 0 开始,如果没有设置,
// 索引默认从 1 开始
const initialValue = 0;
const numbers = [10,20,40,50,60,70,80]
const total = numbers.reduce((sum,current,index,originArray) => {
return item + next
},initialValue);
reduce 应用场景:
- 将二维数组变成一维
const flattened = [[0, 1], [2, 3], [4, 5]]
const out = flattened.reduce((item,current) => item.concat(current),[]);
// [0, 1, 2, 3, 4, 5] 'out'
- 统计数组中元素出现的次数
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
const out = names.reduce((allNames,current) => {
if(current in allNames){
allNames[current] ++ ;
}else {
allNames[current] = 1
}
return allNames;
},{});
// 注意初始值设置成 {}
// {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
- 按顺序执行 promise
const p1 = num => new Promise((resolve, reject) => {
resolve(num * 10)
})
const p2 = num => new Promise((resolve, reject) => {
resolve(num * 20)
})
const p3 = num => new Promise((resolve, reject) => {
resolve(num * 30)
})
const p4 = num => new Promise((resolve, reject) => {
resolve(num * 40)
})
const promiseArr = [p1,p2,p3,p4];
// 此时第一参数表示的就是上一个 promise 了
const out = promiseArr.reduce((beforePromise,current) => {
console.log(beforePromise,'beforePromise');
return beforePromise.then(current);
},Promise.resolve(10));
out.then(console.log)
- splice 删除,替换或者添加元素,返回的是被修改的内容,此方法会改变原数组。
splice 使用场景:
// 函数签名
splice(start,deleteCount,要替换的内容)
- slice 截取字符串,返回一个新的数组,不会改变原数组。
slice 使用场景:
// 函数签名
slice(start,end) // 包含 start 不包含 end
// 另外一个用法,用来把类数组转换成数组,作用同 Array.from()
// arguments 为例
const arr = Array.prototype.slice.call(arguments)
find 使用场景:
返回数组中满足测试函数的第一个元素的值,否则返回 undefined
所以可以用来查找
arr.find(函数)
filter 使用场景:
filter() 方法创建给定数组一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素。
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
const filePaths = files
.map(file => file.trim())
.filter(Boolean)
.map(fileName => `~/cool_app/${fileName}`);
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
// filter(Boolean)
// 等价于 filter((x)=>Boolean(x))
flat 使用场景:
用来扁平化数组,返回一个新的数组。
flat()
// 提取嵌套数组的深度,默认是 1 ,
flat(depth)
// 可以提取嵌套任意深度的数组
flat(Infinity)
6.数组方法分类
1.会改变原数组的方法
push()
pop()
unshift() //往数组前面添加数据
shift() //往数组前面弹出数据
reverse()
sort()
splice()
2.不会改变原数组方法
concat()
slice()
reduce()
join()
map()
filter
ES6 结构赋值
const colors = ['green', 'red', 'pink']
const newColors = [...colors]
colors[0] = 'black'
// colors:['black', 'red', 'pink']
// newColors:['green', 'red', 'pink']
7.Array.of() 和 Array.from()
Array 构造函数中 ES6 新增的方法:from() 和 of()
from() 是将参数(类数组结构的数据,或有length属性和可索引元素的结构)转换成数组实例。前提是类数组
of() 是将参数转换成数组实例,解决 new Array() 行为不统一的问题
// 解决 new Array() 行为不统一的问题
new Array(2) // [empty × 2]
new Array('abc') // ['abc']
Array.of(2) // [2]
Array.of('abc') // ['abc']
8.数组的存储方式
从 Chorme 的源码来看,js 中的数组 JSArray 是继承自 JSObject 的,所以可以把数组看作是特殊的对象,内部也是以 key-value 的方式进行存储的,所以可以存储不同类型的数据。因此,数组的存储方式分为两种:快数组和慢数组。
快数组(fast):存储结构是 FixedArray ,数组长度<= elements.length,push 或者 pop可能会引起扩容和减容。
慢数组(slow):存储结构是 HashTable,数组下标作为 key。
1.快数组
FixedArray 是 V8 中实现的一个类似数组的类,表示一段连续的内存,可以使用索引直接定位。新创建的空数组默认就是快数组。当执行 push 操作时,如果数组满的时候会 JSArray 会进行动态扩容。是以时间换空间。
动态扩容
Chorme 源码中 push 的操作是使用 c++ 内嵌汇编实现的。实现过程如下:
- 执行 push 时,发现内存不足
- 申请内存,申请内存大小的计算规则 new_capacity = old_capacity /2 + old_capacity + 16(原内存大小的1.5倍+16) .
- 将数组复制到新的内存中。
- 将新元素放到 length + 1的位置
- length + 1
- 返回length
动态减容
c++实现,步骤如下:
- 执行 pop,获取数组长度,获取 length+1 的元素,length-1
- 数组容量是否大于等于 length - 1 的2倍
- 是的话,计算释放空间的大小,做好标记等待 gc 回收
- 不是的话用 holes 对象填充
- 返回被删除的元素
2.慢数组
慢数组是以哈希表的形式保存在内存中,它不需要连续的存储空间,节省了内存,但是需要维护一个哈希表。与快数组相比,性能较差。是以时间换空间。