JS的另一些坑

昨天要实现一个类似于筛选的功能,落实到最后,其实就是要在一个 Array 里挑出符合一定条件的元素。恰好,在前后台都有类似的需求,分别用 JavaScript 和 Ruby 两种不同的语法实现。

Ruby 常用的数组方法

在 Ruby 里,数组的方法很好用,昨天顺便总结了一下。

a = [1, 2, 3, 4, 5]
b = [3, 4, 5, 6, 7]
a - b # 从 a 中删除掉 a, b 同时存在的元素,结果是 [1, 2]
a + b # 把 a 的元素加上 b 的元素,组成一个新的数组,结果是 [1, 2, 3, 4, 5, 3, 4, 5, 6, 7]
a | b # 将 a 和 b 取并集,相当于将 a+b 再去重,也就是 (a+b).uniq,结果是 [1, 2, 3, 4, 5, 6, 7]
a & b # 将 a 和 b 取交集,取出 a 和 b 都包含的元素组成新数组,结果是 [3, 4, 5]
a.include?(1) # 判断 a 中是否存在元素 1, 结果是 true
1.in?(a) # Rails有这个语法,原生Ruby是没有的,同样是判断 a 中是否存在元素 1,结果是 true
a.reject { |aa| aa < 2 } # 将 a 中小于 2 的元素删除,结果是 [2, 3, 4, 5]
a.map { |aa| aa >=2 } # 取出 a 中大于等于 2 的元素,结果是 [2, 3, 4, 5]

JS 数组的错误用法

前台是 Javascript,数组用起来好像就没那么方便了。

a = [{id:1, title:a1}, {id:2, title: a2}, {id:3, title: a3}]
b = [2, 3]

以上是两个数组,对于数组 a 来说,它的元素是三个 hash;而数组 b 则是简单的数组。我要实现的一个功能是,从数组 a 中挑出满足 id 属于数组 b 的所有元素。以上的例子,我要实现的结果是得到一个 [{id:2, title: a2}, {id:3, title: a3}] 的新数组。

查了一下 JavaScript 的语法,用一个 filter 方法,有点类似于 Ruby 中的 map 方法,可以把满足条件的元素过滤出来。再查一下,惊喜地发现居然 Javascript 也有 in 方法,于是,我就写了这样的代码:

a.filter(function(e) { return e.id in b })

逻辑非常清晰,但是却得不到想要的结果。搞了好久,也不知道哪里出了问题。只好问同事。同事过来一看,他说,JavaScript 里用 in 这个语法吗?

当然有啊,我明明查到了,于是打开文档给他看。仔细一看,in 方法确实是有,但完全不是我认为的那个意思。

The in operator returns true if the specified property is in the specified object.

在 JavaScript 里,in 方法是用来判断一个对象是否具有某个属性的。看官方的例子:

// Arrays
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
0 in trees        // returns true
3 in trees        // returns true
6 in trees        // returns false
'bay' in trees    // returns false (you must specify the 
                  // index number, not the value at that index)
'length' in trees // returns true (length is an Array property)
Symbol.iterator in trees // returns true (arrays are iterable, works only in ES2015+)

// Predefined objects
'PI' in Math          // returns true

// Custom objects
var mycar = {make: 'Honda', model: 'Accord', year: 1998};
'make' in mycar  // returns true
'model' in mycar // returns true

JS 数组的正确用法

那应该用什么方法呢?我继续查了一下文档,可以用的是 includes 方法,这个方法就跟 Ruby 中的 include? 方法一致了。

a.filter(function(e) { return b.includes(e.id) })

改完之后,我发现还是不对。继续查找原因,发现在项目中,b 数组的元素竟然是 String 类型的。所以,最后正确的写法是这样的。

a = [{id:1, title:a1}, {id:2, title: a2}, {id:3, title: a3}]
b = ["2", "3"]
c = a.filter(function(e) { return b.includes(e.id.toString()) })
// 得到结果是 [{id:2, title: a2}, {id:3, title: a3}]

总结

总结一下, in 方法看起来非常直观,但是

· JavaScript, rails