引子
在学习Vue的“渲染函数”时,查看到官方文档有一段这样的代码:
render() {
return Vue.h('div',
Array.apply(null, { length: 20 }).map(() => {
return Vue.h('p', 'hi')
})
)
}
于是对数组构造方法产生了好奇,我们知道构造数组有一种方法就是通过构造函数,即
var arr = new Array(10) //生成一个长度为10的数组
/*与上面等价*/
var arr1 = Array(10)
也就是说Array是一个静态方法,可以直接调用,也可以采用构造函数的new式调用。
联想
在看到Vue官方文档中的那段代码,我们联想到,apply、call这类是通过传递上下文来调用某个函数的,区别是apply(contextObject,[arg1,arg2,…,argN]),call(contextObject,arg1,arg2,…,argN)。即apply第二个参数是形参数组,call第二个参数开始都是逗号分隔的形参。但是这里使用的是apply,第二个参数却用的是对象。似乎有点不对劲。
但是我们忽略了一点是,数组本质就是一个对象,除了以下几点:
- 当有新的元素添加到列表中时,自动更新length属性
- 设置length为一个较小值将截断数组
- 从Array.prototype中继承一些有用的方法
- 其类属性为“Array”
所以就有了类数组对象的概念,即拥有一个数值length属性和对应的非负整数健的对象。
这里其实是传的类数组对象。
根据犀牛书的指导,我们知道 JavaScript的数组方法是特意定义为通用的,因此它们不仅应用在真正的数组,而且在类数组上都能正确工作。
区别
Array.apply(null, {length: 10}) //生成的是一个每项都为undefined的数组
/*下面两个方法等效*/
Array.apply(null, [10])
Array.call(null, 10) //生成的是一个每项都未初始化(empty)的数组
由于存在这种区别,对于数组的map、forEach、for in、reduce等遍历方法(for循环会遍历)不会去遍历未初始化的项(delete的项也会变成empty),所以Vue文档中才采用了第一种写法。
新的写法
由于浏览器对ES6的支持性不断完善,现在大部分浏览器都已经原生支持ES6语法(试验性的),只是需要标记为module,即
<script type="module">
//此处可以写原生ES6代码
console.log("es6 module")
</script>
利用ES6中对数组的新增方法fill,我们可以这样写
Array(10).fill() //生成的是一个每项都为undefined的数组
所以我们如果在Vue的单文件组件中写的话,可以直接:
render() {
return Vue.h('div',
Array(20).fill().map(() => {
return Vue.h('p', 'hi')
})
)
}