目錄
什麼是this?
直接呼叫
作為物件的方法
使用apply,call
作为构造函数
箭头函数
首頁 web前端 js教程 什麼是this?深入解析JavaScript中的this

什麼是this?深入解析JavaScript中的this

Aug 04, 2022 pm 05:02 PM
javascript this

什麼是this?以下這篇文章跟大家介紹一下JavaScript中的this,並聊聊this在函數不同呼叫方式下的區別,希望對大家有所幫助!

什麼是this?深入解析JavaScript中的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是干什么的怎么用本文不涉及,请移驾:applycall

这是个万能公式,实际上上面直接调用的代码,我们可以看成这样的:

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对象的函数,像FunctionObjectArrayDate等都是全局定义的构造函数。其实每一个函数都可以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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

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

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

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

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

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

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

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

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

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

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

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

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

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

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

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

See all articles