搜尋
首頁web前端js教程深入解析JavaScript程式設計中的this關鍵字使用_基礎知識

JavaScript 裡的 this 到底指得是什麼?很多人會告訴你 this 指的是當前物件。這樣理解對麼?在大多數情況下確實沒錯。例如我們常常會在網頁上寫這樣的 JavaScript:

<input type="submit" value="提交" onclick="this.value='正在提交数据'" />

這裡的this顯然指的是當前對象,也就是這個提交按鈕。通常,我們使用this的情況都與此類似。但是有什麼情況不是這樣的呢?

大家看看這個例子:

var foo = function() {
  console.log(this);
}
foo();
new foo();

 

比較一下 foo() 和 new foo() 的運行結果,你會發現,前者 this 指向的並非 foo 本身,而是當前頁面的window對象,而後者才真正的指向foo。這是為什麼呢?

其實這牽涉到JavaScript的一個重要特性,就是所謂的「閉包」。閉包這個概念說複雜也不複雜,但也不是簡單到可以用一兩句話說清楚。偶會在以後的文章中深入探討這個Javascript 最重要的特性。現在,我要告訴大家的是,因為閉包的存在,JavaScript中的作用域變得相當重要。

所謂的作用域,簡單的說,就是創建一個函數時在什麼環境下創建的。而this變數的值,如果沒有指定的話,就是函數目前的作用域。

在前面的例子裡,foo() 函數是在全域作用域(這裡就是window物件),所以this的值是目前的window物件。而透過 new foo() 這樣的形式,其實是創建了一個foo()的副本,並在這個副本上進行的操作,所以這裡的this就是foo()的這個副本。

這樣講可能有點抽象,大家來看個實際的例子:

<input type="button" id="aButton" value="demo" onclick="" />
<script type="text/javascript">
function demo() {
  this.value = Math.random();
}
</script>

 

如果直接呼叫demo() 函數,程式就會報錯,因為demo函數是在window物件中定義的,所以demo的擁有者(作用域)是window,demo的this也是window。而window是沒有value屬性的,所以就報錯了。

2015119174624685.png (391×372)

如果我們透過建立副本的方式,將這個函數的副本加入到一個HTML元素,那麼他的擁有者就成了這個元素,this也指涉了這個元素:

document.getElementById("aButton").onclick = demo;

這樣就將aButton的onlick屬性設定為demo()的一個副本,this也指向了aButton。

2015119174656357.png (391×373)

你甚至可以為多個不同的HTML元素建立不同的函數副本。每個副本的擁有者都是相對應的HTML元素,各自的this也都指向他們的擁有者,不會造成混亂。

2015119174716805.png (391×482)

但是,如果你這樣定義某個元素的onlick事件:

<input type="button" id="aButton" value="demo" onclick="demo()" />

 

點擊這個button之後,你會發現,程式又會報錯了──this又指向了window!

其實,這種方法並沒有為程式建立一個函數,而只是引用了這個函數。

具體看一下差異吧。

使用建立函數副本的方法:

<input type="button" id="aButton" value="demo" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
  this.value = Math.random();
}
button.onclick= demo;
alert(button.onclick);
</script>

得到的輸出是:

function demo() {
  this.value = Math.random();
}

 使用函數引用的方法:

<input type="button" id="aButton" value="demo" onclick="demo()" />

得到的輸出是:

function onclick() {
  demo();
}

這樣就能看出差別了吧。函數引用的方式中,onclick事件只是直接呼叫demo()函數,而demo()函數的作用域仍舊是window對象,所以this仍然指向window。

2015119174740033.png (391×368)

這樣就又引出了一個問題:既然函數副本這麼好用,為什麼還需要函數引用的方法呢?答案是性能。每新建一個函數的副本,程式就會為這個函數副本分配一定的記憶體。而實際應用中,大多數函數不一定會被調用,於是這部分記憶體就被白白浪費了。而使用函數引用的方式,程式只會給函數的本體分配內存,而引用只分配指針,這樣效率就高很多。程式設計師麼,節約為主,恩

所以我們來看一個更好的解決方案:

<script type="text/javascript">
function demo(obj) {
  obj.value = Math.random();
}
</script>
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />

 

这样,效率和需求就都能兼顾了。

 
this的指向
JavaScript由于其在运行期进行绑定的特性,JavaScript 中的 this 可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。常言道,字不如表,表不如图。为了让人更好的理解JavaScript this 到底指向什么?下面用一张图来进行解释:

2015119174758093.jpg (1251×421)

上图我称之为”JavaScript this决策树“(非严格模式下)。下面通过例子来说明这个图如何来帮助我们对this进行判断:

var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
   this.x = this.x + x; 
   this.y = this.y + y; 
   } 
 };
