關於this的全面解析(下)頁面連接segmentfault
調用位置就是函數在代碼中被調用的位置(而不是聲明的位置),尋找調用位置就是尋找「函數被調用的位置」,最重要的是分析調用棧(就是爲了到達當前執行位置所調用的全部函數)。瀏覽器
function baz() { //當前調用棧是baz //當前調用位置是全局位置 console.log('baz'); bar(); //<--bar的調用位置 } function bar() { //當前的調用棧是baz->bar //所以當前的調用位置在baz中 console.log('bar'); foo(); //<--foo的調用位置 } function foo() { //當前的調用棧是baz->bar->foo //所以當前的調用位置在bar中 console.log('foo'); } baz(); //<-baz的調用位置
把調用棧想象成一個函數調用鏈,如上圖代碼中的樣式,可是這種方法很是麻煩而且容易出錯。另外一個查看調用棧的方法是使用瀏覽器的調試工具。app
首先須要找到調用位置,而後判斷尋求下列四條規則中的哪一條。函數
1 默認綁定工具
首先介紹最經常使用的函數調用類型:獨立函數調用。能夠把這條規則看做是沒法應用其餘規則時的默認規則。this
function foo() { console.log(this.a); //<-this指向全局做用域 } var a = 2; foo(); //<-foo調用位置
在代碼中,foo()是直接使用不帶任何修飾的函數引用進行調用的,所以只能使用默認綁定,沒法應用其餘規則。調試
function foz() { "use strict"; console.log(this.a); //<--嚴格模式下不能將全局對象用於綁定 } foz(); //TypeError
2 隱藏綁定code
另外一條須要考慮的規則是調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含,這種說法有時候會有誤導。對象
function foa() { console.log(this.a); } var foaObj = { a: "Hello", foa: foa //<--foa函數調用位置 } foaObj.foa();
foa函數在嚴格意義上來講不屬於foaObj對象。然而,調用位置會使用foaObj上下文來引用函數,所以能夠判斷爲函數調用時,foaObj對象包含並引用它。ip
當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象。所以,調用foa函數時this被綁定到foaObj這個對象上,因此this.a 和 foaObj.a 是同樣的。
然而,有一個常見的this綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用默認綁定,從而把this綁定到全局對象或undefined上。
function fob() { console.log(this.a); } var fobObj = { a: "Hello", fob: fob } var focObj = fobObj.fob; var a = 1; //a是全局對象的屬性 focObj();
focObj引用其實是fob函數,因此this綁定的是全局對象中的a。
3 顯式綁定
就像咱們剛纔看到的那樣,在分析隱式綁定時,咱們必須在一個對象內部包含一個指向函數的屬性,並經過這個屬性間接引用函數,從而把this間接綁定到這個對象上。
JavaScript提供的絕大多數函數以及你本身建立的全部函數均可以使用call(…) 和 apply (…) 方法。
這兩個方法的第一參數是一個對象,是給this準備的,接着在調用函數時將其綁定到this。由於你能夠直接指定this的綁定對象,所以咱們稱之爲顯式綁定。
function fod() { console.log(this.a); } var fodObj = { a: 2 } fod.call(fodObj); //2
經過fod.call(…)方法,能夠強制把this綁定到fodObj這個對象上。
然而,顯示綁定仍然沒法解決以前提出的丟失綁定問題。
可是顯示綁定的一個變種能夠解決這個問題。
function foh() { console.log(this.a); } var fohObj = { a: 2 } var baa = function() { foh.call(fohObj); } baa(); //2 setTimeout(baa, 100); //2 baa.call(window); //2
咱們建立了一個baa函數,並在它的內部手動調用了foh.call(fohObj),所以強制把foh的this綁定到了fohObj上。不管以後如何調用函數baa,它總會手動在fohObj上調用foh。這種綁定是一種顯示的強制綁定,所以咱們稱之爲硬綁定。
硬綁定的典型應用場景就是建立一個包裹函數,負責接收參數並返回值。
function foi(something) { console.log(this.a, something); return this.a + something; } var foiObj = { a: 2 } var bae = function() { return foi.apply(foiObj, arguments); } var b = bae(3); console.log(b);
另外一種使用方法是建立一個能夠重複使用的輔助函數。
function fol(something) { console.log(this.a, something); return this.a + something; } function bind(fn, obj) { return function() { return fn.apply(obj, arguments); } } var folObj = { a: 3 } var bac = bind(fol, folObj); var c = bac(4); console.log(c);
4 new綁定
使用new來調用函數,或者說發生構造函數調用時,會自動執行下面的操做。
1) 建立(或者說構造)一個全新的對象。
2) 這個新對象會被執行Prototype鏈接。
3) 這個新對象會綁定到函數調用的this。
4) 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。
function abc(a) { this.a = a; } var x = new abc(10); console.log(x.a);