自定义Javascript的Apply和Call方法
Liber 2014-04-26 00:43

前几天被问到Apply和Call方法的应用场景,稀里糊涂的乱答一气。

接着又被问到它们的原理,直接语塞。

好在,求知欲还没有彻底沦陷,终于给了自己一个满意的答案:


// Custom the Javascript Apply & Call method.

Function.prototype.customApply = function(obj) {
  obj.constructor.prototype['_temp'] = this;
  var argStr = '';
  if(arguments[1]) {
    for(var i = 0; i < arguments[1].length; i++) {
      argStr += ',arguments[1]['+i+']';
    }
    argStr = argStr.slice(1); // Use slice(1) to remove the first ','
  }
  var functionStr = 'obj._temp('+argStr+')';
  var re = eval(functionStr);
  delete obj.constructor.prototype._temp;
  return re;
}

Function.prototype.customCall = function(obj) {
  return this.customApply(obj, Array.prototype.slice.customApply(arguments, [1]));
}

function Cat() {
  this.food = 'fish';
}

Cat.prototype = {
  say: function(param1, param2) {
    console.log('I Love ' + this.food + ', ' + param1 + ', ' + param2);
  }
}

var blackCat = new Cat();
blackCat.say('smallFish', 'bigFish');

// Demo use.
var whiteDog = {food: 'bone'};
Cat.prototype.say.customApply(whiteDog, ['Bone!', 'BONE!!!']);
Cat.prototype.say.customCall(whiteDog, 'smallBone', 'bigBone');

// Unit test maybe.
function test() {
  var t = Array.prototype.slice.customCall(arguments, 1);
  console.log(t);
}
test(1, 2, 3);

复制粘贴以上代码到控制台即可。

原理:
主要看customApply这个里面的这句:
obj.constructor.prototype['_temp'] = this;
首先,这个方法是被say这个方法调用的,而我们知道,每个function实际上都是Function的一个实例,是一个对象。
所以,这里的this实际上就是那个say方法。
这句代码先是为传进来的obj对象的prototype对象新增了一个_temp的属性,然后将say方法赋值给它。
简单的讲,就相当于为obj对象添加了一个say方法。
就这样obj对象就可以调用say方法了,达到了我们的目的。

原理就是这样,其他的一些代码是处理别的事情,跟原理没什么关系,就不多说了。

不知道Javascript源码是用什么原理实现applycall的,我下载了Chrome V8的源码,是C写的,看了以后头更大了……