从如何优雅的将类数组对象转化为数组谈起

今日研读阮老师的ES6标准入门,读到函数的扩展方法时看到这么一段代码:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.call(arrayList) // ["a","b","c"]

很明显,上面代码转化代码的关键就是[].slice.call()这一句了。
鄙人不才,平常在项目里碰到需要解构的json数据时都是直接怼上for/forin硬肛的,现在想想真是羞愧啊,往事不提不提~
阮老师的这个demo勾起了我的兴趣,那么我就从call()讲起。
要理解var arr = [].slice.call(arrayList) // ["a","b","c"]这句代码的意思,你需要有以下三点需要理解:

  1. [ ]是什么?
  2. slice()函数起到什么作用?
  3. call()函数起到什么作用?

先讲第一点。[ ]是什么,J开头的程序员肯定都知道,js里的数组嘛,没什么好说的。值得讲讲的就一点,就是[]与new Array()到底有什么区别?它们到底哪个效率高?[]到底需不需要分配内存?我不做回答,大家可以自行探究。

再讲第二点,slice()函数,我们来看官方MDN定义:

slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改.

这就很明显了,这就是[]对象(js里函数也是一个对象)调用了一下slice()函数嘛,为啥调用它,因为它返回新的数组对象呀。那么问题又来了,是不是只要会返回新的数组对象的函数都可以在这里调用呢?我们暂且先不谈论,先往下讲。

最后说说第三点,call( )函数。我们来看MDN定义:

Function.prototype.call()
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。

当然,MDN对此函数还有补充:

语法

fun.call(thisArg, arg1, arg2, …)

扫描二维码关注公众号,回复: 2078788 查看本文章

参数

thisArg
在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

返回值

返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。

简单来说呢,就是我们调用call()函数的根本目的,就是为了去解决js里单个object对象没有length属性,也调用不了slice()函数的痛点,当然这个调用也是通过prototype.call()这个原型链去调用。
如果你尝试用这句代码

var arr = [].slice.call(arrayList,1)  //["b","c"]

call(this,args1,args2…)函数中this之后的参数会随之传递给slice()函数。

如果我们把call()函数换成apply()可以得到一样的结果吗?答案是肯定的。

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.apply(arrayList) // ["a","b","c"]

但是如果我们把call()换成bind()呢?还可以得到数组吗?答案是否定的。

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.bind(arrayList) // function slice(){...]

经测试,bind()函数果然不负众望,给咱们返回了一个slice()函数。究其原因,是因为:

bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

那么,除了以上几个方法外还有什么可以‘优雅’的转化数组的方式吗?答案是毋庸置疑的。
首先挂上ES6里已经实现的函数Array.from()
MDN:

Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。

使用起来也异常暴力,深谙简单就是艺术的美学:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = Array.from(arrayList) // ["a","b","c"]

至于兼容性情况,对于万恶之源IE浏览器来说肯定是不支持的,这辈子都不可能支持的。(不接受拿Edge来跟我抬杠)

我们还有一个Object.values()函数可以达到类似的目的。下面关门放MDN:

Object.values()方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于for-in 循环枚举原型链中的属性 )。

关于什么是可枚举性,这个就不深说,简单来说就是用for-in迭代时能取到对象里所有的属性。
我们来看代码:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = Object.values(arrayList) // ["a","b","c"3]

看到区别了吗?这次返回的数组多了一个5,很显然它是把对象里的所有key对应的value返回出来了。再看几个例子:

let arrayList = {
    "a":"1",
    "b":"2",
    "c":"3",
}
var arr = Object.values(arrayList) // ["a","b","c"3]

let arrayList1 = {
    "a":"aa",
    "b":"bb",
    "c":"cc",
}
var arr1 = Object.values(arrayList1) // ["aa","bb","cc"]

以上的例子充分说明Object.values()对引用的参数对象是没有类数组对象那种格式限制的。

当然,兼容性方面除了IE外,其他浏览器稍微高一点版本都是支持的。

猜你喜欢

转载自blog.csdn.net/dk2290/article/details/80976614