快速理解JavaScript中call和apply原理

在了解call()apply()原理以前,咱们必须对this的做用和使用方法有所了解,若是你熟悉this 的用法,那么请直接往下看。javascript

call方法:

语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另外一个对象替换当前对象。
说明:call 方法能够用来代替另外一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 若是没有提供 thisObj 参数,那么 Global 对象被用做 thisObj。java

apply方法:

语法:apply([thisObj[,argArray]])
定义:应用某一对象的一个方法,用另外一个对象替换当前对象。
说明:若是 argArray 不是一个有效的数组或者不是 arguments 对象,那么将致使一个 TypeError。 若是没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用做 thisObj, 而且没法被传递任何参数。es6

call()apply()的做用十分类似,只是参数类型上的差异,以适应不一样的使用场景。它们都是为了改变函数运行时的 context(上下文)而存在的,再说的直白一点,就是为了改变函数内部 this 的指向。数组

举例说明

咱们有一句很经典的谚语,说的是:龙生龙,凤生凤,老鼠生来会打洞,这从遗传上解释是,动物的某些行为有多是由一系列基因所调控的,可是,注意,咱们恰恰想让龙来打洞呢,该如何去实现?下面将围绕这个话题来解释call()apply()的原理。app

var dragon = {
	name : 'foo'
	// other attribute
}

var mouse = {
	name : 'tom',
	makeHole : function(where){
		console.log(this.name + ' is making a hole in the ' + where)
	}
	// other attribute
}

mouse.makeHole.call(dragon,'hill')
复制代码

运行上面代码后会在控制台上打印出:函数

能够看出,咱们声明了一个dragon的对象,咱们并无赋予它打洞的功能,可是咱们使用call()继承了mouse的方法,就能够作到mouse函数所能作到的事情。ui

这究竟是怎么作到的呢?让咱们来看看call()的参数: 第一个是一个对象,这个对象将代替Function类里本来的this对象,咱们传入的是this,记住,这个thismakeHole函数里指的是将来将要实例化这个函数的对象(我知道这有些拗口),当声明了dragon的时候,这个this指的就是dragon。除了第一个参数,后面全部的参数都是传给父函数自己使用的参数。this

apply()call()功能几乎同样,惟一的区别就是apply()第二个参数只能是数组,这个数组将做为参数传给原函数的参数列表argumentsspa

模拟实现call()函数

call()函数是什么样的原理呢?咱们用一个实例来帮助理解。prototype

//建立Dragon
function Dragon(name) {
  this.name = name;
}

//建立一个说话的函数
function say(content) {
  console.log(this.name + ' : ' + content)
}

//模拟原生call函数
Function.prototype.myCall = function(context) {
  context = context || window;
  
  var args = [];
  context.fn = this;

  for (var i = 1; i < arguments.length; i++) {
    args.push(arguments[i]);
  };
  
  context.fn(...args);
  delete context.fn;
};

//实例化一个名字为'foo'的龙
var foo = new Dragon('foo')

//让foo说话
say.myCall(foo, 'I can talk!')
复制代码

上面的代码很容易理解,惟一的困难点在于理解在原型链上的myCall函数 咱们来分析实现的步骤:

  1. 作一个多场景适配,当myCall函数没有接收到参数时,context对应的是window对象
  2. 建立一个空数组,用于接收形参。
  3. 绑定this,这里的this表明的就是上下文中的say函数。
  4. for循环将参数添加到args数组,循环从1开始是由于第0位是foo对象,并不是咱们须要的参数
  5. 执行函数,并将args数组做为rest参数传入,这里是ES6的写法,不熟悉的同窗参见阮一峰老师的rest 参数文档
  6. 删除函数

打印结果为:

能够看到,这里咱们实现了让一个叫作foo的龙说话! apply()函数实现方式一样相似,能够修改上述例子实现,主要是在参数一部分作处理。

相关文章
相关标签/搜索