<h2>一、簡單說一下bind、call、apply的區別</h2> <p> 三者都是用於改變函數體內this的指向,可是bind與apply和call的最大的區別是:bind不會當即調用,而是返回一個新函數,稱爲綁定函數,其內的this指向爲建立它時傳入bind的第一個參數,而傳入bind的第二個及之後的參數做爲原函數的參數來調用原函數。</p>git
var obj = {}; function test() { console.log(this === obj); } test(); //false var testObj = test.bind(obj); testObj(); //true
<p> apply和call都是爲了改變某個函數運行時的上下文而存在的(就是爲了改變函數內部this的指向);apply和call的調用返回函數執行結果;</p> <p> 若是使用apply或call方法,那麼this指向他們的第一個參數,apply的第二個參數是一個參數數組,call的第二個及其之後的參數都是數組裏面的元素,就是說要所有列舉出來;</p> <p>如下是MDN文檔:</p>github
bind語法: func.bind(thisArg[, arg1[, arg2[, ...]]]) thisArg 當綁定函數被調用時,該參數會做爲原函數運行時的this指向。當使用new 操做符調用綁定函數時,該參數無效。 arg1, arg2, ... 當綁定函數被調用時,這些參數將置於實參以前傳遞給被綁定的方法。
call語法: fun.call(thisArg, arg1, arg2, ...) thisArg::在fun函數運行時指定的this值。須要注意的是,指定的this值並不必定是該函數執行時真正的this值,若是這個函數處於非嚴格模式下,則指定爲null和undefined的this值會自動指向全局對象(瀏覽器中就是window對象),同時值爲原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。 arg1, arg2, ... 指定的參數列表。
apply語法: fun.apply(thisArg, [argsArray]) thisArg: 在 fun 函數運行時指定的 this 值。須要注意的是,指定的 this 值並不必定是該函數執行時真正的 this 值,若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動指向全局對象(瀏覽器中就是window對象),同時值爲原始值(數字,字符串,布爾值)的 this 會指向該原始值的自動包裝對象。 argsArray: 一個數組或者類數組對象,其中的數組元素將做爲單獨的參數傳給 fun 函數。若是該參數的值爲null 或 undefined,則表示不須要傳入任何參數。從ECMAScript 5 開始可使用類數組對象。
<p><strong>區別總結:</strong></p> <p>當咱們使用一個函數須要改變this指向的時候纔會用到<code>call,apply,bind</code><br>若是你要傳遞的參數很少,則可使用<code>fn.call(thisObj, arg1, arg2 ...)</code><br>若是你要傳遞的參數不少,則能夠用數組將參數整理好調用<code>fn.apply(thisObj, [arg1, arg2 ...])</code><br>若是你想生成一個新的函數長期綁定某個函數給某個對象使用,則可使用</p>數組
const newFn = fn.bind(thisObj); newFn(arg1, arg2...)
<h2>二、bind、call、apply的實現</h2> <h3>myBind:</h3>瀏覽器
Function.prototype.myBind = function() { var _this = this; var context = [].shift.call(arguments);// 保存須要綁定的this上下文 var args = [].slice.call(arguments); //剩下參數轉爲數組 console.log(_this, context, args); return function() { return _this.apply(context, [].concat.call(args, [].slice.call(arguments))); } };
<h3>myCall:</h3>app
/** * 每一個函數均可以調用call方法,來改變當前這個函數執行的this關鍵字,而且支持傳入參數 */ Function.prototype.myCall = function(context) { //第一個參數爲調用call方法的函數中的this指向 var context = context || global; //將this賦給context的fn屬性 context.fn = this;//此處this是指調用myCall的function var arr = []; for (var i=0,len=arguments.length;i<len;i++) { arr.push("arguments[" + i + "]"); } //執行這個函數,並返回結果 var result = eval("context.fn(" + arr.toString() + ")"); //將this指向銷燬 delete context.fn; return result; }
<h3>myApply:</h3>函數
/** * apply函數傳入的是this指向和參數數組 */ Function.prototype.myApply = function(context, arr) { var context = context || global; context.fn = this; var result; if (!arr) { result = context.fn(); //直接執行 } else { var args = []; for (var i=0,len=arr.length;i<len;i++) { args.push("arr[" + i + "]"); } result = eval("context.fn([" + args.toString() + "])"); } //將this指向銷燬 delete context.fn; return result; }
<p> 以上是bind、apply、和call的模擬實現</p> <blockquote>注意:綁定函數(bind函數返回的新函數)不能夠再經過apply和call改變其this指向,即當綁定函數調用apply和call改變其this指向時,並不能達到預期效果。</blockquote>this
var obj = {}; function test() { console.log(this === obj); } var testObj = test.bind(obj); testObj(); //true var objTest = { "做者": "chengbo" }; /** * 預期返回false, 可是testObj是個綁定函數,因此不能改變其this指向 */ testObj.apply(objTest); //true testObj.call(objTest); //true
<p>歡迎留言。</p> <p>原文連接:《<a href="https://xiechengbo.github.io/2018/10/15/bind%E3%80%81call%E3%80%81apply%E7%9A%84%E5%8C%BA%E5%88%AB%E4%B8%8E%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" rel="nofollow noreferrer">bind、call、apply的區別與實現原理</a>》</p>prototype