本文以《JavaScript高级程序设计》第4版作为基础参考,整理使用JavaScript开发过程中,数组使用相关的知识点。
本文是开发知识点系列第十一篇。
- 第一篇:JavaScript开发中变量、常量声明的规矩总结
- 第二篇:JavaScript开发:数据类型知识总结
- 第三篇:JavaScript开发:使用Number数据类型需要注意的问题
- 第四篇:JavaScript开发:操作符在实际开发中的使用总结
- 第五篇:JavaScript开发:流程控制语句在实际开发中的使用总结
- 第六篇:JavaScript开发:函数在实际开发中的使用总结(1)
- 第七篇:JavaScript开发:日期对象在开发中的使用总结
- 第八篇:JavaScript开发:正则表达式在开发中的使用总结
- 第九篇:JavaScript开发:函数在实际开发中的使用总结(2)
- 第十篇:JavaScript开发:字符串数据在开发中的使用总结
除了Object,Array是ECMAScript中最常用的类型。数组是一组有序的数据,可以存储任何类型的数据。同时数组是数据结构栈、队列、链表的基础。
创建数组
在JavaScript中,创建数组的方式有多种
- 使用Array构造函数
javascript
let arr1 = new Array();
console.log(arr1); // 输出: []
let arr2 = new Array('Apple', 'Banana', 'Cherry');
console.log(arr2); // 输出: ['Apple', 'Banana', 'Cherry']
let arr3 = new Array(10);
console.log(arr3); // 输出: [空属性 × 10]
- 使用数组字面量
javascript
let arr4 = [];
console.log(arr4); // 输出: []
let arr5 = ['Apple', 'Banana', 'Cherry'];
console.log(arr5); // 输出: ['Apple', 'Banana', 'Cherry']
- 使用
Array.of
方法
javascript
let arr6 = Array.of('Apple', 'Banana', 'Cherry');
console.log(arr6); // 输出: ['Apple', 'Banana', 'Cherry']
- 使用
Array.from
方法
javascript
let arr7 = Array.from('Apple');
console.log(arr7); // 输出: ['A', 'p', 'p', 'l', 'e']
数组空位
使用数组字面量初始化数组时,可以使用逗号创建空位
js
const options = [,,,] // 创建包含3个元素的数组
conosle.log(options.length) // 5
conosle.log(options) // [空属性 × 5]
ES6新增的方法和迭代器与早期的ECMAScript版本中的方法行为不同。ES6新增方法普遍将这些空位当成存在的元素,只不过值为undefined
javascript
let arr = [1, , , 4, 5];
for (let value of arr) {
console.log(value);
}
// 输出:
// 1
// undefined
// undefined
// 4
// 5
ES6之前的方法则会忽略这个空位,但具体的行为会因方法而异。
当使用map
遍历这个数组时,空位也会被跳过,但会保留在新数组中
javascript
let arr = [1, , , 4, 5];
let newArr = arr.map(value => value * 2);
console.log(newArr);
// 输出: [2, 空属性 × 2, 8, 10]
当使用join
拼接这个数组时,空位会被当作空字符串处理
javascript
let arr = [1, , , 4, 5];
let str = arr.join('-');
console.log(str);
// 输出: '1---4-5'
类数组
类数组(Array-like)对象是一种拥有length属性和索引元素,但并不具有数组所拥有的全部方法的对象。
常见的类数组对象有:函数的arguments对象,DOM方法返回的结果(如document.getElementsByTagName()),字符串等。
一个类数组对象的例子
javascript
let arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
console.log(arrayLike[0]); // 输出: 'a'
console.log(arrayLike.length); // 输出: 3
看起来像一个数组,但它并不是一个真正的数组,因为它没有数组的方法,如push、pop等。
如果想将一个类数组对象转换为一个真正的数组,可以使用Array.from()
方法或者扩展运算符(...
)
javascript
let array = Array.from(arrayLike);
// 或者
let array = [...arrayLike];
数组索引
数组索引是从0开始的数字,用于访问和操作数组的元素。可以使用方括号语法来获取或设置数组元素的值
javascript
// 创建一个数组
let fruits = ['Apple', 'Banana', 'Cherry'];
// 使用索引获取数组元素
console.log(fruits[0]); // 输出: 'Apple'
console.log(fruits[1]); // 输出: 'Banana'
console.log(fruits[2]); // 输出: 'Cherry'
// 使用索引设置数组元素的值
fruits[1] = 'Blueberry';
console.log(fruits); // 输出: ['Apple', 'Blueberry', 'Cherry']
// 使用索引添加新的数组元素
fruits[4] = 'Durian';
console.log(fruits); // 输出: ['Apple', 'Blueberry', 'Cherry', 空白, 'Durian']
// 使用索引删除数组元素
delete fruits[1];
console.log(fruits); // 输出: ['Apple', 空白, 'Cherry', 空白, 'Durian']
需要注意的是,如果使用一个大于或等于数组长度的索引来设置元素,数组的长度会自动增加到索引值加一。同时,如果删除一个元素,数组的长度不会改变,对应的索引位置会变成空位。
Array构造函数静态方法
Array构造函数有以下几个静态方法
- Array.from():从类数组对象或可迭代对象创建一个新的数组实例
javascript
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
let arr1 = Array.from(arrayLike);
console.log(arr1); // 输出: ['a', 'b']
let set = new Set(['foo', 'bar', 'baz', 'foo']);
let arr2 = Array.from(set);
console.log(arr2); // 输出: ['foo', 'bar', 'baz']
- Array.isArray():判断某个变量是否是数组。
javascript
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // 输出: true
let notArr = 'hello';
console.log(Array.isArray(notArr)); // 输出: false
- Array.of():创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
javascript
let arr1 = Array.of(1, 2, 3);
console.log(arr1); // 输出: [1, 2, 3]
let arr2 = Array.of(undefined);
console.log(arr2); // 输出: [undefined]
数组检测
检测一个变量是否为数组,可以使用以下几种方法
- 使用
Array.isArray()
方法。这是最推荐的方法,因为它对所有的数组都能返回正确的结果
javascript
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // 输出: true
let notArr = 'hello';
console.log(Array.isArray(notArr)); // 输出: false
- 使用
instanceof
操作符
javascript
let arr = [1, 2, 3];
console.log(arr instanceof Array); // 输出: true
let notArr = 'hello';
console.log(notArr instanceof Array); // 输出: false
- 使用
Object.prototype.toString
方法。这个方法对所有的数组都能返回正确的结果,但使用起来比较麻烦
javascript
let arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // 输出: true
let notArr = 'hello';
console.log(Object.prototype.toString.call(notArr) === '[object Array]'); // 输出: false
关于数据类型判断我写过一篇文章:JavaScript开发:数据类型知识总结
数组属性
数组有以下几个常用的属性
- length:数组的长度,表示数组中元素的数量。可以通过读取这个属性获取数组的长度,也可以通过设置这个属性来改变数组的长度
javascript
let arr = [1, 2, 3];
console.log(arr.length); // 输出: 3
arr.length = 2;
console.log(arr); // 输出: [1, 2]
arr.length = 5;
console.log(arr); // 输出: [1, 2, 空属性 × 3]
- constructor:指向数组的构造函数,即Array
javascript
let arr = [1, 2, 3];
console.log(arr.constructor === Array); // 输出: true
- prototype:允许向数组添加属性和方法
javascript
Array.prototype.myMethod = function() {
console.log('Hello, world!');
};
let arr = [1, 2, 3];
arr.myMethod(); // 输出: 'Hello, world!'
除了length
属性外,其它属性在日常编程中使用的不多。
数组方法
迭代器方法
keys()
, values()
, entries()
是ES6引入的新的数组迭代器方法,它们分别返回一个新的Array Iterator对象,包含数组的键、值、键值对。
keys()
:返回一个新的Array Iterator对象,包含数组中每个索引的键
javascript
let arr = ['a', 'b', 'c'];
let iterator = arr.keys();
for (let key of iterator) {
console.log(key); // 输出: 0 1 2
}
values()
:返回一个新的Array Iterator对象,包含数组中每个索引的值
javascript
let arr = ['a', 'b', 'c'];
let iterator = arr.values();
for (let value of iterator) {
console.log(value); // 输出: 'a' 'b' 'c'
}
entries()
:返回一个新的Array Iterator对象,包含数组中每个索引的键/值对
javascript
let arr = ['a', 'b', 'c'];
let iterator = arr.entries();
for (let entry of iterator) {
console.log(entry); // 输出: [0, 'a'] [1, 'b'] [2, 'c']
}
复制和填充方法
copyWithin()
和fill()
都是es6
新增方法。
copyWithin()
copyWithin()
方法可以接受三个参数:
- target(必需):复制到的目标索引位置。
- start(可选):元素复制的起始位置。如果被忽略,copyWithin将会从0开始复制。
- end(可选):元素复制的结束位置。如果被忽略,copyWithin方法将会一直复制到数组结尾。
这个方法会改变原数组,并返回修改后的数组。
一些使用copyWithin()
方法的例子
javascript
let arr = [1, 2, 3, 4, 5];
// 从索引1开始的元素替换为从索引3开始到数组末尾的元素
arr.copyWithin(1, 3);
console.log(arr); // 输出: [1, 4, 5, 4, 5]
arr = [1, 2, 3, 4, 5];
// 从索引0开始的元素替换为从索引2开始到索引4的元素
arr.copyWithin(0, 2, 4);
console.log(arr); // 输出: [3, 4, 3, 4, 5]
在第一个例子中,copyWithin()
方法将从索引3开始到数组末尾的元素[4, 5]复制到了从索引1开始的位置,所以数组变成了[1, 4, 5, 4, 5]。
在第二个例子中,copyWithin()
方法将从索引2开始到索引4的元素[3, 4]复制到了从索引0开始的位置,所以数组变成了[3, 4, 3, 4, 5]。
fill()
fill()
方法可以接受三个参数:
- value(必需):用来填充数组元素的值。
- start(可选):开始填充的起始位置。如果被忽略,
fill()
将会从0开始填充。 - end(可选):停止填充的结束位置。如果被忽略,
fill()
方法将会一直填充到数组结尾。
这个方法也会改变原数组,并返回修改后的数组。
以下是一些使用fill()
方法例子
javascript
let arr = [1, 2, 3, 4, 5];
// 将数组所有元素都填充为'a'
arr.fill('a');
console.log(arr); // 输出: ['a', 'a', 'a', 'a', 'a']
arr = [1, 2, 3, 4, 5];
// 将从索引1开始到数组末尾的元素都填充为'b'
arr.fill('b', 1);
console.log(arr); // 输出: [1, 'b', 'b', 'b', 'b']
arr = [1, 2, 3, 4, 5];
// 将从索引1开始到索引3的元素都填充为'c'
arr.fill('c', 1, 3);
console.log(arr); // 输出: [1, 'c', 'c', 4, 5]
在第一个例子中,fill()
方法将数组所有元素都填充为'a',所以数组变成了['a', 'a', 'a', 'a', 'a']。
在第二个例子中,fill()
方法将从索引1开始到数组末尾的元素都填充为'b',所以数组变成了[1, 'b', 'b', 'b', 'b']。
在第三个例子中,fill()
方法将从索引1开始到索引3的元素都填充为'c',所以数组变成了[1, 'c', 'c', 4, 5]。
copyWithin()
和fill()
这两个方法都是用来修改数组的,但它们的作用和使用方式是不同的。
copyWithin()
方法是用数组中的一段元素去替换数组中的另一段元素, fill()
方法是用一个固定的值去填充数组中的一段元素。copyWithin()
和fill()
的主要区别在于copyWithin()
是用数组自身的元素去修改数组,而fill()
是用一个固定的值去修改数组。
转换方法
所有对象都有toLocaleString()
、toString()
、valueof()
方法。因为数组也是对象,所以数组也具有这三种方法,继承自对象。
其中valueOf()
返回的是数组本身。toString()
返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。调用toLocaleString()也返回一个逗号分隔的数组值的字符串。
但toString()
和toLocaleString()
是有区别的,如果数组是一个对象数组,下面例子可以看出不同
js
const p1 = {
toString(){
return 1
},
toLocaleString(){
return 2
}
}
const p2 = {
toString(){
return 3
},
toLocaleString(){
return 4
}
}
const arr = [p1, p2]
console.log(arr.toString()) // 1,3
console.log(arr.toLocaleString()) // 2,4
栈方法
数组的栈方法主要有两个:push()
和pop()
。
push()
:会将一个或多个元素添加到数组的末尾,并返回新的数组长度
javascript
let arr = [1, 2, 3];
let newLength = arr.push(4, 5);
console.log(arr); // 输出: [1, 2, 3, 4, 5]
console.log(newLength); // 输出: 5
pop()
:会删除并返回数组的最后一个元素
javascript
let arr = [1, 2, 3];
let lastElement = arr.pop();
console.log(arr); // 输出: [1, 2]
console.log(lastElement); // 输出: 3
队列方法
数组的队列方法主要有两个:shift()
和unshift()
。
shift()
:会删除并返回数组的第一个元素
javascript
let arr = [1, 2, 3];
let firstElement = arr.shift();
console.log(arr); // 输出: [2, 3]
console.log(firstElement); // 输出: 1
unshift()
:会将一个或多个元素添加到数组的开头,并返回新的数组长度
javascript
let arr = [1, 2, 3];
let newLength = arr.unshift(0);
console.log(arr); // 输出: [0, 1, 2, 3]
console.log(newLength); // 输出: 4
排序方法
数组的排序方法主要有两个:sort()
和reverse()
。
sort()
:会原地对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点
javascript
let arr = [1, 10, 5, 2];
arr.sort();
console.log(arr); // 输出: [1, 10, 2, 5]
如果想按照数值大小进行排序,需要提供一个比较函数
javascript
let arr = [1, 10, 5, 2];
arr.sort((a, b) => a - b);
console.log(arr); // 输出: [1, 2, 5, 10]
reverse()
:会原地颠倒数组中元素的顺序,并返回数组
javascript
let arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr); // 输出: [5, 4, 3, 2, 1]
操作方法
介绍三个:splice()
、slice()
、concat()
splice()
:在任意的位置给数组添加或删除任意个元素
javascript
let arr = [1, 2, 3, 4, 5];
arr.splice(2, 0, 'a', 'b');
console.log(arr); // 输出: [1, 2, 'a', 'b', 3, 4, 5]
slice()
:返回一个新数组,包含从开始到结束(不包括结束)选择的数组的一部分浅拷贝
javascript
let arr = [1, 2, 3, 4, 5];
let newArr = arr.slice(1, 3);
console.log(newArr); // 输出: [2, 3]
concat()
:用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
javascript
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let newArr = arr1.concat(arr2);
console.log(newArr); // 输出: [1, 2, 3, 4, 5, 6]
搜索和位置方法
数组有几个常用的搜索和位置方法,包括indexOf
、lastIndexOf
、find
、findIndex
、includes
等。
indexOf()
:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1
javascript
let arr = [1, 2, 3, 2, 1];
console.log(arr.indexOf(2)); // 输出: 1
console.log(arr.indexOf(4)); // 输出: -1
lastIndexOf()
:返回在数组中可以找到一个给定元素的最后一个索引,如果不存在,则返回-1
javascript
let arr = [1, 2, 3, 2, 1];
console.log(arr.lastIndexOf(2)); // 输出: 3
console.log(arr.lastIndexOf(4)); // 输出: -1
find()
:返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined
javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.find(value => value > 3)); // 输出: 4
console.log(arr.find(value => value > 5)); // 输出: undefined
findIndex()
:返回数组中满足提供的测试函数的第一个元素的索引。否则返回 -1
javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.findIndex(value => value > 3)); // 输出: 3
console.log(arr.findIndex(value => value > 5)); // 输出: -1
includes()
:判断一个数组是否包含一个指定的值,如果是返回 true,否则false
javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // 输出: true
console.log(arr.includes(6)); // 输出: false
迭代方法
数组有几个常用的迭代方法,包括forEach
、map
、filter
、some
、every
等。
forEach()
:对数组的每个元素执行一次给定的函数
javascript
let arr = [1, 2, 3];
arr.forEach((value, index) => {
console.log(`Element ${index} is ${value}`);
});
// 输出:
// Element 0 is 1
// Element 1 is 2
// Element 2 is 3
map()
:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
javascript
let arr = [1, 2, 3];
let newArr = arr.map(value => value * 2);
console.log(newArr); // 输出: [2, 4, 6]
filter()
:创建一个新数组,包含通过测试的所有元素
javascript
let arr = [1, 2, 3, 4, 5];
let newArr = arr.filter(value => value % 2 === 0);
console.log(newArr); // 输出: [2, 4]
some()
:测试数组中是否至少有一个元素通过了被提供的函数测试
javascript
let arr = [1, 2, 3, 4, 5];
let hasEvenNumber = arr.some(value => value % 2 === 0);
console.log(hasEvenNumber); // 输出: true
every()
:测试数组的所有元素是否都通过了指定函数的测试
javascript
let arr = [2, 4, 6, 8, 10];
let areAllEvenNumbers = arr.every(value => value % 2 === 0);
console.log(areAllEvenNumbers); // 输出: true
归并方法
reduce()
和reduceRight()
数组的归并方法有reduce()
、reduceRight()
reduce()
方法对数组中的每个元素执行一个提供的reducer函数(升序执行),将其结果汇总为单个返回值。
reduce()
接受两个参数:一个回调函数和一个初始值。
回调函数接受四个参数:累计器(accumulator
)、当前值(currentValue
)、当前索引(currentIndex
)和源数组(array
)。
一些使用reduce()
方法的例子
- 计算数组所有元素的和
javascript
let arr = [1, 2, 3, 4, 5];
let sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出: 15
- 计算数组中每个元素出现的次数
javascript
let arr = ['a', 'b', 'a', 'c', 'b', 'a'];
let counts = arr.reduce((accumulator, currentValue) => {
if (currentValue in accumulator) {
accumulator[currentValue]++;
} else {
accumulator[currentValue] = 1;
}
return accumulator;
}, {});
console.log(counts); // 输出: { a: 3, b: 2, c: 1 }
reduceRight()
方法和reduce()
方法非常相似,都是对数组中的每个元素执行一个reducer
函数,将其结果汇总为单个返回值。不同的是,reduceRight()
从数组的末尾向前应用reducer
函数,而reduce()
则是从数组的开头开始。
reduceRight()
接受两个参数:一个回调函数和一个初始值。
回调函数也接受四个参数:累计器(accumulator
)、当前值(currentValue
)、当前索引(currentIndex
)和源数组(array
)。
使用reduceRight()
方法
javascript
let arr = ['a', 'b', 'c', 'd', 'e'];
let str = arr.reduceRight((accumulator, currentValue) => accumulator + currentValue, '');
console.log(str); // 输出: 'edcba'
reduce()
高级用法
- 计算数组中每个元素出现的次数
javascript
let arr = ['a', 'b', 'a', 'c', 'b', 'a'];
let counts = arr.reduce((accumulator, currentValue) => {
if (currentValue in accumulator) {
accumulator[currentValue]++;
} else {
accumulator[currentValue] = 1;
}
return accumulator;
}, {});
console.log(counts); // 输出: { a: 3, b: 2, c: 1 }
- 将二维数组扁平化
javascript
let arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let flatArr = arr.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
console.log(flatArr); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
- 数组去重
js
let arr = [1, 2, 3, 2, 1];
let uniqueArr = arr.reduce((accumulator, currentValue) => {
if (!accumulator.includes(currentValue)) {
accumulator.push(currentValue);
}
return accumulator;
}, []);
console.log(uniqueArr); // 输出: [1, 2, 3]
- 保证Promise顺序执行
js
// 假设我们有三个异步任务,它们分别在1秒、2秒和3秒后解析
let task1 = () => new Promise(resolve => setTimeout(() => resolve('task1'), 1000));
let task2 = () => new Promise(resolve => setTimeout(() => resolve('task2'), 2000));
let task3 = () => new Promise(resolve => setTimeout(() => resolve('task3'), 3000));
let tasks = [task1, task2, task3];
tasks.reduce((promise, task) => {
return promise.then(result => {
return task().then(taskResult => result.concat(taskResult));
});
}, Promise.resolve([])).then(arrayOfResults => {
// 所有任务都完成了,arrayOfResults是它们的结果
console.log(arrayOfResults); // 输出: ['task1', 'task2', 'task3']
});
reduce()
方法需要提供一个回调函数和一个初始值。如果没有提供初始值,reduce()
会将数组的第一个元素作为初始值,并从第二个元素开始迭代。这可能会导致意外的结果,特别是当数组为空或只有一个元素时。
当然reduce()
还有其它高级用法。这是只是举例,主要还是要多总结。
二进制数组
二进制数组由以下三类对象组成
-
ArrayBuffer
:ArrayBuffer对象用于表示一段固定长度的原始二进制数据缓冲区。可以通过"视图"进行操作。 -
TypedArray
:TypedArray对象表示一个底层的二进制数据缓冲区的一个类数组视图。包括Int8Array
、Uint8Array
、Uint8ClampedArray
、Int16Array
、Uint16Array
、Int32Array
、Uint32Array
、Float32Array
、Float64Array
等。 -
DataView
:DataView对象提供了一个数据缓冲区的低级别的接口,可以通过它来读写多种二进制数据类型。
使用这三类对象的例子
javascript
// 创建一个长度为16字节的ArrayBuffer
let buffer = new ArrayBuffer(16);
// 创建一个视图,用于操作这个ArrayBuffer
let int32View = new Int32Array(buffer);
// 使用这个视图来操作ArrayBuffer
for (let i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}
console.log(int32View); // 输出: Int32Array [0, 2, 4, 6]
// 创建一个DataView,用于操作这个ArrayBuffer
let view = new DataView(buffer);
// 使用DataView来读取一个32位整数
let num = view.getInt32(0);
console.log(num); // 输出: 0
例子中首先创建了一个16字节的ArrayBuffer,然后创建了一个32位整数的视图来操作这个ArrayBuffer。然后又创建了一个DataView来操作这个ArrayBuffer。
在实际开发中我用到的一个例子:造轮子之大文件并行下载、并发数控制、断点续传,下载大文件切片后拼接到一起,就用了二进制数组。
二进制数组还可以应用在ajax请求,Canvas和WebSocket等场景中。
数组和集合Set区别
数组和集合(set)是两种不同的数据结构,它们之间的主要区别在于如何存储和访问数据。
-
存储方式:数组是一种线性数据结构,它将元素存储在连续的内存位置中。每个元素都有一个索引,可以通过索引直接访问元素。而集合是一种无序的数据结构,它不使用索引来存储元素,而是使用一种称为哈希表的结构来存储元素。
-
元素的唯一性:在数组中,元素可以重复。而在集合中,所有的元素都是唯一的,不允许有重复的元素。
-
访问速度:由于数组使用索引来访问元素,因此访问速度通常很快。而集合由于不使用索引,所以访问速度可能会慢一些。但是,集合的优点是查找元素的速度通常很快,因为它使用哈希表来存储元素。
-
插入和删除操作:在数组中,插入和删除操作可能需要移动大量的元素,因此这些操作的速度可能会比较慢。而在集合中,由于元素是无序的,所以插入和删除操作通常很快。
-
顺序:数组中的元素有固定的顺序,而集合中的元素是无序的。
可以使用Set数据结构来对数组进行去重
javascript
let array = [1, 2, 2, 3, 4, 4, 5];
let uniqueArray = [...new Set(array)];
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
使用数组创建数据结构栈
可以使用数组来创建一个栈(Stack)数据结构。栈是一种遵循后进先出(LIFO)原则的数据结构,新添加的元素放在栈顶,只有最后添加的元素可以被取出。这种特性可以通过数组的栈方法push
和pop
方法实现
javascript
class Stack {
constructor() {
this.items = [];
}
// 添加一个(或多个)元素到栈顶
push(element) {
this.items.push(element);
}
// 移除栈顶的元素,同时返回被移除的元素
pop() {
if (this.isEmpty()) {
return 'Stack is empty';
}
return this.items.pop();
}
// 返回栈顶的元素,不对栈做任何修改
peek() {
if (this.isEmpty()) {
return 'Stack is empty';
}
return this.items[this.items.length - 1];
}
// 如果栈里没有任何元素就返回 true,否则返回 false
isEmpty() {
return this.items.length === 0;
}
// 移除栈里的所有元素
clear() {
this.items = [];
}
// 返回栈里的元素个数
size() {
return this.items.length;
}
}
// 使用
let stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.peek()); // 输出:2
console.log(stack.pop()); // 输出:2
console.log(stack.peek()); // 输出:1
Stack
类有一个items
属性,它是一个数组,用来存储栈里的元素。push
方法用来添加元素到栈顶,pop
方法用来移除栈顶的元素,peek
方法用来查看栈顶的元素,isEmpty
方法用来检查栈是否为空,clear
方法用来清空栈,size
方法用来获取栈的大小。
使用数组创建数据结构队列
队列是一种遵循先进先出(FIFO)原则的数据结构,新添加的元素放在队尾,只有最先添加的元素可以被取出。这种行为可以通过数组的push
和shift
方法来实现
javascript
class Queue {
constructor() {
this.items = [];
}
// 添加一个(或多个)元素到队尾
enqueue(element) {
this.items.push(element);
}
// 移除队首的元素,同时返回被移除的元素
dequeue() {
if (this.isEmpty()) {
return 'Queue is empty';
}
return this.items.shift();
}
// 返回队首的元素,不对队列做任何修改
front() {
if (this.isEmpty()) {
return 'Queue is empty';
}
return this.items[0];
}
// 如果队列里没有任何元素就返回 true,否则返回 false
isEmpty() {
return this.items.length === 0;
}
// 移除队列里的所有元素
clear() {
this.items = [];
}
// 返回队列里的元素个数
size() {
return this.items.length;
}
}
// 使用
let queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
console.log(queue.front()); // 输出:1
console.log(queue.dequeue()); // 输出:1
console.log(queue.front()); // 输出:2
例子中,Queue
类有一个items
属性,它是一个数组,用来存储队列里的元素。enqueue
方法用来添加元素到队尾,dequeue
方法用来移除队首的元素,front
方法用来查看队首的元素,isEmpty
方法用来检查队列是否为空,clear
方法用来清空队列,size
方法用来获取队列的大小。
使用数组创建数据结构链表
链表是一种包含链接到其他元素的元素的线性集合。每个元素都存储了一个链接,指向列表中的下一个元素。链表通常从头部开始,头部包含第一个元素,尾部包含最后一个元素
javascript
class Node {
constructor(data, next = null) {
this.data = data;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
}
insertAtBeginning(data) {
let newNode = new Node(data);
newNode.next = this.head;
this.head = newNode;
return this.head;
}
insertAtEnd(data) {
let newNode = new Node(data);
if (!this.head) {
this.head = newNode;
return this.head;
}
let tail = this.head;
while (tail.next !== null) {
tail = tail.next;
}
tail.next = newNode;
return this.head;
}
deleteNode(data) {
if (!this.head) {
return;
}
if (this.head.data === data) {
this.head = this.head.next;
return;
}
let previous = this.head;
let current = previous.next;
while (current !== null) {
if (current.data === data) {
previous.next = current.next;
return;
}
previous = current;
current = current.next;
}
}
}
// 使用
let list = new LinkedList();
list.insertAtBeginning(1);
list.insertAtEnd(2);
list.deleteNode(1);
例子中,Node
类表示链表中的一个节点,它有两个属性:data
(存储节点的数据)和next
(链接到下一个节点)。LinkedList
类表示链表,它有一个属性:head
(链表的头部节点)。
insertAtBeginning
方法在链表的头部插入一个新节点,insertAtEnd
方法在链表的尾部插入一个新节点,deleteNode
方法删除链表中的一个节点。
总结要点
数组使用的地方很多很多。最重要的是找到数组和实际需求之间的映射关系,以及理解由数组构成的多种模型。
数组和字符串有一些相同属性或方法:length
属性、slice()
、includes()
、indexOf()
、lastIndexOf()
、concat()
。
- 创建数组的方式有构造函数、字面量和使用ES6的
Array.of
和Array.from
方法 - 数组本身的重要性质有数组的空位设置、数组的索引和数组的构造函数静态方法
- 数组的检测方法有三种:
Array.isArray()
、instanceOf
、Object.prototype.toString
- 数组的属性主要是length属性的使用,如果加长的话,默认为
undefined
的。 - 类数组类数组(Array-like)对象是一种拥有
length
属性和索引元素,但并不具有数组所拥有的全部方法的对象。以使用Array.from()
方法或者扩展运算符(...
)将类数组变为真正的数组。 - 数组的方法有迭代器方法、复制和填充方法、转换方法、栈方法、队列方法、排序方法、操作方法、搜索为位置方法、迭代方法、归并方法。
- 归并方法
reduce
有一些重要用法。 - 可以使用集合Set对数组去重
- 数组可以用来创建栈、队列和链表数据结构
- 二进制数组在ajax、Canvas、WebSocket中会使用到。
本文完。