天天看點

原生JS實作call apply bindcall apply  bind三者均可以改變this指向callapplybind

call apply  bind三者均可以改變this指向

call

 和 

apply

 都可以動态改變 

this

 的指向。作用都是相同的,隻是傳參的方式不同。

除了第一個參數外,

call

 可以接收一個參數清單,

apply

 隻接受一個參數數組。

bind将函數綁定到某個對象,bind()會建立一個函數,函數體内的this對象的值會被綁定到傳入bind()中的第一個參數的值

下面涉及很多對僞數組arguments的操作,不能直接使用數組的方法

下面三種方法對取第二個之後的參數是等效的

原生JS實作call apply bindcall apply  bind三者均可以改變this指向callapplybind

call

整體思路:将函數或者方法添加在對象上,那麼該對象就可以調用該函數了,但是要調用結束後删除掉該函數。

  1. 獲得對象,當沒有傳遞參數時候,為window(特殊情況處理)
  2. 給對象添加方法,方法指派為該函數(this擷取)
  3. 獲得第二個之後的參數作為執行的參數
  4. 執行該對象的方法(傳參)獲得結果
  5. 删除對象的方法
  6. 傳回結果
Function.prototype.mycall=function(context){
    var context=context||window;
    context.fn=this;//目前調用call的函數作為對象的方法
    var args=[...arguments].slice(1);
    var res=context.fn(...args);
    delete context.fn;
    return res;
}
           

驗證:

var a=2;
var obj={a:1}
function say(b,c) {
	console.log(this.a)
	console.log(b+c)
}
say(4,3);//2,7
say.mycall(obj,1,1)//1 2
           

apply

思路:與call類似,對于傳入參數進行判斷,如果存在第二個參數(數組),展開數組傳入函數執行

  1. 獲得對象,當沒有傳遞參數時候,為window(特殊情況處理)
  2. 給對象添加方法,方法指派為該函數(this擷取)
  3. 判斷是否存在第二個參數(數組)存在,執行該對象的方法(傳參)獲得結果; 不存在,執行該對象的方法(不傳參)獲得結果
  4. 删除對象的方法
  5. 傳回結果
Function.prototype.myapply=function(context){
    var context=context||window;
    context.fn=this;
    var res;
    if(arguments[1]){//存在第二個參數
        res=context.fn(...arguments[1]);
    }else{不存在第二個參數
        res=context.fn();
    }
    delete context.fn;
    return res;
}
           

驗證

var a=2;
var obj={a:1}
function say(b,c) {
	console.log(this.a)
	console.log(b+c)
}
say(4,3);//2,7
say.myapply(obj,[1,1])//1 2
           

bind

傳回一個函數,使用apply方法

Function.prototype.mybind=function (context) {
	if (typeof this!=='function') {//調用bind的一定要是函數,傳回綁定this後的函數,否則報錯
		throw new Error(this+'is not a function')
	}
	var self=this;//儲存調用bind的函數
	// var context=[].shift.call(arguments);
	// var context=Array.prototype.shift.call(arguments);
	//如果沒有傳入參數,通過arguments獲得綁定的this上下文
	var args=[].slice.call(arguments,1);//擷取綁定this時候傳入的參數,與調用時的參數區分開
	// var args=Array.prototype.slice.call(arguments,1);//與上面一行等效
	var fbound=function () {
		var bindArgs=[...arguments];
		// 如果是構造函數,this為構造函數的執行個體,上下文仍為新建立的對象,不修改為context
		self.apply(this instanceof self ? this:context, args.concat(bindArgs));
	}
	//傳回的函數的原型鍊要和調用bind的函數原型鍊相同
	fbound.prototype=Object.create(self.prototype);
	return fbound;
}
           

驗證

function bar(){
	var sum=0;
	var arr=[...arguments];
	for (var i = 0; i < arr.length; i++) {
		sum+=arr[i];
	}
	console.log(this);
	console.log(sum)
}
var foob=bar.mybind({a:1},3);
bar(1,2);//window 3
foob(4)//{a:1} 7
           

繼續閱讀