JavaScript 参数中的数组展开 [译]


Posted in Javascript onSeptember 21, 2012

译者注:本文要讲的是ECMAScript 6中的知识点,如果你连ES5都不了解的话.我得说,你已经很落后了.CSS4,HTML6,甚至ES7 ES8都已经开始规划了,赶紧形动起来吧,否则淘汰!

有些时候,我们需要把一个数组展开成多个元素,然后把这些元素作为函数调用的参数.JavaScript中可以使用Function.prototype.apply来实现这种展开操作,但它不能被应用在执行构造函数的情况下.本文解释了什么是展开操作以及如何在使用new运算符的同时进行展开操作.

1.展开(Spreading)

展开的意思是在一个函数调用或方法调用中,或者执行一个构造函数时,通过一个数组来提供所需的参数.在Python中,这种操作称之为unpacking. ECMAScript.next中已经有了(展开操作符)spread operator (表示为一个前缀...)来执行这个展开操作.在目前的JavaScript中,你可以通过Function.prototype.apply方法来实现同样的效果.

译者注:展开操作符除了能用在实参的位置,把数组展开,还可以用在形参的位置,表示剩余参数.请看我翻译的MDN文档剩余参数

2.展开函数参数

Math.max()方法返回它的0到若干个数值类型的参数中的最大值.有了展开操作符,你可以直接使用一个数组来作为参数:

Math.max(...[13, 7, 30])
这等同于下面的写法

Math.max(13, 7, 30)

在目前的JavaScript中,你可以使用apply().
> Math.max.apply(null, [13, 7, 30]) 
30

apply方法的作用是:使用下面的这种调用方式:
func.apply(thisValue, [param1, param2, ...])

来代替这种
thisValue.func(param1, param2, ...)

需要注意的是,func不一定是属于thisValue的方法,apply可以让它临时拥有这个方法.

3.展开构造函数的参数

Date构造函数接受几个数值类型的参数,产生一个Date对象.通过展开操作符,你可以直接传入一个数组.

new Date(...[2011, 11, 24]) // 2011年的圣诞夜

但是,这次我们不能使用apply方法来实现展开操作,因为它不能与new一起工作:
> new Date.apply(null, [2011, 11, 24]) 
TypeError: function apply() { [native code] } is not a constructor

new运算符希望Date.apply是一个构造函数.就算你用小括号将这个表达式括起来,根本问题还是存在:apply执行的是一个函数调用,它不能将参数传递给new运算符.

3.1 解决办法
第一步. 我们先让结果变的正确,稍候再考虑怎么用数组代替分割开的参数.

new (Date.bind(null, 2011, 11, 24))

我们先用bind()来创建一个无参数的函数(参数已经绑定在这个绑定函数的内部了),然后使用new调用它,就像调用一个普通的构造函数一样.bind的函数签名如下:
func.bind(thisValue, arg1, arg2, ...)

bind函数将原函数func转变成一个全新的函数,这个全新函数的this值永远是参数thisValue指定的值,并且它的初始参数包含了从arg1开始到最后的所有参数.当调用这个新函数时,新添加的参数会跟随在那些已有的通过bind绑定的参数后面.MDN上有更详细的资料.注意上面的例子中,第一个参数是null,因为Date函数并不需要一个thisValue:在作为构造函数调用时,new运算符会覆盖掉通过bind指定的thisValue.

第二步.我们想把数组传给bind.所以再次使用了apply,将一个数组转换为展开的参数传递给bind函数.

new (Function.prototype.bind.apply( 
Date, [null].concat([2011, 11, 24])))

我们在函数Function.prototype.bind上调用apply方法,带有两个参数:

•第一个参数: this的值指定为Date, 也就相当于上面写的的Date.bind(...).
•第二个参数: 传给bind方法的参数,null和后面的数组[2011, 11, 24]连接成的新数组.

3.2 一个库函数

Mozilla建议将上述工作封装成一个库方法.下面的代码正是在它们的建议之上稍微修改了一下:

if (!Function.prototype.construct) { 
Function.prototype.construct = function(argArray) { 
if (! Array.isArray(argArray)) { 
throw new TypeError("Argument must be an array"); 
} 
var constr = this; 
var nullaryFunc = Function.prototype.bind.apply( 
constr, [null].concat(argArray)); 
return new nullaryFunc(); 
}; 
}

运行一下:
> Date.construct([2011, 11, 24]) 
Sat Dec 24 2011 00:00:00 GMT+0100 (CET)

3.3 一个看似更简单的解决方案
你可以手动实现new运算符的操作.例如:
var foo = new Foo("abc");

实际上等同于:
var foo = Object.create(Foo.prototype); 
Foo.call(foo, "abc");

根据这个原理,我们可以写一个简单的库方法:
Function.prototype.construct = function(argArray) { 
var constr = this; 
var inst = Object.create(constr.prototype); 
constr.apply(inst, argArray); 
return inst; 
};

唉!Date作为一个普通函数来调用和作为一个构造函数来调用是一样的:它会忽略掉call()和apply()方法中第一个参数指定的this值,总会生成并返回一个新的实例.

译者注:这里作者理解错了,Date作为普通函数调用和作为构造函数来调用是完全不一样的.不加new的情况下,无论有没有参数,Date()只会返回当前时间的字符串,也就是(new Date()).toString()

> Date.construct([2011, 11, 24]) 
{}

译者注:内置的构造函数中,Array(),Function(),RegExp(),Error()等构造函数在调用时,加new或不加几乎一样.比如Array(10)也是生成一个数组,但Number(),String(),Boolean()就不一样了.不加new它们是类型转换函数,返回的是原始值,加new是构造函数,返回的是对象值.
>typeof Number("1") 
"number" 
>typeof new Number("1") 
"object"

