万物科技 学,以致用

JavaScript 5. 数组再探

2017-06-05
Geng

前面基础部分已经介绍过了数组, 这里再介绍一些高级的内容.

数组内的搜索

前面介绍过indexOf/lastIndexOfincludes

find()

数组的find()方法返回满足符合检验函数的第一个元素, 否则返回undefined:

function isBigEnough(element) {
  return element >= 15;
}

[12, 5, 8, 130, 44].find(isBigEnough); 
130

这个方法语法为:find(function(item, index, array), 其中的那个函数会作用在每一个元素上, 各个参数为:

  • item就是元素.
  • index是索引值.
  • array是数组本身.

当然, 使用之前介绍的箭头函数更好:

let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];

let user = users.find(item => item.id == 1);

console.log(user.name); 
John

其他两个参数很少会用到, 不做介绍了. findIndex()用法没啥区别, 唯一不一样的就是返回是索引而不是元素值.

如果想要返回所有符合的元素呢? 可以使用filter()

filter()

用法和find()一样: filter(function(item, index, array), 但是返回的符合元素组成的数组:

let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];

// returns array of the first two users
let someUsers = users.filter(item => item.id < 3);

console.log(someUsers); // 2
[ { id: 1, name: 'John' }, { id: 2, name: 'Pete' } ]

数组的变换

可以想象成空间的映射关系

map 映射

还是熟悉的味道:map(function(item, index, array):

let upperCase = ["Bilbo", "Gandalf", "Nazgul"].map( item => item.toUpperCase() );
console.log(upperCase);
[ 'BILBO', 'GANDALF', 'NAZGUL' ]

可见, 这就是对每个元素进行某个操作, 然后返回操作后的结果.

forEach

其实forEach不应该放在这里, 但是这个方法和map很像, 所以拿过来做一个比较. 用法与map没有区别, 唯一不同是它没有返回值, 仅仅是对每个元素进行某操作:

["Bilbo", "Gandalf", "Nazgul"].forEach( item => console.log(item.toUpperCase()) );
BILBO
GANDALF
NAZGUL





undefined

sort 排序

这个方法直接介绍过, 这里在返回来看看:

let numArr = [ 1, 2, 15 ];
numArr.sort();
console.log(numArr);
[ 1, 15, 2 ]

看上面结果, 神奇吗? 排序结果完全不对啊! 这是为什么?

因为sort默认里面要排序的是字符串, 对于字符串来说, 上面结果就没问题了.

那我想要对数字排序呢? 我们可以给sort()传递一个排序函数, 根据返回的正负来排序:

function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

let numArr = [ 1, 2, 15 ];

numArr.sort(compareNumeric);

console.log(numArr);  
[ 1, 2, 15 ]

因为仅仅能判断正负即可, 所以可以简化为:

numArr.sort( (a, b) => a - b );

console.log(numArr); 
[ 1, 2, 15 ]

降维 reduce/reduceRight

我们使用map()forEach()遍历数组, 使用某种方法处理数组, 但是并没有降低数组维度.

这里, 我们可以使用 reduce/reduceRight来降低数组维度, 就是使一个一维数组变为零维, 也就是一个数字, 语法为:

let value = arr.reduce( function(previousValue, item, index, arr) { /*...*/ }, initialValue );

回调函数参数中的第二个开始的参数很熟悉了吧? 那么第一个参数呢?

  • previousValue是之前调用结果的值.

另外initialValue就是整个运算的初始值.

直接上例子看:

let arr = [1, 2, 3, 4, 5]

let sum = arr.reduce( (sum, current) => sum + current, 0 );

console.log(sum); 
15

简单说, 就是sum的初始值为0, 第一次计算的时候, current是第一个元素, 然后用sum记录sum + current, 然后以此类推.

不过我更喜欢用数学公式的方式说明这个问题, 因为我觉得它本来就是这样啊,:

这是一个递归公式, 其中\(N = n_0, n_1, … , n_m\)是一个数组, 我们想要计算它的累加, \(S_k\)为前\(k\)个元素的和.

上面的reduce()语句的回调函数如果是任意一个函数, 初值为\(i_0\), 那么:

假设我要计算各个元素质积, 那么:

let product = arr.reduce( (product, current) => product * current, 1 );

console.log(product); 
120

如果没有设初值, 那么初值为第一个元素, 然后从第二个元素开始计算

上面代码可以改写为:

let sumNoInit = arr.reduce( (sum, current) => sum + current );
console.log(sumNoInit); 

let productNoInit = arr.reduce( (product, current) => product * current );
console.log(productNoInit); 
15
120

reduceRight()的区别仅仅是从最后一个(最右边)开始算起, 无他

可迭代对象(Iterables)

可迭代对象包含数组, 字符串等, 它们的特点就是可以使用for..of循环:

let ages = [20, 30, 40, 50];
for(let age of ages) {
    console.log(age);
}
20
30
40
50
let fullName = "Jim Clark Silver";
for(let name of fullName) {
    console.log(name);
}
J
i
m
 
C
l
a
r
k
 
S
i
l
v
e
r

可见, 对于数组, 是元素为单位的遍历; 对于字符串, 是字母为单位的遍历.

似数组 (Array-likes)

似数组是有索引和长度的对象, 类似数组, 故而得名.

Array.from()

我们可以用Array.from()方法将一个iterable或者array-like转变为Array:

let arrayLike = {
  0: "Hello",
  1: "World",
  length: 2
};

let arr = Array.from(arrayLike); 
console.log(arr)
console.log(arr.length)
[ 'Hello', 'World' ]
2

你还可以给Array.from()提供一个映射方法:

let arrDouble = Array.from(arrayLike, ele => ele + ele);
console.log(arrDouble)
[ 'HelloHello', 'WorldWorld' ]

Comments

你可以请我喝喝茶,聊聊天,鼓励我

Wechat Pay
wechat

Thanks!