Skip to content

Instantly share code, notes, and snippets.

@GeekaholicLin
Last active December 6, 2016 13:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GeekaholicLin/ba1d1b12712f5ae4b6fcaf1d00112fe5 to your computer and use it in GitHub Desktop.
Save GeekaholicLin/ba1d1b12712f5ae4b6fcaf1d00112fe5 to your computer and use it in GitHub Desktop.
the trick of using call and apply together[当call和apply一起使用]

在看Javascript 秘密花园的时候,发现一段晦涩难懂的代码片段,由于 中文版的没有讲到这个技巧的妙处,所以硬着头皮,看看英文版

Another trick is to use both call and apply together to turn methods - functions that use the value of this as well as their arguments - into normal functions which only use their arguments.

(大概的意思是:可以一起使用callapply将使用this和参数的方法函数转化为使用相同参数的普通函数), 这不是在普通函数中利用callapply,绑定传入的上下文吗?嗯...挺类似的,但是它两个一起使用,因为上下文是一个函数,需要再利用一次。

全部代码如下

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函数的形参。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment