最近在项目中,看到有同事写一个数组的方法的时候,习惯性的用以下写法:
*[].forEach.call(array,fn);*[].map.call(array,fn);*[].sort.call(array,fn);...
我的第一个想法是,为啥对于一个数组不直接写成:
*array.forEach(fn)*array.map(fn)*array.sort(fn)
询问其原因后,同事的说法是:在有些书上说过,这么写保险,保证数组的方法万无一失可以调用,不报错。
观点同事的出发点没错,就是保证书写代码的健壮性。但我个人的观点是,这种不管三七二十一统一都调用.call来保证功能可用的方法,在一定程度上,牺牲了代码的可读性,同时给代码增加了一些冗余。
为什么这么说呢?我们先看看同事的这个万无一失的担忧在哪里。
问题我们知道在js中有一种结构叫:类数组(array-like),这是怎么样的一种结构呢?就是:表现形式上像数组,但是却没有数组的方法。
比如:
在函数体内有个参数arguments就是获取所有的参数,这个参数获取的结果像是一个数组,但是却没有数组的方法。
在老的浏览器中,使用方法document.querySelectorAll获取到所有Dom节点,如果要遍历,就不能直接使用数组forEach方法(最新的Chrome、Fifox已经不存在这个问题了)。
结论综上所述,只有我们遇到类数组的时候,再调用数组的.call将类数组转换为一个真正的数组,然后使用数组提供的方法。如果我们明确知道这就是一个数组,就没必要再call转换一次了,实在影响代码可读性。
基本功call和apply都算是继承的一种写法,[].map.call(a1,fn)就是将数组的map方法继承给a1,并且调用这个map方法;[].map.call和Array.prototype.map.call是相同的,几乎没啥差异,性能上也差不多,见这里的测评;同上我们常用的Object.prototype.toString.call也可以写成({}).toString.call,都是可以的,只所以写成({})是由于不写的话,就成了}.而不是对象{};