关于Array.reduce()分组示例中.bind()用法的两个疑问
先看这段用Array.reduce()按站点分组的示例代码:
var stats = [ { site: "google.fr", browser: "Chrome", value: "50%" }, { site: "google.fr", browser: "FireFox", value: "30%" }, { site: "google.fr", browser: "Internet Explorer", value: "20%" }, { site: "mozilla.fr", browser: "FireFox", value: "60%" }, { site: "mozilla.fr", browser: "Internet Explorer", value: "20%" }, { site: "microsoft.fr", browser: "Chrome", value: "10%" }, { site: "microsoft.fr", browser: "FireFox", value: "20%" }, ]; function compareSite(site, item) { return site === item.site; } function containSite(site, items) { return items.some(compareSite.bind(null, site)); } function groupBySite(memo, item) { var site = memo.filter(containSite.bind(null, item.site)); if (site.length > 0) { site[0].push(item); } else { memo.push([item]); } return memo; } // We use an empty array as accumulator var results = stats.reduce(groupBySite, []); console.log(results);
运行结果:
[ [ { site: 'google.fr', browser: 'Chrome', value: '50%' }, { site: 'google.fr', browser: 'FireFox', value: '30%' }, { site: 'google.fr', browser: 'Internet Explorer', value: '20%' } ], [ { site: 'mozilla.fr', browser: 'FireFox', value: '60%' }, { site: 'mozilla.fr', browser: 'Internet Explorer', value: '20%' } ], [ { site: 'microsoft.fr', browser: 'Chrome', value: '10%' }, { site: 'microsoft.fr', browser: 'FireFox', value: '20%' } ] ]
接下来解答你的两个疑问:
疑问1:为什么在containSite函数中必须使用.bind(null, site)?
首先得回忆Array.some()的回调规则:它会把遍历到的数组元素、索引、原数组依次作为参数传给回调函数。而我们的compareSite函数定义是(site, item)——第一个参数是要匹配的站点,第二个是要检查的元素。
如果直接把compareSite传给some,some会把数组元素当成第一个参数传给它,这就和compareSite的参数顺序完全反了!
用bind(null, site)的作用,就是把site固定成compareSite的第一个参数,同时把null作为this的指向(这里我们不需要用到this,所以传null就行)。这样当some调用这个绑定后的函数时,就会把数组元素作为第二个参数传进去,刚好匹配compareSite的参数顺序,实现“检查当前数组里有没有元素的site等于指定site”的逻辑。
疑问2:为什么在groupBySite函数中使用的是.bind(null, item.site)而非之前的.bind(null, site)?
这里的核心是绑定的是当前遍历的元素的站点。
在groupBySite里,item是stats数组中正在被处理的当前元素,item.site就是这个元素所属的站点。我们需要调用containSite来检查:memo(已经分组好的数组集合)里有没有已经存在这个站点的分组。
而containSite的第一个参数需要的是“要查找的站点”,所以这里必须把当前item的site绑定进去,而不是一个固定的site变量——毕竟每个元素的站点可能不一样,我们要动态匹配当前元素的站点。
简单说,这里的item.site是随遍历过程动态变化的,每次处理不同的item,绑定的站点值就不同,这样才能正确把每个元素分到对应的站点组里。
内容的提问来源于stack exchange,提问作者Gron465

