在JavaScript中,call、apply和bind是Function對象自帶的三個方法,這三個方法的主要做用是改變函數執行時的上下文,再具體一點就是改變函數運行時的this指向。javascript
call() 方法調用一個函數, 其具備一個指定的 this 值和多個參數(參數的列表)。java
fun.call(thisArg, arg1, arg2, ...)
thisArg的取值有如下4種狀況:react
咱們能夠用以下代碼驗證一下:數組
function func1() { console.log(this); } function func2() {} var obj = { name: "jacky" }; func1.call(); // Window func1.call(null); // Window func1.call(undefined); // Window func1.call(1); // Number {1} func1.call(''); // String {""} func1.call(true); // Boolean {true} func1.call(func2); // ƒ func2() {} func1.call(obj); // {name: "jacky"}
咱們再來看個例子,理解怎麼改變this的指向:app
function Animal(){ this.name = 'animal'; this.sayName = function(){ console.log(this.name); } } function Cat(){ this.name = 'cat'; } var animal = new Animal(); var cat = new Cat(); animal.sayName.call(cat); // cat // this 永遠指向最後調用它的那個對象 // 該例子中sayName方法的this指向Cat
call和apply的第一個參數都是要改變上下文的對象,而call從第二個參數開始以參數列表的形式展示,apply則是把除了改變上下文對象的參數放在一個數組裏面做爲它的第二個參數,他們倆之間的差異在於參數的區別。函數
以下代碼:this
function Animal(...args) { console.log(`${this.name} 和其餘參數 ${args}`); }; let cat = { name: 'xiaomao' }; // 1. 使用 call Animal.call(cat, 1, 2, 3); // xiaomao 和其餘參數 1,2,3 // 2. 使用 apply Animal.apply(cat, [1, 2, 3]); // xiaomao 和其餘參數 1,2,3
因爲兩個方法實際效果是同樣的,對於二者平時用該如何選擇呢?prototype
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; // 用 apply方法 [].push.apply(arr1, arr2); // 給arr1添加arr2 console.log(arr1); // [1, 2, 3, 4, 5, 6]
var numbers = [1, 4, 6, 2, 3, 100, 98]; console.log( Math.max.apply(Math, numbers) ); // 100 console.log( Math.max.call(Math, 1, 4, 6, 2, 3, 100, 98) ); // 100 console.log( Math.min.call(Math, ...numbers) ); // 1
let arr = [1, 2, 3, 4]; let str = 'string', obj = { name: 'jacky' } // 判斷傳入參數是否爲數組 function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } console.log(isArray(arr)); // true console.log(isArray(str)); // false console.log(Object.prototype.toString.call(arr)); // [object Array] console.log(Object.prototype.toString.call(str)); // [object String] console.log(Object.prototype.toString.call(obj)); // [object Object] console.log(Object.prototype.toString.call(null)); // [object Null]
function Animal(name){ this.name = name; this.showName = function(){ console.log(this.name); } } function Cat(name){ Animal.call(this, name); } var cat = new Cat("xiaomao"); cat.showName(); // xiaomao
缺點:代理
function func() { var args = [].slice.call(arguments); console.log(args); } func(1, 2, 3); // [1, 2, 3]
還有像調用 getElementsByTagName , document.childNodes 之類的,它們返回NodeList對象都屬於僞數組。code
注意這裏傳入多少個參數是不肯定的,因此使用apply是最好的。
function log(){ console.log.apply(console, arguments); }; log(1); // 1 log(1, 2, 3); // 1 2 3
MDN的解釋是:
bind()方法會建立一個新函數,稱爲綁定函數,當調用這個綁定函數時,綁定函數會以建立它時傳入 bind()方法的第一個參數做爲 this,傳入 bind() 方法的第二個以及之後的參數加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數。
說直白一點,bind方法是建立一個函數,而後能夠在須要調用的時候再執行函數,並不是是當即執行函數;而call,apply是在改變了上下文中的this指向後並當即執行函數。
bind 接收的參數類型與 call 是同樣的,給它傳遞的是一組用逗號分隔的參數列表。
var bar = function(){ console.log(this.x); } var foo = { x: 2 } bar(); // undefined var func = bar.bind(foo); func(); // 2
class PageA { constructor(callBack) { this.className = 'PageA' this.MessageCallBack = callBack this.MessageCallBack('執行了') } } class PageB { constructor() { this.className = 'PageB' this.pageClass = new PageA(this.handleMessage) } // 回調函數 handleMessage(msg) { console.log('處理' + this.className + '的回調 ' + msg) // 處理PageA的回調 執行了 } } new PageB();
運行上面的代碼,咱們發現回調函數this丟失了?問題出在這行代碼
this.pageClass = new PageA(this.handleMessage)
傳遞過去的this.handleMessage是一個函數內存地址,沒有上下文對象,也就是說該函數沒有綁定它的this指向。
解決方案:用bind綁定this的指向
this.pageClass = new PageA(this.handleMessage.bind(this))
這也是爲何react的render函數在綁定回調函數的時候,也要使用bind綁定一下this的指向,也是由於一樣的問題以及原理。
var bar = function() { console.log(this.x); } var obj1 = { x: 2 } var obj2 = { x: 3 } var obj3 = { x: 4 } var func1 = bar.bind(obj1).bind(obj2); var func2 = bar.bind(obj1).bind(obj2).bind(obj3); func1(); // 2 func2(); // 2
輸出結果都爲第一個綁定的obj對象的x值。緣由是,在Javascript中,bind()方法返回的外來的綁定函數對象僅在建立的時候記憶上下文(若是提供了參數),屢次 bind() 是無效的。更深層次的緣由, bind() 的實現,至關於使用函數在內部包了一個 call / apply ,第二次 bind() 至關於再包住第一次 bind() ,故第二次之後的 bind 是沒法生效的。
call和apply的第一個參數都是要改變上下文的對象,而call從第二個參數開始以參數列表的形式展示; apply則是把除了改變上下文對象的參數放在一個數組裏面做爲它的第二個參數,他們倆之間的差異在於參數的區別。
call和apply改變了函數的this上下文後便執行該函數,而bind則是返回改變了上下文後的一個函數,其內的this指向爲建立它時傳入bind的第一個參數,而傳入bind的第二個及之後的參數做爲原函數的參數來調用原函數。bind的參數能夠在函數執行的時候再次添加。