在《趣談js的bind牌膠水》這篇文章中,我聊到了js的bind牌膠水,這篇文章我來聊聊bind牌膠水的升級版:call和apply方法。前端
在《趣談js的bind牌膠水》中,我經過js的相關歷史,敘述了bind、call、apply三方法誕生的背景,同時也指出這三個方法出現的共同目的就是就是爲js的一等公民Function函數找個門當戶對的人家(指明Function函數的this指向),既然bind方法已經知足了目的,爲何還須要創造出call、apply兩個方法呢?這兩個方法和bind有哪些異同點?帶着些許疑問,且隨小生遨遊前行。git
call:召喚、呼叫、訪問github
apply:應用、適用、申請編程
在call和apply的中文釋義中咱們能夠看出call、apply這兩個方法帶有明顯的鏈接特性,好比「召喚call」:who召喚who?「應用apply」:who應用到who上?還有bind的中文釋意義:「綁定」,從這三個中文釋義中不難看出知足鏈接特性的動詞須要三元素:1.主動鏈接方、2.被動鏈接方、3.鏈接兩者的中介。對比這三個中文釋義,能夠看出bind和call、apply的釋義略有不一樣,bind的中文釋義帶有明顯的靜態鏈接特性(只鏈接),call、apply的中文釋義中帶有明顯的動態鏈接特性(鏈接以後還使用),因此在三個方法的使用上,bind只負責鏈接函數與相應的對象,call、apply在鏈接好函數與相應的對象後還主動把「鏈接了指定對象的函數」給當場運行了!數組
function.call(thisArg, arg1, arg2, ...); // call語法 function.apply(thisArg, [argsArray]); // apply語法 複製代碼
具體的語法能夠去MDN上看詳情,這裏關於thisArg
說如下幾個注意點:架構
在上面的幾種thisArg
參數例子中,咱們發現一個共同的事實就是:thisArg
參數永遠會是個對象,原始值就用原始值對應的包裝對象,函數就用該引用該函數的對象,無對象時就是全局對象,那些看上去沒對象的狀況,其實也是有對象的,不難看出,js是一門面向對象編程的語言,到處都是對象,萬物皆有對象,那你呢,你有沒有對象?app
call和apply方法都是爲了改變函數的this值而生,具體使用以下:dom
var obj = {
age: 22
}
function say(name) {
console.log('我是:' + name + '|今年:' + this.age);
}
say.call(obj, 'jack'); // 我是:jack|今年:22
say.apply(obj, ['mike']); // 我是:mike|今年:22
複製代碼
技能詳解: 「Master」從天地中召喚出一個強力風暴,逐一對多個目標形成60/85/135/160(+0.35)點魔法傷害。函數式編程
技能演示:函數
var Master = {
name: '召喚師'
};
var target1 = 'enemy1';
var target2 = 'enemy2';
var target3 = 'enemy3';
var target4 = 'enemy4';
var target5 = 'enemy5';
function NorthernStorm(target1, target2, target3, target4, target5) {
console.log(this.name + ' have slained an enemy ' + target1);
console.log(this.name + ' have slained an enemy ' + target2);
console.log(this.name + ' have slained an enemy ' + target3);
console.log(this.name + ' have slained an enemy ' + target4);
console.log(this.name + ' have slained an enemy ' + target5);
}
NorthernStorm.call(Master, target1, target2, target3, target4, target5);
複製代碼
技能詳解:「Master」從天地中召喚出一個強大的末日風暴,能夠瞬間應用到一個目標羣體上,形成200/250/300/444(+1)點AOE魔法傷害。
技能演示:
var Master = {
name: '召喚師'
};
var target1 = 'enemy1';
var target2 = 'enemy2';
var target3 = 'enemy3';
var target4 = 'enemy4';
var target5 = 'enemy5';
function PowerfulStorm(arr) {
console.log(this.name + ' Penta Kill!');
}
PowerfulStorm.apply(Master, [target1, target2, target3, target4, target5]);
複製代碼
哈哈,上面我用遊戲技能簡單的演示了一下call和apply方法的使用,但願能幫助你們理解相關概念,爲了加深理解這裏我針對幾個具體的使用場景作了幾個示例:
var nums = [11, 15, 2, 20, 10];
var max = Math.max.apply(null, nums);
var min = Math.min.apply(null, nums);
console.log(max); // 20
console.log(min); // 2
複製代碼
function func() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
}
func('hello', 'world'); // ["hello", "world"]
複製代碼
var arr = [];
var res = Object.prototype.toString.call(arr); // 這裏獲取的是變量的 [[class]]屬性,通常方法沒有,只有借用Object原型上的toString方法才能夠
console.log(res); // [Object Array]
複製代碼
關於apply和call的使用例子不作過多敘述,由於網上一大把,以前一直以爲js的call、apply、bind三方法使用很彆扭,很醜陋(如今也以爲),後來我學會換個角度看世界後就舒服了不少,以這個例子爲例:
var nums = [11, 15, 2, 20, 10];
var max = Math.max.apply(null, nums);
複製代碼
咱們把不相關的剔除掉(一、爲空時this指向的對象就是Window全局對象;二、Window對象取代Math對象使用max方法),代碼以下:
Window.max(nums);
複製代碼
注意:上面的代碼只是輔助理解,在實際運行時,Window對象上只會短暫的存在max方法,一次性的使用了max方法以後,就會從Window上delete掉max方法,因此經過call、apply綁定給指定對象的函數最終並不會存在於指定對象上。
我我的一直以爲bind、call、apply使用起來不舒服,感受無關緊要,但後來發現這三個方法仍是有不少用武之地的,好比在dom對象中綁定事件就須要bind方法,好比想複用某些函數就能夠用到call和apply,js出現這三個方法很大程度上是由於js用的是函數式編程的樣子,但其實又是面向對象(DOM對象,數據對象等)的裏子,兩種編程思路參雜在了一塊兒,參雜其實沒問題,但兩者的參雜沒能很好融合,設計bind、apply、call就是爲了討好兩方,融合兩者,但這種帶有臨時性質的妥協方案,效果不咋地,由於一山不容二虎,總得有人作紅花,有人甘當綠葉,不是嗎?直到以Angular、React、Vue等爲表明的MVVM架構和改進的ES6新標準出現,前端開發進入新的模式,MVVM架構能讓前端開發較好的實現「面向對象」的編程模式,同時利用ES6的相關特性兼顧函數式編程的靈活性,以往不少問題都不須要bind、call、apply這三兄弟了,好比ES6的箭頭函數就是解決bind的神器,在React的開發中,若是按照傳統思路給事件的匿名函數綁定對象,須要手動用bind綁定,但利用ES6的「箭頭函數」能夠這樣綁定:
<div
onClick={(res) => {
// 這裏的this就是
this.setState({
name: 'jack'
});
}}
>
Click Me
</div>
複製代碼
好比在上面如何使用call、apply的例子中能夠用ES6的擴展操做符...替代來處理:
// 將arguments轉換爲數組
function func() {
var args = ([...arguments]);
console.log(args);
}
func('hello', 'world'); // ["hello", "world"]
// 求數組最大值
var res = Math.max(...[2,20,22]);
console.log(res); // 22
複製代碼
JS在不斷的升級,這三個方法在當前開發的某些場景中可能還會有用武之地,但在我看來,bind、apply、call做爲一個「妥協方案」終將會慢慢的退出舞臺,但在它們被遺忘以前理解設計者們的智慧和想法,我以爲是頗有意思的。
文章涉及內容不少,不免會有紕漏,望理性指正,一塊兒進步哦。