在執行上下文的建立階段,會分別生成變量對象,創建做用域鏈,肯定this指向。this的指向,是在函數被調用的時候肯定的。也就是執行上下文被建立時肯定的。所以,一個函數中的this指向,能夠是很是靈活的前端
this對象表明函數運行時,自動生成的一個內部對象,只能在函數內部使用
在全局執行環境中(在任何函數體外部)this 都指向全局對象。
在函數內部,this的值取決於函數被調用的方式。數組
關鍵點:瀏覽器
this
永遠指向一個對象,而且擁有着個對象的值this
指向undefined
this
在一個函數內出現的時候,this指向調用這個函數的對象
不管是否在嚴格模式下,在全局執行環境中(在任何函數體外部)this 都指向
全局對象
。
// 在瀏覽器中, window 對象同時也是全局對象: console.log(this === window); // true a = 37; console.log(window.a); // 37 this.b = "MDN"; console.log(window.b) // "MDN" console.log(b) // "MDN"
在函數內部,this的值取決於函數被調用的方式。app
當函數做爲對象裏的方法被調用時, this 指向
調用該方法的對象
若是函數做爲一個對象的屬性方法,而且被調用的時候,那麼這個屬性方法中的this 就指向這個對象
下面的例子中,當 o.f()
被調用時,函數內的this
將綁定到o對象
。函數
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
請注意,這樣的行爲,根本不受函數定義方式或位置的影響。在前面的例子中,咱們在定義對象o的同時,將函數內聯定義爲成員 f 。可是,咱們也能夠先定義函數,而後再將其附屬到o.f。這樣作會致使相同的行爲:ui
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37
這代表函數是從o的f成員調用的纔是重點。this
一樣,this 的綁定只受最靠近的成員引用的影響。在下面的這個例子中,咱們把一個方法g看成對象o.b的函數調用。在此次執行期間,函數中的this將指向o.b。事實證實,這與他是對象 o 的成員沒有多大關係,最靠近的引用纔是最重要的。prototype
o.b = {g: independent, prop: 42}; console.log(o.b.g()); // 42
若是是普通函數調用方式。非嚴格模式下,this
指向window
,嚴格模式下,this
是undefined
;
非嚴格模式下,this 的值默認指向全局對象。在瀏覽器中,全局對象是windowcode
function f1(){ return this; } //在瀏覽器中: f1() === window; //在瀏覽器中,全局對象是window //在Node中: f1() === global;
在嚴格模式下,this將保持他進入執行環境時的值,因此下面的this將會默認爲undefined。對象
function f2(){ "use strict"; // 這裏是嚴格模式 return this; } f2() === undefined; // true
因此,在嚴格模式下,若是 this 沒有被執行環境(execution context)定義,那它將保持爲 undefined。
若是是構造函數調用方式,this指向實例化出來的新對象
/* * 構造函數這樣工做: * * function MyConstructor(){ * // 函數實體寫在這裏 * // 根據須要在this上建立屬性,而後賦值給它們,好比: * this.fum = "nom"; * // 等等... * * // 若是函數具備返回對象的return語句, * // 則該對象將是 new 表達式的結果。 * // 不然,表達式的結果是當前綁定到 this 的對象。 * //(即一般看到的常見狀況)。 * } */ function C(){ this.a = 37; } var o = new C(); console.log(o.a); // logs 37
雖然構造器返回的默認值是this所指的那個對象,但它仍能夠手動返回其餘的對象(若是返回值不是一個對象,則返回this對象)。
function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); // logs 38
在剛剛的例子中(C2),由於在調用構造函數的過程當中,手動的設置了返回對象
,與this綁定的默認對象被丟棄了。(這基本上使得語句 「this.a = 37;」成了「殭屍」代碼,實際上並非真正的「殭屍」,這條語句執行了,可是對於外部沒有任何影響,所以徹底能夠忽略它)。
call()
、apply()
方式調用,this指向被綁定的對象
;
若是要想把 this 的值從一個環境傳到另外一個,就要用 call 或者apply 方法。
// 將一個對象做爲call和apply的第一個參數,this會被綁定到這個對象。 var obj = {a: 'Custom'}; // 這個屬性是在global對象定義的。 var a = 'Global'; function whatsThis(arg) { return this.a; // this的值取決於函數的調用方式 } whatsThis(); // 'Global' whatsThis.call(obj); // 'Custom' whatsThis.apply(obj); // 'Custom'
當一個函數在其主體中使用 this 關鍵字時,能夠經過使用函數繼承自Function.prototype
的 call
或 apply
方法將 this
值綁定到調用中的特定對象
。
function add(c, d) { return this.a + this.b + c + d; } var o = {a: 1, b: 3}; // 第一個參數是做爲‘this’使用的對象 // 後續參數做爲參數傳遞給函數調用 add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // 第一個參數也是做爲‘this’使用的對象 // 第二個參數是一個數組,數組裏的元素用做函數調用中的參數 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
使用 call 和 apply 函數的時候要注意,若是傳遞給 this 的值不是一個對象,JavaScript 會嘗試使用內部 ToObject 操做將其轉換爲對象
。所以,若是傳遞的值是一個原始值好比 7 或 'foo',那麼就會使用相關構造函數將它轉換爲對象,因此原始值 7 會被轉換爲對象,像 new Number(7) 這樣,而字符串 'foo' 轉化成 new String('foo') 這樣,例如:
function bar() { console.log(Object.prototype.toString.call(this)); } //原始值 7 被隱式轉換爲對象 bar.call(7); // [object Number]
bind()
方式調用,this指向被綁定的對象
;
ECMAScript 5 引入了 Function.prototype.bind
。調用f.bind(someObject)
會建立一個與f具備相同函數體和做用域的函數,可是在這個新函數中,this
將永久
地被綁定到了bind的第一個參數,不管這個函數是如何被調用的。
function f(){ return this.a; } var g = f.bind({a:"azerty"}); console.log(g()); // azerty var h = g.bind({a:'yoo'}); // bind只生效一次! console.log(h()); // azerty var o = {a:37, f:f, g:g, h:h}; console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
在箭頭函數中,this與封閉詞法環境的this保持一致。在全局代碼中,它將被設置爲全局對象:
若是是箭頭函數,是根據當前的詞法做用域來決定this, 具體來講,箭頭函數會繼承外層函數調用的this綁定
。
var globalObject = this; var foo = (() => this); console.log(foo() === globalObject); // true
注意:若是將this傳遞給call、bind、或者apply,它將被忽略。不過你仍然能夠爲調用添加參數,不過第一個參數(thisArg)應該設置爲null。
// 接着上面的代碼 // 做爲對象的一個方法調用 var obj = {foo: foo}; console.log(obj.foo() === globalObject); // true // 嘗試使用call來設定this console.log(foo.call(obj) === globalObject); // true // 嘗試使用bind來設定this foo = foo.bind(obj); console.log(foo() === globalObject); // true
不管如何,foo 的 this 被設置爲他被建立時的環境(在上面的例子中,就是全局對象)。這一樣適用於在其餘函數內建立的箭頭函數:這些箭頭函數的this被設置爲封閉的詞法環境的。
// 建立一個含有bar方法的obj對象, // bar返回一個函數, // 這個函數返回this, // 這個返回的函數是以箭頭函數建立的, // 因此它的this被永久綁定到了它外層函數的this。 // bar的值能夠在調用中設置,這反過來又設置了返回函數的值。 var obj = { bar: function() { var x = (() => this); return x; } }; // 做爲obj對象的一個方法來調用bar,把它的this綁定到obj。 // 將返回的函數的引用賦值給fn。 var fn = obj.bar(); // 直接調用fn而不設置this, // 一般(即不使用箭頭函數的狀況)默認爲全局對象 // 若在嚴格模式則爲undefined console.log(fn() === obj); // true // 可是注意,若是你只是引用obj的方法, // 而沒有調用它 var fn2 = obj.bar; // 那麼調用箭頭函數後,this指向window,由於它從 bar 繼承了this。 console.log(fn2()() == window); // true
在上面的例子中,一個賦值給了 obj.bar
的函數(稱爲匿名函數 A),返回了另外一個箭頭函數(稱爲匿名函數 B)。所以,在 A 調用時,函數B的this
被永久設置爲obj.bar
(函數A)的this
。當返回的函數(函數B)被調用時,它this
始終是最初設置的。在上面的代碼示例中,函數B的this
被設置爲函數A的this
,即obj
,因此即便被調用的方式一般將其設置爲 undefined
或全局對象
(或者如前面示例中的其餘全局執行環境中的方法),它的 this
也仍然是 obj
。
在對象原型鏈上某處定義的方法,this
指向的是調用這個方法的對象
對於在對象原型鏈上某處定義的方法,一樣的概念也適用。若是該方法存在於一個對象的原型鏈上,那麼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
。這是 JavaScript 的原型繼承中的一個有趣的特性。
當函數在一個 getter 或者 setter 中被調用。用做 getter 或 setter 的函數都會把this
綁定到設置或獲取屬性的對象
。
function sum() { return this.a + this.b + this.c; } var o = { a: 1, b: 2, c: 3, get average() { return (this.a + this.b + this.c) / 3; } }; Object.defineProperty(o, 'sum', { get: sum, enumerable: true, configurable: true}); console.log(o.average, o.sum); // logs 2, 6
當函數被用做事件處理函數時,它的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); }
當代碼被內聯on-event 處理函數調用時,它的this
指向監聽器所在的DOM元素
:
<button onclick="alert(this.tagName.toLowerCase());"> Show this </button>
上面的 alert 會顯示button。注意只有外層代碼中的this是這樣設置的:
<button onclick="alert((function(){return this})());"> Show inner this </button>
在這種狀況下,沒有設置內部函數的this,因此它指向 global/window 對象(即非嚴格模式下調用的函數未設置this時指向的默認對象)。
做爲定時器的參數時,this
指向window
setInterval(function() { console.log(this); }, 1000);
若是你以爲這篇文章對你有所幫助,那就順便點個贊吧,點點關注不迷路~
黑芝麻哇,白芝麻發,黑芝麻白芝麻哇發哈!
前端哇發哈