【build your own xxx】實現你自己的call和apply

亞古發表於2019-02-23
【build your own xxx】實現你自己的call和apply

新開一個坑,起名為【build your xxx】,自己造一些小輪子。
工作中不要重複造輪子,但是以學習的目的去造輪子卻意義重大。
之前貌似在知乎上看到一個問題是說如何使用JavaScript實現它原生的call和apply方法,今天我來實現一番。

call

首先看看call是幹什麼的,從MDN上扒一張圖:

【build your own xxx】實現你自己的call和apply

舉個例子

    function showName(gender, age){
    	console.log(this.name, " ", gender, " ", age)
    }
    var obj = {
        name: "亞古"
    }
    showName.call(obj, "female", 22)// 亞古   female   22
複製程式碼

梳理思路

可以看出來Func.call(obj, arg1, arg2…)實現了這麼幾件事:

  1. 以obj.Func的方式呼叫
  2. 把引數arg1, arg2 …傳遞給Func
  3. 不對obj和Func造成副作用

實現

    Function.prototype.Zcall = function (othis) {
        othis.fn = this;
        othis.fn();
	}
    showName.Zcall(obj) // 亞古   undefined   undefined
複製程式碼

第一個步驟已經實現了,但是很明顯的是這樣子會對傳入的othis造成副作用,即給othis物件無緣無故新增了一個方法,所以:

    Function.prototype.Zcall = function (othis) {
        othis.fn = this;
        othis.fn();
        delete othis.fn;
    }
複製程式碼

副作用已經消除了,接下來就是引數的問題,這裡有個問題是引數個數是不定的,貌似可以使用一個陣列來arr儲存住arguments裡面的引數,然後再執行othis.fn(arr)。但是,這樣等於說只給fn傳了一個陣列引數,並不能達到目的。
此時問題轉化為我們如何實現像 othis.fn(arguments[0], arguments1, arguments2 …) 這樣的語句呢?
此時可以想起一個不怎麼常用的方法eval

【build your own xxx】實現你自己的call和apply

簡單的說就是可以把字串解析為JavaScript語句來執行。
藉助eval,改寫Zcall方法:

    Function.prototype.Zcall = function (othis) {
		othis.fn = this;
		let args = [];
		for(let i = 1, len = arguments.length;i < len;i++) {
			args.push("arguments[" + i + "]");
		}

		// othis.fn();
		eval("othis.fn(" + args + ")");
		delete othis.fn;
	}
複製程式碼

其實就是用args把arguments用字串的方式儲存住,然後在eval方法中再把字串重新解析為語句。

apply

同理來實現apply:

    Function.prototype.Zapply = function (othis) {
		othis.fn = this;
		let argsArr = arguments[1];
		if (!arguments[1]) {
			let args = [];
			for(let i = 0, len = arguments[1].length;i < len;i++) {
				args.push("arguments[1][" + i + "]");
			}

			eval("othis.fn(" + args + ")");
		}else{
			othis.fn();
		}
		
		delete othis.fn;
	}
複製程式碼

參考資料:

MDN-eval
MDN-apply

JavaScript深入之call和apply的模擬實現

相關文章