什麼是this?深入解析JavaScript中的this
什麼是this?以下這篇文章跟大家介紹一下JavaScript中的this,並聊聊this在函數不同呼叫方式下的區別,希望對大家有所幫助!
JavaScript
中的this
#格外的不一樣,例如Java
語言中的 this
是在程式碼的執行階段是不可更改,而JavaScript的this
是在呼叫階段進行綁定。因為這性質所以給了this
很大的發揮空間。但在嚴格模式和非嚴格模式下又有些不一樣,在函數的不同呼叫方式也導致this有些差別。
什麼是this?
首先對this的下個定義:this是在執行上下文創建時確定的一個在執行過程中不可更改的變數。
所謂執行上下文,就是JavaScript引擎在執行一段程式碼內部會用到的一些變數、函數 、this提前宣告然後儲存在變數物件中的過程。這個'程式碼片段'包括:全域程式碼(script標籤內部的程式碼)、函數內部程式碼、eval內部程式碼。而我們所熟知的作用域鏈也會在保存在這裡,以一個類別數組的形式儲存在對應函數的[[Scopes]]屬性中。
this只在函數呼叫階段確定,也就是執行上下文創建的階段進行賦值,保存在變數物件中。這個特性也導致了this的多變性:即當函數在不同的呼叫方式下都可能會導致this的值不同。
上面我們說過了在嚴格模式下和非嚴格模式下this表現不同:
var a = 1; function fun() { 'use strict'; var a = 2; return this.a; } fun();//报错 Cannot read property 'a' of undefined
嚴格模式下,this指向undefined;
var a = 1; function fun() { var a = 2; return this.a; } fun();//1
非嚴格模式下this指向window;
#上面同一段程式碼,在不同模式下之所以有不同表現,就是因為this在嚴格模式,非嚴格模式的不同。
結論:當函數獨立呼叫的時候,在嚴格模式下它的this指向undefined,在非嚴格模式下,當this指向undefined的時候,自動指向全域物件(瀏覽器中就是window)
多提一句,在全域環境下,this就是指向自己,再看一個例子:
this.a = 1; var b = 1; c = 1; console.log(this === window)//true //这三种都能得到想要的结果,全局上下文的变量对象中存在这三个变量
再多提一句,當this不在函數中用的時候會怎樣?看一個例子:
var a = 1000; var obj = { a: 1, b: this.a + 1 } function fun() { var obj = { a: 1, c: this.a + 2 //严格模式下这块报错 Cannot read property 'a' of undefined } return obj.c; } console.log(fun());//1002 console.log(obj.b);//1001
這種情況下this還是指向了window。那我們可以單獨下個結論:
當obj在全域宣告的時候,obj內部屬性中的this指向全域對象,當obj在一個函數中宣告的時候,嚴格模式下this會指向undefined,非嚴格模式會自動轉為指向全域物件。
好了,剛剛小試牛刀下,知道了嚴格模式和非嚴格模式下this的區別,然而我們日常應用最多的還是在函數中用this,上面也說過了this在函數的不同呼叫方式還有差別,那麼函數的呼叫方式都有哪些呢?四種:
- 在全域環境或是普通函數中直接呼叫
- 作為物件的方法
- 使用apply和call
- 作為構造函數
以下分別就四種情況展開:
直接呼叫
上面的例子,其實就是直接呼叫的,不過我決定再寫一個例子:
var a = 1; var obj = { a: 2, b: function () { function fun() { return this.a } console.log(fun()); } } obj.b();//1
fun函數雖然在obj.b方法中定義,但它還是一個普通函數,直接調用在非嚴格模式下指向undefined,又自動指向了全局對象,正如預料,嚴格模式會報錯undefined.a不成立,a未定義。
重要的事情再說一遍:當函數獨立調用的時候,在嚴格模式下它的this指向undefined,在非嚴格模式下,當this指向undefined的時候,自動指向全局物件(瀏覽器中就是window)。
作為物件的方法
var a = 1; var obj = { a: 2, b: function() { return this.a; } } console.log(obj.b())//2
b所引用的匿名函數作為obj的一個方法調用,這時候this指向調用它的物件。這裡也就是obj。那如果b方法不作為物件方法呼叫呢?啥意思呢,就是這樣:
var a = 1; var obj = { a: 2, b: function() { return this.a; } } var t = obj.b; console.log(t());//1
如上,t函數執行結果竟然是全域變數1,為啥呢?這就涉及Javascript的記憶體空間了,就是說,obj物件的b屬性儲存的是對該匿名函數的一個引用,可以理解為一個指標。當賦值給t的時候,並沒有單獨開闢記憶體空間儲存新的函數,而是讓t儲存了一個指針,該指針指向這個函數。相當於執行了這麼一段偽代碼:
var a = 1; function fun() {//此函数存储在堆中 return this.a; } var obj = { a: 2, b: fun //b指向fun函数 } var t = fun;//变量t指向fun函数 console.log(t());//1
此時的t就是一個指向fun函數的指針,調用t,相當於直接調用fun,套用以上規則,打印出來1自然很好理解了。
使用apply,call
关于apply和call是干什么的怎么用本文不涉及,请移驾:apply,call
这是个万能公式,实际上上面直接调用的代码,我们可以看成这样的:
function fun() { return this.a; } fun();//1 //严格模式 fun.call(undefined) //非严格模式 fun.call(window)
这时候我们就可以解释下,为啥说在非严格模式下,当函数this指向undefined的时候,会自动指向全局对象,如上,在非严格模式下,当调用fun.call(undefined)的时候打印出来的依旧是1,就是最好的证据。
为啥说是万能公式呢?再看函数作为对象的方法调用:
var a = 1; var obj = { a: 2, b: function() { return this.a; } } obj.b() obj.b.call(obj)
如上,是不是很强大,可以理解为其它两种都是这个方法的语法糖罢了,那么apply和call是不是真的万能的呢?并不是,ES6的箭头函数就是特例,因为箭头函数的this不是在调用时候确定的,这也就是为啥说箭头函数好用的原因之一,因为它的this固定不会变来变去的了。关于箭头函数的this我们稍后再说。
作为构造函数
何为构造函数?所谓构造函数就是用来new对象的函数,像Function
、Object
、Array
、Date
等都是全局定义的构造函数。其实每一个函数都可以new对象,那些批量生产我们需要的对象的函数就叫它构造函数罢了。注意,构造函数首字母记得大写。
function Fun() { this.name = 'Damonre'; this.age = 21; this.sex = 'man'; this.run = function () { return this.name + '正在跑步'; } } Fun.prototype = { contructor: Fun, say: function () { return this.name + '正在说话'; } } var f = new Fun(); f.run();//Damonare正在跑步 f.say();//Damonare正在说话
如上,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。为啥呢?new做了啥呢?
伪代码如下:
function Fun() { //new做的事情 var obj = {}; obj.__proto__ = Fun.prototype;//Base为构造函数 obj.name = 'Damonare'; ...//一系列赋值以及更多的事 return obj }
也就是说new做了下面这些事:
- 创建一个临时对象
- 给临时对象绑定原型
- 给临时对象对应属性赋值
- 将临时对象return
也就是说new其实就是个语法糖,this之所以指向临时对象还是没逃脱上面说的几种情况。
当然如果直接调用Fun(),如下:
function Fun() { this.name = 'Damonre'; this.age = 21; this.sex = 'man'; this.run = function () { return this.name + '正在跑步'; } } Fun(); console.log(window)
其实就是直接调用一个函数,this在非严格模式下指向window,你可以在window对象找到所有的变量。
另外还有一点,prototype对象的方法的this指向实例对象,因为实例对象的__proto__
已经指向了原型函数的prototype。这就涉及原型链的知识了,即方法会沿着对象的原型链进行查找。
箭头函数
刚刚提到了箭头函数是一个不可以用call和apply改变this的典型。
我们看下面这个例子:
var a = 1; var obj = { a: 2 }; var fun = () => console.log(this.a); fun();//1 fun.call(obj)//1
以上,两次调用都是1。
那么箭头函数的this是怎么确定的呢?箭头函数会捕获其所在上下文的 this
值,作为自己的 this
值,也就是说箭头函数的this在词法层面就完成了绑定。apply,call方法只是传入参数,却改不了this。
var a = 1; var obj = { a: 2 }; function fun() { var a = 3; let f = () => console.log(this.a); f(); }; fun();//1 fun.call(obj);//2
如上,fun直接调用,fun的上下文中的this值为window,注意,这个地方有点绕。fun的上下文就是此箭头函数所在的上下文,因此此时f的this为fun的this也就是window。当fun.call(obj)再次调用的时候,新的上下文创建,fun此时的this为obj,也就是箭头函数的this值。
再来一个例子:
function Fun() { this.name = 'Damonare'; } Fun.prototype.say = () => { console.log(this); } var f = new Fun(); f.say();//window
有的同学看到这个例子会很懵逼,感觉上this应该指向f这个实例对象啊。不是的,此时的箭头函数所在的上下文是__proto__
所在的上下文也就是Object函数的上下文,而Object的this值就是全局对象。
那么再来一个例子:
function Fun() { this.name = 'Damonare'; this.say = () => { console.log(this); } } var f = new Fun(); f.say();//Fun的实例对象
如上,this.say所在的上下文,此时箭头函数所在的上下文就变成了Fun的上下文环境,而因为上面说过当函数作为构造函数调用的时候(也就是new的作用)上下文环境的this指向实例对象。
【相关推荐:javascript学习教程】
以上是什麼是this?深入解析JavaScript中的this的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數
