在看Javascript 秘密花园的时候,发现一段晦涩难懂的代码片段,由于 中文版的没有讲到这个技巧的妙处,所以硬着头皮,看看英文版
Another trick is to use both
call
andapply
together to turn methods - functions that use the value of this as well as their arguments - into normal functions which only use their arguments.
(大概的意思是:可以一起使用call
和apply
将使用this
和参数的方法函数转化为使用相同参数的普通函数),
这不是在普通函数中利用call
和apply
,绑定传入的上下文吗?嗯...挺类似的,但是它两个一起使用,因为上下文是一个函数,需要再利用一次。
全部代码如下
function Person(first, last) {
this.first = first;
this.last = last;
}
Person.prototype.fullname = function(joiner, options) {
options = options || { order: "western" };
var first = options.order === "western" ? this.first : this.last;
var last = options.order === "western" ? this.last : this.first;
return first + (joiner || " ") + last;
};
// Create an unbound version of "fullname", usable on any object with 'first'
// and 'last' properties passed as the first argument. This wrapper will
// not need to change if fullname changes in number or order of arguments.
//()
Person.fullname = function() {
// Result: Person.prototype.fullname.call(this, joiner, ..., argN);
return Function.call.apply(Person.prototype.fullname, arguments);
};
var grace = new Person("Grace", "Hopper");
// 'Grace Hopper'
grace.fullname();
// 'Turing, Alan'
Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
让我们来看核心代码部分:
return Function.call.apply(Person.prototype.fullname, arguments);
这个怎么理解?文章的作者已经给出了答案: Result: Person.prototype.fullname.call(this, joiner, ..., argN);
,还是难以理解,
刚开始看的时候还是难以理解,在中文版的翻译中给出了相同效果的代码[在stackoverflow也给出了相同的答案]:
Foo.method = function() {
var args = Array.prototype.slice.call(arguments);
Foo.prototype.method.apply(args[0], args.slice(1));
};
嗯..有点眉目,但是这只是效果相同,还是讲不通核心代码return Function.call.apply(Person.prototype.fullname, arguments);
讲的是什么
,怎么实现的功能。
那一步一步来看:
1.首先
Function.call
说的是Function
这个function【可以通过打印知道,function Function(){}】上的call
函数 实际上是Function.prototype.call()
【原型链向上查找】,既然知道是一个函数那就好办了。 2.Function.call.apply
说的是,在Function.call
函数下对apply
函数进行调用。【现在开始是不是清晰了不少?】 3.Function.call.apply(Person.prototype.fullname, arguments);
实际等价于Person.prototype.fullname.call(arguments[0],arguments[1]...);
讲到这里,有一个关键的点,就是,为什么arguments
参数数组(array)会散开,变成了参数数列(list)呢?
想起了前几天在stackoverflow搜索到的答案 【PS:答案可以快速找出一个数组中的最大值】 其中一句话,印象特别深刻。
apply
is a convenient way to pass an array of data as parameters to a function
在这里,正是利用apply
将参数数组转换为一个个的参数的优势。
在最开始代码片段最后的Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
也就清楚了参数为什么是这样。
{ first: "Alan", last: "Turing" }
作为args[0],自然是call
绑定的上下文,即this
指向的对象,而另外两个实参","
,{order: "eastern"}
对应Person.prototype.fullname
函数的形参。