摘要:在這次執行期間,函數中的將指向。在剛剛的例子中,因為在調用構造函數的過程中,手動的設置了返回對象,與綁定的默認對象被丟棄了。在上面的例子中,一個賦值給了的函數稱為匿名函數,返回了另一個箭頭函數稱為匿名函數。
一、引言
在執行上下文的創建階段,會分別生成變量對象,建立作用域鏈,確定this指向。this的指向,是在函數被調用的時候確定的。也就是執行上下文被創建時確定的。因此,一個函數中的this指向,可以是非常靈活的
二、this對象的定義this對象代表函數運行時,自動生成的一個內部對象,只能在函數內部使用
在全局執行環境中(在任何函數體外部)this 都指向全局對象。
在函數內部,this的值取決于函數被調用的方式。
關鍵點:
this永遠指向一個對象,并且擁有著個對象的值
在嚴格模式下,在全局作用域中和匿名函數中,this指向undefined
當this在一個函數內出現的時候,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的值取決于函數被調用的方式。
1.對象方法調用模式當函數作為對象里的方法被調用時, 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。這樣做會導致相同的行為:
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37
這表明函數是從o的f成員調用的才是重點。
同樣,this 的綁定只受最靠近的成員引用的影響。在下面的這個例子中,我們把一個方法g當作對象o.b的函數調用。在這次執行期間,函數中的this將指向o.b。事實證明,這與他是對象 o 的成員沒有多大關系,最靠近的引用才是最重要的。
o.b = {g: independent, prop: 42}; console.log(o.b.g()); // 422.函數調用模式
如果是普通函數調用方式。非嚴格模式下,this指向window,嚴格模式下,this是undefined;
非嚴格模式下,this 的值默認指向全局對象。在瀏覽器中,全局對象是window
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。
3.構造函數調用模式如果是構造函數調用方式,this指向實例化出來的新對象
/* * 構造函數這樣工作: * * function MyConstructor(){ * // 函數實體寫在這里 * // 根據需要在this上創建屬性,然后賦值給它們,比如: * this.fum = "nom"; * // 等等... * * // 如果函數具有返回對象的return語句, * // 則該對象將是 new 表達式的結果。 * // 否則,表達式的結果是當前綁定到 this 的對象。 * //(即通??吹降某R娗闆r)。 * } */ 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;”成了“僵尸”代碼,實際上并不是真正的“僵尸”,這條語句執行了,但是對于外部沒有任何影響,因此完全可以忽略它)。
4.call / apply 調用模式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]5.bind方法調用
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, azerty6.箭頭函數
在箭頭函數中,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 。
7.原型鏈的this在對象原型鏈上某處定義的方法,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 的原型繼承中的一個有趣的特性。
8.getter 與 setter 中的 this當函數在一個 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, 69.作為一個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 ; i10.作為一個內聯事件處理函數 當代碼被內聯on-event 處理函數調用時,它的this指向監聽器所在的DOM元素:上面的 alert 會顯示button。注意只有外層代碼中的this是這樣設置的:
在這種情況下,沒有設置內部函數的this,所以它指向 global/window 對象(即非嚴格模式下調用的函數未設置this時指向的默認對象)。
11.作為定時器的參數作為定時器的參數時, this 指向 windowsetInterval(function() { console.log(this); }, 1000);如果你覺得這篇文章對你有所幫助,那就順便點個贊吧,點點關注不迷路~
黑芝麻哇,白芝麻發,黑芝麻白芝麻哇發哈!
前端哇發哈
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103394.html
摘要:在中,通過棧的存取方式來管理執行上下文,我們可稱其為執行棧,或函數調用棧。因為執行中最先進入全局環境,所以處于棧底的永遠是全局環境的執行上下文。 一、什么是執行上下文? 執行上下文(Execution Context): 函數執行前進行的準備工作(也稱執行上下文環境) JavaScript在執行一個代碼段之前,即解析(預處理)階段,會先進行一些準備工作,例如掃描JS中var定義的變量、...
摘要:由此可知閉包是函數的執行環境以及執行環境中的函數組合而構成的。此時產生了閉包。二閉包的作用閉包的特點是讀取函數內部局部變量,并將局部變量保存在內存,延長其生命周期。三閉包的問題使用閉包會將局部變量保持在內存中,所以會占用大量內存,影響性能。 一、什么是閉包 1.閉包的定義 閉包是一種特殊的對象。它由兩部分構成:函數,以及創建該函數的環境(包含自由變量)。環境由閉包創建時在作用域中的任何...
摘要:令人困惑的是,文檔中稱,指定的回調函數,總是排在前面。另外,由于指定的回調函數是在本次事件循環觸發,而指定的是在下次事件循環觸發,所以很顯然,前者總是比后者發生得早,而且執行效率也高因為不用檢查任務隊列。 一、定時器 除了放置異步任務的事件,任務隊列還可以放置定時事件,即指定某些代碼在多少時間之后執行。這叫做定時器(timer)功能,也就是定時執行的代碼。 定時器功能主要由setTim...
摘要:使用指定的參數調用構造函數,并將綁定到新創建的對象。由構造函數返回的對象就是表達式的結果。情況返回以外的基本類型實例中只能訪問到構造函數中的屬性,和情況完全相反,結果相當于沒有返回值。 定義 new 運算符創建一個用戶定義的對象類型的實例或具有構造函數的內置對象的實例。 ——(來自于MDN) 舉個栗子 function Car(color) { this.color = co...
摘要:腳本執行,事件處理等。引擎線程,也稱為內核,負責處理腳本程序,例如引擎。事件觸發線程,用來控制事件循環可以理解為,引擎線程自己都忙不過來,需要瀏覽器另開線程協助。異步請求線程,也就是發出請求后,接收響應檢測狀態變更等都是這個線程管理的。 一、進程與線程 現代操作系統比如Mac OS X,UNIX,Linux,Windows等,都是支持多任務的操作系統。 什么叫多任務呢?簡單地說,就是操...
閱讀 2906·2023-04-26 01:01
閱讀 3682·2021-11-23 09:51
閱讀 2513·2021-11-22 14:44
閱讀 3541·2021-09-23 11:57
閱讀 2826·2021-09-22 14:58
閱讀 5866·2021-09-10 11:25
閱讀 2099·2019-08-30 13:11
閱讀 1588·2019-08-30 12:59