call和apply的作用
call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數,該方法的語法和作用與 apply() 方法類似,只有一個區別,就是 call() 方法接受的是一個參數列表,而 apply() 方法接受的是一個包含多個參數的數組
在説實現自定義call、apply之前,我們首先看下一段代碼
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
我們自己實現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的參數以數組的形式傳入,所以我們只要做一下修改
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;
}