//决策树解释:point.moveTo(1,1)函数不是new进行调用,进入否决策,
//是用dot(.)进行调用,则指向.moveTo之前的调用对象,即point
point.moveTo(1,1); //this 绑定到当前对象,即point对象

point.moveTo()函数在 “JavaScript this决策树“中进行判定的过程是这样的:

1)point.moveTo函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)point.moveTo函数是用dot(.)进行调用的,即进入“是”分支,即这里的this指向point.moveTo中.之前的对象point;

图解point.moveTo函数的this指向什么的解析图如下图所示:

2015119175054859.jpg (1245×416)

再举例,看下面的代码:

function func(x) { 
 this.x = x; 
 } 
func(5); //this是全局对象window,x为全局变量
//决策树解析:func()函数是用new进行调用的么?为否,进入func()函数是用dot进行调用的么?为否,则 this指向全局对象window
x;//x => 5

func()函数在 “JavaScript this决策树“中进行判定的过程是这样的:

1)func(5)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)func(5)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

图解func函数的this指向什么的解析图如下图所示:

2015119175222664.jpg (1242×416)

针对作为函数直接调用的方式,下面看一个复杂的例子:

var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
   // 内部函数
   var moveX = function(x) { 
   this.x = x;//this 指向什么?window
  }; 
  // 内部函数
  var moveY = function(y) { 
  this.y = y;//this 指向什么?window
  }; 
  moveX(x); 
  moveY(y); 
  } 
 }; 
 point.moveTo(1,1); 
 point.x; //=>0 
 point.y; //=>0 
 x; //=>1 
 y; //=>1

point.moveTo(1,1)函数实际内部调用的是moveX()和moveY()函数, moveX()函数内部的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)moveX(1)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)moveX(1)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

下面看一下作为构造函数调用的例子:

function Point(x,y){ 
  this.x = x; // this &#63;
  this.y = y; // this &#63;
 }
var np=new Point(1,1);
np.x;//1
var p=Point(2,2);
p.x;//error, p是一个空对象undefined
window.x;//2

Point(1,1)函数在var np=new Point(1,1)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)var np=new Point(1,1)调用是用new进行调用的么?这个明显是,进入“是”分支,即this指向np;

2)那么this.x=1,即np.x=1;

Point(2,2)函数在var p= Point(2,2)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)var p= Point(2,2)调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)Point(2,2)函数不是用dot(.)进行调用的?判定为否,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

3)this.x=2即window.x=2.

最后看一下函数用call 和apply进行调用的例子:

function Point(x, y){ 
  this.x = x; 
  this.y = y; 
  this.moveTo = function(x, y){ 
    this.x = x; 
    this.y = y; 
  } 
 } 

var p1 = new Point(0, 0); 
var p2 = {x: 0, y: 0}; 
p1.moveTo.apply(p2, [10, 10]);//apply实际上为p2.moveTo(10,10)
p2.x//10

p1.moveTo.apply(p2,[10,10])函數在 “JavaScript this決策樹“中進行判定的過程是這樣的:

我們知道,apply 和 call 這兩個方法異常強大,他們允許切換函數執行的上下文環境(context),即 this 綁定的物件。 p1.moveTo.apply(p2,[10,10])其實是p2.moveTo(10,10)。則p2.moveTo(10,10)可解釋為:

1)p2.moveTo(10,10)函數呼叫是用new呼叫的麼?這個明顯不是,進入「否」分支,即函數是否用dot(.)進行呼叫? ;

2)p2.moveTo(10,10)函數是用dot(.)進行呼叫的,即進入「是」分支,即這裡的this指向p2.moveTo(10,10)中.之前的物件p2 ,所以p2.x=10;

關於JavaScript函數執行環境的過程,IBM developerworks文件庫中的一段描述感覺很不錯,摘抄如下:

「JavaScript 中的函數既可以被當作普通函數執行,也可以作為物件的方法執行,這是導致this 意義如此豐富的主要原因。當一個函數被執行時,會建立一個執行環境(ExecutionContext) ,函數的所有的行為都發生在此執行環境中,建構該執行環境時,JavaScript 首先會建立arguments變量,其中包含呼叫函數時傳入的參數。函數的形參表,值為arguments變數中對應的值,如果arguments變數中沒有對應值,則該形參初始化為undefined。在該函數內定義的局部變量,需要注意的是此時這些變量初始化為undefined,其賦值操作在執行環境(ExecutionContext)創建成功後,函數執行時才會執行,這點對於我們理解JavaScript 中的變量作用域非常重要,鑑於篇幅,我們先不在這裡討論這個主題。 ExecutionContext)建立成功,函數開始逐行執行,所需變數均從先前建置好的執行環境(ExecutionContext)中讀取。 理解這段話對於理解Javascript函數將大有好處。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具