當一個函數被保存爲對象的一個屬性時,咱們稱它爲一個方法
。當一個方法被調用時,this 被綁定到該對象
。若是調用表達式包含一個提取屬性的動做(即包含一個.
點表達式或[subscript]
下標表達式),那麼它就是被當作一個方法來調用。編程
var myObj = { value: 0, increment: function (inc) { this.value += typeof inc === 'number' ? inc : 1; } }; myObj.increment(); console.log(myObj.value); // 1 myObj.increment(2); console.log(myObj.value); // 3
var add = function (a,b) { return a + b; }; var sum = add(3,4); // sum的值爲7
以此模式調用函數時,this 被綁定到全局對象
。數組
延伸:調用內部函數時,如何把 this 綁定到外部函數的 this 變量上瀏覽器
// 承接上面代碼 // 給 myObj 增長一個 double 方法 myObj.double = function() { var that = this; // 解決方法 var helper = function () { console.log(this); // this指向全局對象,若是寫成this.value = add(this.value, this.value); 就獲取不到正確的結果了 that.value = add(that.value, that.value); }; helper(); // 以函數的形式調用 helper }; myObj.double(); // 以方法的形式調用 double console.log(myObj.value); // 6
var Quo = function (string) { this.status = string; } var myQuo = new Quo("confused"); // 構造一個 Quo 實例 console.log(myQuo.status); // "confused"
一個函數,若是建立的目的就是但願結合new
前綴來調用,那它就被稱爲構造(器)函數
,函數內部的this 指向新建立的實例
。app
js 是一門函數式的面向對象編程語言,函數也是一個對象,因此函數能夠擁有本身的方法,apply、call就是其中的兩種方法。編程語言
此種調用模式容許咱們能夠顯式地設置 this 的指向
,具體使用見下文。函數
1. fun.apply(thisArg[, argsArray])
在指定 this 值和參數(參數以數組
或類數組
對象的形式存在)的狀況下調用某個函數。thisArg
:在 fun 函數運行時指定的 this 值,若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動指向全局對象(瀏覽器中就是window對象)argsArray
:一個數組或者類數組對象,其中的數組元素將做爲單獨的參數傳給 fun 函數。若是該參數的值爲null 或 undefined,則表示不須要傳入任何參數。也可使用 arguments 對象做爲 argsArray 參數,用arguments把全部的參數傳遞給被調用對象。oop
/* 求一個數組中的最大最小值 */ var numbers = [5, 6, 2, 3, 7]; /* simple loop based algorithm */ max = -Infinity, min = +Infinity; for (var i = 0; i < numbers.length; i++) { if (numbers[i] > max) max = numbers[i]; if (numbers[i] < min) min = numbers[i]; } /* vs. using Math.min/Math.max apply */ var max = Math.max.apply(null, numbers); /* This about equal to Math.max(numbers[0], ...) or Math.max(5, 6, ..) */ var min = Math.min.apply(null, numbers);
從上面的例子能夠看到:原本須要寫成遍歷數組變量的任務,apply使用內建的函數就完成了。據此,能夠簡化某些對數組的操做this
var arr1 = [1,2,3]; var arr2 = [4,5,6]; /* 若是咱們要把 arr2 展開,而後一個一個追加到 arr1 中去,最後讓 arr1=[1,2,3,4,5,6] * arr1.push(arr2)是不行的,由於這樣作會獲得[1,2,3,[4,5,6]] * 能夠循環arr2,而後一個一個的push,可是這樣比較麻煩,使用apply,就so easy了 */ Array.prototype.push.apply(arr1,arr2); console.log(arr1); // [1,2,3,4,5,6] /* 也能夠用arr1.concat(arr2),可是concat方法返回的是一個新數組,並不改變arr1自己 */
2. fun.call(thisArg[, arg1[, arg2[, ...]]])
該方法的做用和 apply() 方法相似,只有一個區別,就是 call() 方法接受的是一個參數列表,而apply()方法接受的是一個包含多個參數的數組。prototype
Math.max.apply(null, [1,2,3,4]); Math.max.call(null, 1, 2, 3, 4); /* eg. 使用call方法調用父構造函數 */ function Animal(name){ this.name = name; this.showName = function(){ console.log(this.name); } } function Cat(name){ Animal.call(this, name); // 此行代碼中的this指向Cat的實例 } var cat = new Cat("Black Cat"); cat.showName(); // "Black Cat"
3. fun.bind(thisArg[, arg1[, arg2[, ...]]])
當在函數fun上調用bind( )方法並傳入一個對象thisArg做爲參數,這個方法將返回一個新函數。調用新的函數將會把原始的函數fun當作thisArg的方法來調用。thisArg
:當綁定函數被調用時,該參數會做爲原函數運行時的 this 指向。當使用new 操做符
調用綁定函數時,該參數無效
arg1, arg2, ...
:當綁定函數被調用時,這些參數加上綁定函數自己的參數會按照順序做爲原函數運行時的參數code
// eg.1 var sum = function (x,y) { return x + y; }; var succ = sum.bind(null, 1); succ(2); // => 3: x綁定到1,並傳入2做爲實參y // eg.2 function f(y,z) { return this.x + y + z; }; var g = f.bind({x:1}, 2); // 綁定this和y g(3); // =>6: this.x綁定到1,y綁定到2,z綁定到3 // eg.3 建立綁定函數 this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 81 var retrieveX = module.getX; retrieveX(); // 9, because in this case, "this" refers to the global object // Create a new function with 'this' bound to module var boundGetX = retrieveX.bind(module); boundGetX(); // 81
bind 函數在ES5版本中才被加入,ES3版本的bind( )方法實現以下(js權威指南p191):
if (!Function.prototype.bind) { Function.prototype.bind = function(o[, args]) { var self = this, boundArgs = arguments; // bind()方法的返回值是一個函數 return function() { // 建立一個實參列表,將傳入bind()的第二個及後續的實參都傳入這個函數 var args = [], i; for(i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]); for(i = 0; i < arguments.length; i++) args.push(arguments[i]); // 如今將self做爲o的方法來調用,傳入這些實參 return self.apply(o, args); } } } /* 關鍵點有二:一是改變this的指向,二是改變傳入參數的個數 * * 上述代碼並未實現ES5中bind方法的所有特性,但思路比較清晰明瞭,且知足大部分需求了 */