译者注:本文要讲的是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方法,带有两个参数:
"https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_to_chain_constructors" target=_blank>它们的建议之上稍微修改了一下:
复制代码 代码如下:
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"}
数组
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。