call/apply/bind方法簡介
在JavaScript中,函數中this的指向往往在調用時纔可確定,而JavaScript提供了call/apply/bind方法讓我們得以顯示綁定函數的this指向。
它們的第一個參數是一個對象,它們會把這個對象綁定到調用他們的函數內的this。因爲你可以直接指定 this 的綁定對象,因此我們稱之爲顯式綁定。
//用例
var a = { q: 1 };
var b = { q: 2 };
var c = { q: 3 };
function cs(s) {
console.log(this.q)
}
cs.bind(a)();//1
cs.call(b);//2
cs.apply(c);//3
tips
var s = new fn.myBind({ a: 2333 })();//報錯!!,運算優先級:屬性訪問>帶參new>函數調用>無參new
var s = new (fn.myBind({ a: 2333 }))();//正確姿勢
自定義Call方法實現
參數從arguments[1]開始,func.myCall(obj:Object[,agr1:any[,agr2:any[...]]])
if (!Function.prototype.myCall) {
Function.prototype.myCall = function (targetThis) {
//targetThis默認爲windows(嚴格模式不允許)
targetThis = targetThis || window;
//利用對象調用指定this
targetThis.fn = this;
//收集參數
var agrs = [];
//因爲arguments[0]===targetThis,故從下標1開始
for (var ge = 1, len = arguments.length; ge < len; ge++) {
agrs.push('arguments[' + ge + ']');
}
//利用eval展開參數 並執行函數
var result = eval('targetThis.fn(' + agrs + ')');
//刪除附加對象的屬性以消除副作用
delete targetThis.fn;
//返回結果
return result;
}
}
自定義apply方法實現
參數放在數組裏func.call(obj:Object[,agr:Array])
if (!Function.prototype.myApply) {
Function.prototype.myApply = function (targetThis, arrAgrs) {
//targetThis默認爲windows(嚴格模式不允許)
targetThis = targetThis || window;
//利用對象調用指定this
targetThis.fn = this;
var agrs = [];
//收集參數數組
for (var ge = 0, len = arrAgrs.length; ge < len; ge++) {
agrs.push('arrAgrs[' + ge + ']');
}
//利用eval展開參數 並執行函數
var result = eval(' targetThis.fn(' + agrs + ')');
//刪除附加對象的屬性以消除副作用
delete targetThis.fn;
//返回結果
return result;
}
}
自定義bind方法實現
參數從arguments[1]開始,func.myCall(obj:Object[,agr1:any[,agr2:any[...]]])
//考慮參數合併以及new優先級和原型繼承
if (!Function.prototype.myBind) {
Function.prototype.myBind = function (targetThis) {
//若非函數對象來調用本方法,報異常
if (typeof this !== "function") {
throw new TypeError(
"Function.prototype.bind error"
);
}
//收集參數
var bindArgs = Array.prototype.slice.call(arguments, 1),
originFunction = this,//保存原始this(原始函數)
fnProto = function () { },//利用空函數間接鏈接prototype以應對new時的原型繼承
fnBounding = function () {
//考覈new操作this綁定優先
return originFunction.apply(
(
this instanceof fnProto ? this : targetThis
),
bindArgs.concat(
Array.prototype.slice.call(arguments)
)
)
};
fnProto.prototype = this.prototype;//鏈接原型
//new一個fnProto以實現簡潔繼承原型,防止對fnBounding.prototype的操作污染originFunction原型prototype
fnBounding.prototype = new fnProto();
return fnBounding;
};
}
軟綁定
bind之後可以再bind或call/apply
if (!Function.prototype.softBind) {
Function.prototype.softBind = function (obj) {
var fn = this;
// 捕獲所有 curried 參數
var curried = [].slice.call(arguments, 1);
var bound = function () {
return fn.apply(
(!this || this === (window || global)) ?
obj : this,
curried.concat.apply(curried, arguments)
);
};
bound.prototype = Object.create(fn.prototype);//鏈接原型
return bound;
};
}