1.call(不傳參版本)es6
// 函數原型上添加 myCall方法 來模擬call Function.prototype.myCall = function(obj){
if(obj === null || obj === undefined){
obj = window;
}else{
obj = object(obj);
} //咱們要讓傳入的obj成爲, 函數調用時的this值. obj._fn_ = this; //在obj上添加_fn_屬性,值是this(要調用此方法的那個函數對象)。 val = obj._fn_(...arg); //不能直接return obj._fn_(...arg) 這樣就不delete屬性了 obj._fn_(); //在obj上調用函數,那函數的this值就是obj. delete obj._fn_; // 再刪除obj的_fn_屬性,去除影響. //_fn_ 只是個屬性名 你能夠隨意起名,可是要注意可能會覆蓋obj上原本就有的屬性 return val }
若是要傳參的話能夠借用es6裏面的拓展運算符 (...)(省略了其餘的判斷)數組
Function.prototype.myCall = function(obj,...arg){ obj._fn_ = this; obj._fn_(...arg); delete obj._fn_; }
測試一下app
let test = { name:'test' } let o = { name:'o', fn:function(){ console.log(this.name, ...arguments); //這裏把參數顯示一下 } } o.fn(1,2,3) // "o" 1 2 3 o.fn.call(test,1,2,3) // "test" 1 2 3 o.fn.myCall(test,1,2,3) // "test" 1 2 3
不用es6能夠用eval函數
Function.prototype.myCall = function(obj){ let arg = [];
let val; for(let i = 1 ; i<arguments.length ; i++){ arg.push( 'arguments[' + i + ']' ) ; // 這裏要push 這行字符串 而不是直接push 值 // 由於直接push值會致使一些問題 // 例如: push一個數組 [1,2,3] // 在下面👇 eval調用時,進行字符串拼接,JS爲了將數組轉換爲字符串 , // 會去調用數組的toString()方法,變爲 '1,2,3' 就不是一個數組了,至關因而3個參數. // 而push這行字符串,eval方法,運行代碼會自動去arguments裏獲取值 } obj._fn_ = this; val = eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS會調用arg數組的toString()方法,這樣就傳入了全部參數 delete obj._fn_;
return val } //測試 let test = { name:'test' } let o = { name:'o', fn:function(){ console.log(this.name, ...arguments); //這裏把參數顯示一下 } } o.fn(1,['a','b'],3) // "o" 1 ["a","b"] 3 o.fn.call(test,1,['a','b'],3) // "test" 1 ["a","b"] 3 o.fn.myCall(test,1,['a','b'],3) // "test" 1 ["a","b"] 3
2.apply(用上面的mycall版本)測試
// ES6 Function.prototype.myApply = function(obj,arr){ let args = []; for(let i = 0 ; i<arr.length; i++){ args.push( arr[i] ); } // 其實直接 ...arr 傳參也能夠 可是效果就和aplly有微小差異了 return this.myCall(obj, ...args); } // eval Function.prototype.myApply = function(obj,arr){ let args = []; for(let i = 0 ; i<arr.length; i++){ args.push( 'arr[' + i + ']' ); // 這裏也是push 字符串 } return eval( 'this.myCall(obj,' + args + ')' ); }
不用mycall版本this
Function.prototype.myApply = function(obj,arr){ let args = []; let val ; for(let i = 0 ; i<arr.length ; i++){ args.push( 'arr[' + i + ']' ) ; } obj._fn_ = this; val = eval( 'obj._fn_(' + args + ')' ) delete obj._fn_; return val }
3.bind(借用apply,call)es5
Function.prototype.bind = function(son){ var that = this; var parent = Array.prototype.slice.call(arguments);//具備length屬性的對象轉成數組 return function() { return that.apply(son, parent.slice(1)); } }
es5spa
Function.prototype.myBind = function(obj){ let _this = this; let argArr = []; let arg1 = []; for(let i = 1 ; i<arguments.length ; i++){ // 從1開始 arg1.push( arguments[i] ); // 這裏用arg1數組收集下參數 // 獲取arguments是從1開始, 但arg1要從 0(i-1)開始 // 如果用Array.prototype.slice.call(argument)就方便多了 argArr.push( 'arg1[' + (i - 1) + ']' ) ; // 若是用arguments在返回的函數裏運行 會獲取不到這個函數裏的參數了 } return function(){ let val ; for(let i = 0 ; i<arguments.length ; i++){ // 從0開始 argArr.push( 'arguments[' + i + ']' ) ; } obj._fn_ = _this; val = eval( 'obj._fn_(' + argArr + ')' ) ; delete obj._fn_; return val }; }
es6prototype
Function.prototype.myBind = function(obj,...arg1){ return (...arg2) => { let args = arg1.concat(arg2); let val ; obj._fn_ = this; val = obj._fn_( ...args ); delete obj._fn_; return val } }