call, apply, bind都是改變函數執行的上下文,說的直白點就是改變了函數this的指向。不一樣的是:call和apply改變了函數的this,而且執行了該函數,而bind是改變了函數的this,並返回一個函數,但不執行該函數。數組
看下面的例子1:app
var doThu = function(a, b) { console.log(this) console.log(this.name) console.log([a, b]) } var stu = { name: 'xiaoming', doThu: doThu, } stu.doThu(1, 2) // stu對象 xiaoming [1, 2] doThu.call(stu, 1, 2) // stu對象 xiaoming [1, 2]
因而可知,在stu上添加一個屬性doThu,再執行這個函數,就將doThu的this指向了stu。而call的做用就與此至關,只不過call爲stu添加了doThu方法後,執行了doThu,而後再將doThu這個方法從stu中刪除。函數
下面來看call函數的內部實現原理:this
Function.prototype.call = function(thisArg, args) { // this指向調用call的對象 if (typeof this !== 'function') { // 調用call的若不是函數則報錯 throw new TypeError('Error') } thisArg = thisArg || window thisArg.fn = this // 將調用call函數的對象添加到thisArg的屬性中 const result = thisArg.fn(...[...arguments].slice(1)) // 執行該屬性 delete thisArg.fn // 刪除該屬性 return result }
apply的實現原理和call同樣,只不過是傳入的參數不一樣而已。下面只給出代碼,不作解釋:prototype
Function.prototype.apply = function(thisArg, args) { if (typeof this !== 'function') { throw new TypeError('Error') } thisArg = thisArg || window thisArg.fn = this let result if(args) { result = thisArg.fn(...args) } else { result = thisArg.fn() } delete thisArg.fn return result }
bind的實現原理比call和apply要複雜一些,bind中須要考慮一些複雜的邊界條件。bind後的函數會返回一個函數,而這個函數也可能被用來實例化:code
Function.prototype.bind = function(thisArg) { if(typeof this !== 'function'){ throw new TypeError(this + 'must be a function'); } // 存儲函數自己 const _this = this; // 去除thisArg的其餘參數 轉成數組 const args = [...arguments].slice(1) // 返回一個函數 const bound = function() { // 可能返回了一個構造函數,咱們能夠 new F(),因此須要判斷 if (this instanceof bound) { return new _this(...args, ...arguments) } // apply修改this指向,把兩個函數的參數合併傳給thisArg函數,並執行thisArg函數,返回執行結果 return _this.apply(thisArg, args.concat(...arguments)) } return bound }