正如你所看到的,在操作Date()方法时,我们所写的这个construct()方法并不能如期工作,而且还有一些其他的内置构造函数也表现的和Date一样.不过如果是在操作一个库中自定义的构造函数的时候,这个方法基本可以正常工作(少部分构造函数返回了自己指定的对象值,而不是返回了默认的自动生成的实例this).

译者注:一个构造函数的return语句只要返回的是个对象值,就会覆盖掉默认的this值.比如:

function Func1(){ 
 this.value = "this"; return {} 
} 
function Func2(){ 
this.value = "this"; return 1}function Func3(){ this.value = "this";}>new Func1() //返回的{}是个对象值,覆盖了默认的this.{}>new Func2() //返回的1是个原始值,所以仍然返回默认的this.{value:"this"}>new Func3() //没有return语句,默认返回了undefined,是个原始值,所以仍然返回默认的this.{value:"this"}>new Func3 //没有参数时,小括号可以省略.{value:"this"}
Javascript 相关文章推荐
JavaScript Object的extend是一个常用的功能
Dec 02 Javascript
js 禁止选择功能实现代码(兼容IE/Firefox)
Apr 23 Javascript
javascript 在firebug调试时用console.log的方法
May 10 Javascript
如何实现修改密码时密码框显示保存到cookie的密码
Dec 10 Javascript
JS实现新浪博客左侧的Blog管理菜单效果代码
Oct 22 Javascript
jQuery实现页面点击后退弹出提示框的方法
Aug 24 Javascript
简单实现js点击展开二级菜单功能
May 16 Javascript
用node和express连接mysql实现登录注册的实现代码
Jul 05 Javascript
微信小程序简单实现form表单获取输入数据功能示例
Nov 30 Javascript
使用layer弹窗和layui表单实现新增功能
Aug 09 Javascript
微信小程序Flex布局用法深入浅出分析
Apr 25 Javascript
layer弹出层显示在top顶层的方法
Sep 11 Javascript
JavaScript中将一个值转换为字符串的方法分析[译]
Sep 21 #Javascript
使用apply方法处理数组的三个技巧[译]
Sep 20 #Javascript
js DOM 元素ID就是全局变量
Sep 20 #Javascript
JavaScript NaN和Infinity特殊值 [译]
Sep 20 #Javascript
JavaScript 更严格的相等 [译]
Sep 20 #Javascript
JavaScript 反科里化 this [译]
Sep 20 #Javascript
Array.prototype.concat不是通用方法反驳[译]
Sep 20 #Javascript
You might like
php获取json数据所有的节点路径
2015/05/17 PHP
linux下php上传文件注意事项
2016/06/11 PHP
thinkPHP js文件中U方法不被解析问题的解决方法
2016/12/05 PHP
ThinkPHP框架实现FTP图片上传功能示例
2019/04/08 PHP
laravel Validator ajax返回错误信息的方法
2019/09/29 PHP
浅谈thinkphp的nginx配置,以及重写隐藏index.php入口文件方法
2019/10/12 PHP
使用Apache的rewrite
2021/03/09 Servers
YUI Compressor压缩JavaScript原理及微优化
2013/01/07 Javascript
web的各种前端打印方法之jquery打印插件jqprint实现网页打印
2013/01/09 Javascript
JavaScript的递归之递归与循环示例介绍
2013/08/05 Javascript
基于jquery实现的定时显示与隐藏div广告的实现代码
2013/08/22 Javascript
利用javascript实现web页面中指定区域打印
2013/10/30 Javascript
JS常用字符串处理方法应用总结
2014/05/22 Javascript
jQuery中extend()和fn.extend()方法详解
2015/06/03 Javascript
JavaScript中创建对象的模式汇总
2016/04/19 Javascript
使用开源工具制作网页验证码的方法
2016/10/17 Javascript
为你的微信小程序体积瘦身详解
2017/05/20 Javascript
layui中使用jquery控制radio选中事件的示例代码
2018/08/15 jQuery
对vue 键盘回车事件的实例讲解
2018/08/25 Javascript
JQuery属性操作与循环用法示例
2019/05/15 jQuery
微信小程序indexOf的替换方法(推荐)
2020/01/14 Javascript
[07:47]DOTA2国际邀请赛采访专栏:探访Valve总部
2013/08/08 DOTA
[01:15:16]DOTA2-DPC中国联赛 正赛 Elephant vs Aster BO3 第一场 1月26日
2021/03/11 DOTA
Python pandas RFM模型应用实例详解
2019/11/20 Python
利用SVG和CSS3来实现一个炫酷的边框动画
2015/07/22 HTML / CSS
html5指南-6.如何创建离线web应用程序实现离线访问
2013/01/07 HTML / CSS
Ticketmaster德国票务网站:购买音乐会和体育等门票
2016/11/14 全球购物
信息管理专业推荐信
2013/10/29 职场文书
教师实习自我鉴定
2013/12/13 职场文书
外企财务年会演讲稿
2014/01/03 职场文书
园林技术个人的自我评价
2014/01/08 职场文书
优秀导游先进事迹材料
2014/01/25 职场文书
四查四看剖析材料
2014/02/14 职场文书
幼儿园教师个人总结
2015/02/05 职场文书
侵犯商业秘密的律师函
2015/05/27 职场文书
Win11 Dev 预览版25174.1000发布 (附更新修复内容汇总)
2022/08/05 数码科技