this指向性問題在開發過程中,可以說是時時刻刻都在,自己也知道一些this指向的區別,但是並沒有細致的研究過,今天看到https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this里面的解釋很詳細,摘錄一些留着自己以后復習復習。
函數的 this 關鍵字在 JavaScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差別。
一、在全局環境中
在全局執行環境中(在任何函數體外部),this都是指向全局對象。在瀏覽器中,window對象即是全局對象:
console.log(this); //Window var a = 1; console.log(window.a); //1 this.b = 3; console.log(b); // 3 console.log(window.b) //3
二、在函數環境中
在函數內容,this指向取決於函數調用的方式:
function f(){ "use strict"; //使用嚴格模式 console.log(this); } f(); // window ;使用嚴格模式時,輸出undefined
這里我理解為實際調用函數的是瀏覽器的window.f();實際並非如果,在嚴格模式下,返回值:false,因為f
是被直接調用的,而不是作為對象的屬性或方法調用的(如window.f()
)。瀏覽器可能在支持嚴格模式時沒有正確實現這個功能,於是它們錯誤地返回了window
對象。
this指向如何發生改變?
1、一般想到的是call和apply方法:將一個對象作為call或者apply的第一個參數,this將會被綁定到這個參數對象上
var obj = {parent:'男'}; var parent = '28'; function child(obj){ console.log(this.parent); } child(); // 28 child.call(obj); //男 child.apply(obj); //男
2、bind方法,調用f.bind(someObject)
會創建一個與f
具有相同函數體和作用域的函數,但是在這個新函數中,this
將永久地被綁定到了bind
的第一個參數,不管函數是怎樣調用的。
function f(){ return this.a; } var g = f.bind({a:"js"}); console.log(g()); // js var h = g.bind({a:'html'}); // this已經被綁定bind的第一個參數,不會重復綁定,輸出的值還是js console.log(h()); // js var o = {a:css, f:f, g:g, h:h}; console.log(o.f(), o.g(), o.h()); // css, js, js
3、箭頭函數
官方有解釋,箭頭函數引入的其中一個原因,就是其不綁定this;在箭頭函數中,箭頭函數的this
被設置為封閉的詞法環境的,換句話說,箭頭函數中的this取決於該函數被創建時的環境。
var objProject = this; var foo = (() => this); console.log(foo()); // window console.log(objProject); // window console.log(foo() === objProject ); // true // 作為對象的一個方法調用 var obj = {foo: foo}; console.log(obj.foo() === objProject ); // true // 嘗試使用call來設定this console.log(foo.call(obj) === objProject ); // true // 嘗試使用bind來設定this foo = foo.bind(obj); console.log(foo() === objProject ); // true
4、作為對象的方法調用時
當函數作為對象的方法被調用時,this指向調用的該函數的對象:
var obj = { a: 37, fn: function() { return this.a; } }; console.log(obj.fn()); // 37
請注意,這樣的行為,根本不受函數定義方式或位置的影響。在前面的例子中,我們在定義對象obj
的同時,將函數內聯定義為成員 fn
。但是,我們也可以先定義函數,然后再將其附屬到obj.fn
。這樣做會導致相同的行為:
var obj = {a: 47}; function independent() { return this.a; } obj.fn = independent; console.log(obj); //{a:47,fn:f} console.log(obj.fn()); // 47
對於在對象原型鏈上某處定義的方法,this
指向的是調用這個方法的對象,就像該方法在對象上一樣
var o = { f: function() { return this.a + this.b; } }; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
在這個例子中,對象p
沒有屬於它自己的f
屬性,它的f屬性繼承自它的原型。雖然在對 f
的查找過程中,最終是在 o
中找到 f
屬性的,這並沒有關系;查找過程首先從 p.f
的引用開始,所以函數中的 this
指向p
。也就是說,因為f
是作為p
的方法調用的,所以它的this
指向了p
。
5、作為構造函數
當一個函數用作構造函數時(使用new關鍵字),它的this
被綁定到正在構造的新對象。
雖然構造器返回的默認值是this
所指的那個對象,但它仍可以手動返回其他的對象(如果返回值不是一個對象,則返回this
對象)。
function C(){ this.a = 37; } var o = new C(); console.log(o.a); // 37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); // 38,手動設置了返回對象
6、作為DOM事件處理函數
當函數被用作事件處理函數時,它的this
指向觸發事件的元素(一些瀏覽器在使用非addEventListener
的函數動態添加監聽函數時不遵守這個約定)。
// 被調用時,將關聯的元素變成藍色 function bluify(e){ console.log(this === e.currentTarget); // 總是 true // 當 currentTarget 和 target 是同一個對象時為 true console.log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // 獲取文檔中的所有元素的列表 var elements = document.getElementsByTagName('*'); // 將bluify作為元素的點擊監聽函數,當元素被點擊時,就會變成藍色 for(var i=0 ; i<elements.length ; i++){ elements[i].addEventListener('click', bluify, false); }
關於this問題的一道面試題:
var baz = 0; let foo = { bar:function() { console.log(this,this.baz); return this.baz; }, baz:1 }; let foo2 = { baz:2 }; let a = foo.bar(); //作為對象的方法調用,this指向調用函數的對象,即foo let b = foo.bar.call(foo2); //使用call方法將this綁定到第一個參數對象向,此時,this指向foo2 let fn = foo.bar; console.log(fn); let c = fn(); //let fn創建的對象,此時,fn = function(){...},此時函數執行時,默認指向全局window對象 let d; (function test(){ d = arguments[0]() })(foo.bar); // arguments.callee包括了一個函數的引用去創建一個arguments對象,它能讓一個匿名函數很方便的指向本身,即此時this指向arguments類數組對象 console.log(a,b,c,d);
結果如下: