- 创建、修改数组
- 使用、复用数组函数
- 使用 Map 创建字典
- 使用 Set 创建不重复的对象的集合
使用数组字面量创建数组优于数组构造函数。主要原因很简单:
[]
与new Array()
(2 个字符与 11 个字符)。此外,由于 JavaScript 的高度动态特性,无法阻止修改内置的Array
构造函数,也就意味着new Array()
创建的不一定是数组。因此,推荐坚持使用数组字面量。
- push:在数组末尾添加元素。
- unshift: 在数组开头添加元素。
- pop: 从数组末尾删除元素。
- shift: 从数组开头删除元素。
- slice
- splice
性能考虑:pop 和 push 与 shift 和 unshift
pop
和push
方法只影响数组最后一个元素:pop
移除最后一个元素,push
在数组末尾增加元素。shift
和unshift
方法修改第一个元素,之后的每一个元素的索引都需要调整。因此,pop
和push
方法比shift
和unshift
要快很多,非特殊情况不建议使用shift
和unshift
方法。
- 遍历数组。
- 基于现有的数组元素映射创建新数组。
- 验证数组元素是否匹配指定的条件。
- 查找特定数组元素。
- 聚合数组,基于数组元素计算(例如,计算数组元素之和)。
数组遍历
- for
- forEach:数组内置方法
映射数组
提取 weapon 数组的粗略方法:
const ninjas = [
{ name: 'Yagyu', weapon: 'shuriken' },
{ name: 'Yoshi', weapon: 'katana' },
{ name: 'Kuma', weapon: 'wakizashi' }
];
const weapons = [];
ninjas.forEach(ninja => {
weapons.push(ninja.weapon);
});
映射数组:
const ninjas = [
{ name: 'Yagyu', weapon: 'shuriken' },
{ name: 'Yoshi', weapon: 'katana' },
{ name: 'Kuma', weapon: 'wakizashi' }
];
const weapons = ninjas.map(ninja => ninja.weapon); // map 函数对数组的每个元素执行回调函数,使用返回值创建新数组
测试数组元素
使用 every 和 some 方法测试数组:
const ninjas = [
{ name: 'Yagyu', weapon: 'shuriken' },
{ name: 'Yoshi' },
{ name: 'Kuma', weapon: 'wakizashi' }
];
const allNinjasAreNamed = ninjas.every(ninja => 'name' in ninja); // true
const allNinjasAreArmed = ninjas.every(ninja => 'weapon' in ninja); // false
const someNinjasAreArmed = ninjas.some(ninja => 'weapon' in ninja); // true
数组查找
const ninjas = [
{ name: 'Yagyu', weapon: 'shuriken' },
{ name: 'Yoshi' },
{ name: 'Kuma', weapon: 'wakizashi' }
];
const ninjaWithWakizashi = ninjas.find(ninja => {
return ninja.weapon === 'wakizashi';
});
console.log(
ninjaWithWakizashi.name === 'Kuma' &&
ninjaWithWakizashi.weapon === 'wakizashi'
);
const ninjaWithKatana = ninjas.find(ninja => {
return ninja.weapon === 'katana';
});
console.log(ninjaWithKatana === undefined);
const armedNinjas = ninjas.filter(ninja => 'weapon' in ninja);
console.log(armedNinjas.length === 2);
console.log(armedNinjas[0].name === 'Yagyu' && armedNinjas[1].name === 'Kuma');
查找数组索引
findIndex()
const ninjas = ['Yagyu', 'Yoshi', 'Kuma', 'Yoshi'];
console.log(ninjas.indexOf('Yoshi') === 1);
console.log(ninjas.lastIndexOf('Yoshi') === 3);
const yoshiIndex = ninjas.findIndex(ninja => ninja === 'Yoshi');
console.log(yoshiIndex === 1);
findIndex
方法接收回调函数,并返回第一个回调函数返回 true
的元素。本质上 findIndex
与 find
方法类似,唯一的区别是 find
方法返回元素本身,而 findIndex
方法返回元素的索引。
数组排序
const ninjas = [{ name: 'Yoshi' }, { name: 'Yagyu' }, { name: 'Kuma' }];
ninjas.sort(function(a, b) {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
合计数组元素
使用 reduce
合计数组元素:
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((aggregated, number) => aggregated + number, 0);
reduce
方法接收初始值,对数组每个元素执行回调函数,回调函数接收上一次回调结果以及当前的数组元素作为参数。最后一次回调函数的结果作为 reduce
的结果。
<body>
<input id="first"/>
<input id="second"/>
<script>
const elems = {
length: 0,
add: function(elem) {
Array.prototype.push.call(this, elem);
},
gather: function(id) {
this.add(document.getElementById(id));
},
find: function(callback) {
return Array.prototype.find.call(this, callback);
}
};
elems.gather("first");
console.log(elems.length === 1 && elems[0].nodeType);
elems.gather("second");
console.log(elems.length === 2 && elems[1].nodeType);
elems.find(elem => elem.id === "second");
console.log(found && found.id === "second");
</script>
</body>
访问对象未显式定义的属性
const dictionary = {
ja: {
'Ninjas for hire': 'レンタル用の忍者'
},
zh: {
'Ninjas for hire': '忍者出租'
},
ko: {
'Ninjas for hire': '고용 닌자'
}
};
console.log(dictionary.ja['Ninjas for hire'] === 'レンタル用の忍者'); // true
console.log(typeof dictionary.ja['constructor'] === 'undefined'); // false
原型继承属性以及 key 仅支持字符串,所以通常不能使用对象作为
map
。由于这种限制,ECMAScript 委员会定义了一个全新类型:Map
。
const ninjaIslandMap = new Map();
const ninja1 = { name: 'Yoshi' };
const ninja2 = { name: 'Hattori' };
const ninja3 = { name: 'Kuma' };
ninjaIslandMap.set(ninja1, { homeIsland: 'Honshu' });
ninjaIslandMap.set(ninja2, { homeIsland: 'Hokkaido' });
除了 get
和 set
方法之外,map
size
:创建了多少个映射has
: 判断指定的 key 是否存在delete
:删除映射
const directory = new Map();
directory.set('Yoshi', '+81 26 6462');
directory.set('Kuma', '+81 52 2378 6462');
directory.set('Hiro', '+81 76 277 46');
for (let item of directory) {
console.log(item[0] !== null, 'Key:' + item[0]);
console.log(item[1] !== null, 'Value:' + item[1]);
}
for (let key of directory.keys()) {
console.log(key !== null, 'Key:' + key);
console.log(directory.get(key) != null, 'Value:' + directory.get(key));
}
for (var value of directory.values()) {
console.log(value !== null, 'Value:' + value);
}
集合中的每个元素都是唯一的(每个 元素只能出现一次),这种集合称为 Set。
通过对象模拟 Set
function Set() {
this.data = {};
this.length = 0;
}
Set.prototype.has = function(item) {
return typeof this.data[item] !== 'undefined';
};
Set.prototype.add = function(item) {
if (!this.has(item)) {
this.data[item] = true;
this.length++;
}
};
Set.prototype.remove = function(item) {
if (this.has(item)) {
delete this.data[item];
this.length--;
}
};
const ninjas = new Set();
ninjas.add('Hattori');
ninjas.add('Hattori');
console.log(ninjas.has('Hattori') && ninjas.length === 1);
ninjas.remove('Hattori');
console.log(!ninjas.has('Hattori') && ninjas.length === 0);
const ninjas = new Set(['Kuma', 'Hattori', 'Yagyu', 'Hattori']);
console.log(ninjas.has('Hattori'));
console.log(ninjas.size === 3);
console.log(!ninjas.has('Yoshi'));
ninjas.add('Yoshi');
console.log(ninjas.has('Yoshi'));
console.log(ninjas.size === 4);
console.log(ninjas.has('Kuma'));
ninjas.add('Kuma');
console.log(ninjas.size === 4);
for (let ninja of ninjas) {
console.log(ninja !== null, ninja);
}
与 Map
和数组类似,Set
也是集合,因此可以使用 for-of
循环进行遍历。
两个集合的并集指的是创建一个新的集合,同时包含 A 和 B 中的所有成员。
const ninjas = ['Kuma', 'Hattori', 'Yagyu'];
const samurai = ['Hattori', 'Oda', 'Tomoe'];
const warriors = new Set([...ninjas, ...samurai]);
assert(warriors.has('Kuma'), 'Kuma is here');
assert(warriors.has('Hattori'), 'And Hattori');
assert(warriors.has('Yagyu'), 'And Yagyu');
assert(warriors.has('Oda'), 'And Oda');
assert(warriors.has('Tomoe'), 'Tomoe, last but not least');
assert(warriors.size === 5, 'There are 5 warriors in total');
两个集合的交集指的是创建新集合,该集合中只包含集合 A 与 B 中同时出现的成 员。
const ninjas = new Set(['Kuma', 'Hattori', 'Yagyu']);
const samurai = new Set(['Hattori', 'Oda', 'Tomoe']);
const ninjaSamurais = new Set([...ninjas].filter(ninja => samurai.has(ninja)));
assert(ninjaSamurais.size === 1, "There's only one ninja samurai");
assert(ninjaSamurais.has('Hattori'), 'Hattori is his name');
两个集合的差集指的是创建新集合,只包含存在于集合 A、但不在集合 B 中的元素。
const ninjas = new Set(['Kuma', 'Hattori', 'Yagyu']);
const samurai = new Set(['Hattori', 'Oda', 'Tomoe']);
const pureNinjas = new Set([...ninjas].filter(ninja => !samurai.has(ninja)));
assert(pureNinjas.size === 2, "There's only one ninja samurai");
assert(pureNinjas.has('Kuma'), 'Kuma is a true ninja');
assert(pureNinjas.has('Yagyu'), 'Yagyu is a true ninja');
- 数组是特殊的对象,具有
length
属性,原型是Array.prototype
。 - 可以使用数组字面量(
[]
)或Array
构造函数创建数组。 - 通过使用数组对象的方法可以修改数组的内容。
- 使用
push
与pop
方法从数组结束位置添加或删除元素。 - 使用
shift
与unshift
方法从数组起始位置添加或删除元素。 splice
方法可以从任意位置添加或删除元素。
- 使用
- 数组可以访问很多有用的方法。
- map 方法可对数组成员调用回调函数,并使用调用结果创建新数组。
- every 与 some 方法检测全部或部分元素是否匹配某些条件。
- find 与 filter 方法查找满足某些条件的元素。
- sort 方法对数组排序。
- reduce 方法将数组成员合计为一个值。
- 可以在自定义对象上,显式定义对象方法,使用
call
或apply
方法对数组的方法进行复用。 - Map 和字典是包含
key
与value
映射关系的对象。 - JavaScript 中的对象是糟糕的 map,只能使用字符串类型作为 key,并且存在访问原型属性的风险。因此,使用内置的 Map 集合。
- 可以使用
for...of
循环遍历 Map 集合。 Set
成员的值都是唯一的。