快速理解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()函數實現方式一樣相似,能夠修改上述例子實現,主要是在參數一部分作處理。

相關文章
相關標籤/搜索