call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數,該方法的語法和做用與 apply() 方法相似,只有一個區別,就是 call() 方法接受的是一個參數列表,而 apply() 方法接受的是一個包含多個參數的數組
在說實現自定義call、apply以前,咱們首先看下一段代碼es6
var age = 6; var person = { age: 3, fn: function() { console.log(this.age); } } var fn = person.fn; fn(); person.fn();
結果是數組
6 3
爲何呢?記住一句話:this的最終指向的是那個調用它的對象var fn = person.fn
,將person
的fn
方法賦值給fn
,但未調用,而fn()
調用的時候實際等於window.fn()
,因此fn中的this指向window
,因此fn()
的結果是window.age = 6
而person.fn()
是person
這個對象調用的,因此此時fn
函數中this
的指向是person
這個對象。因此this.age = 3
app
咱們本身實現call方法,思路就是基於此函數
Function.prototype.myCall = function(_context) { // 當_context未傳值或者爲null或者undefined,context指向window var context = _context || window; // 給對象添加一個方法,這個this就是使用call使用的函數 context.fn = this; // 拼接執行eval時候的參數 var args = []; for(var i = 1; i < arguments.length; i++) { args.push('arguments['+ i +']'); } // 字符串拼接數組,數組隱式轉換成args.toString()即'context.fn('+ args +')', => 'context.fn(arguments[1])' var ret = eval('context.fn('+ args +')'); delete context.fn; return ret; } Function.prototype.myCallEs6 = function(context = window) { // 給對象添加一個方法,這個this就是使用call使用的函數 context.fn = this; // 獲取除了第一個之外參數 const args = [...arguments].slice(1); // 執行方法,此時執行方法的this指向的是context const ret = context.fn(...args); delete context.fn; // 返回執行結果 return ret; } const person = { age: 3 }; function fn(name) { this.name = name; console.log(this); } fn.myCall(person, 'xzf'); fn.call(person, 'xzf');
call和apply做用相同,僅僅只是參數不一樣,apply的參數以數組的形式傳入,因此咱們只要作一下修改this
Function.prototype.myApply = function(_context) { // 當_context未傳值或者爲null或者undefined,context指向window var context = _context || window; // 給對象添加一個方法,這個this就是使用call使用的函數 context.fn = this; var ret; var args = arguments[1]; if(args) { // 假設args爲['123','abc'],['123','abc'].toString()會轉換成'123,abc' // 執行eval的時候,會變成一個數字,一個變量,此時是不存在變量abc的 // 須要經過上面mycall相似的方法進行轉換 var argsArr = []; for(var i = 0; i < arguments.length; i++) { argsArr.push('args['+ i +']'); } ret = eval('context.fn('+ argsArr +')'); } else { ret = context.fn(); } delete context.fn; return ret; } // es6 Function.prototype.myApplyEs6 = function(context = window) { // 當_context未傳值或者爲null或者undefined,context指向window // 給對象添加一個方法,這個this就是使用call使用的函數 context.fn = this; let ret; // 若是有參數,展開數組參數傳入 if(arguments[1]) { ret = context.fn(...arguments[1]); } else { ret = context.fn(); } delete context.fn; return ret; }