Javascript的this詳解

来源:https://www.cnblogs.com/good10000/archive/2019/03/24/10590156.html
-Advertisement-
Play Games

在理解javascript的this之前,首先先瞭解一下作用域。 作用域分為兩種: 詞法作用域和動態作用域的區別是:詞法作用域是在寫代碼或定義時確定的;動態作用域是在運行時確定的。 this的綁定規則 this是在調用時被綁定,取決於函數的調用位置。由此可以知道,一般情況下(非嚴格模式下),this ...


在理解javascript的this之前,首先先瞭解一下作用域。

作用域分為兩種:

  1. 1、詞法作用域:引擎在當前作用域或者嵌套的子作用域查找具有名稱標識符的變數。(引擎如何查找和在哪查找。定義過程發生在代碼書寫階段)
  2. 2、動態作用域:在運行時被動態確定的作用域。

詞法作用域和動態作用域的區別是:詞法作用域是在寫代碼或定義時確定的;動態作用域是在運行時確定的。

this的綁定規則

this是在調用時被綁定,取決於函數的調用位置。由此可以知道,一般情況下(非嚴格模式下),this都會根據函數調用(調用棧)的上下文來綁定對象。

一、jQuery特效預設綁定

預設綁定:預設綁定是指在非嚴格模式下,且沒有使用別的綁定規則時,this根據函數調用(調用棧)的上下文來綁定對象(全局對象)。(嚴格模式下則綁定undefined)

舉個慄子:

?
1 2 3 4 5 6 7 8 9 function foo() {   console.log(this.a); }; function bar() {   var a = 3;   foo(); } var a = 2; bar(); //調用棧在全局作用域,this綁定全局對象

運行結果為: 2

//加上"use strict"運行結果則會變成this is undefined

這裡的函數調用時,使用了預設綁定,函數調用(調用棧)的上下文是全局作用域,因此this綁定了全局對象(global)。

eg2:

?
1 2 3 4 5 6 7 8 function foo() {   console.log(this.a) }; var a = 2; (function() {   "use strict"   foo(); })();

運行結果為: 2

 這裡需要註意:對於預設綁定,決定this綁定對象的不是調用位置是否處於嚴格模式,而是函數體是否處於嚴格模式(函數體處於嚴格模式則this綁定undefined;否則this綁定全局對象)。另外:嚴格模式和非嚴格模式雖然有可能可以綁定,但是最好不混用。

間接引用一般也是會應用預設綁定規則。

?
1 2 3 4 5 6 7 8 function foo() {   console.log(this.a); }; var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo();  //3 (p.foo = o.foo)(); //2

賦值表達式 p.foo = o.foo的返回值是直接引用目標函數foo。

二、響應式圖片隱式綁定

隱式綁定:由上下文對象調用,綁定到上下文對象。

舉個慄子:

?
1 2 3 4 5 6 7 8 9 function foo() {   console.log(this.a); }; var obj = {   a: 2,   foo: foo }; obj.foo();  //2 foo();    //undefined

這段幻燈片代碼中,foo()被當做引用屬性添加到obj對象中,obj調用這個引用屬性函數時,會使用該引用屬性上下文,this會被綁定到obj對象。(這個函數嚴格來說不屬於obj對象,只是作為引用屬性)。屬於隱式綁定。

而下麵foo()函數的直接執行,並不是obj對象引用,所以上下文對象是全局對象。故this綁定了undefined。屬於預設綁定。

對象引用鏈中只有上一層或者說最後一層在調用位置中起作用。

註意:

1.隱式綁定的函數會丟失綁定對象。此時它會應用預設綁定,將this綁定到全局對象或者undefined上,取決於是否是嚴格模式。

eg:

?
1 2 3 4 5 6 7 8 9 10 function foo() {   console.log(this.a); }; var obj = {   a: 2;   foo: foo } var bar = obj.foo; var a = 'biubiubiu'; bar();

運行結果:"biubiubiu"

解析:看似bar是obj.foo的一個引用,實際上bar是直接引用了函數foo,是一個單純的函數調用,故實為預設綁定。

2.參數傳遞就是隱式賦值,因此傳入函數時也會被隱式賦值。

eg:

?
1 2 3 4 5 6 7 8 9 10 11 12 function foo() {   console.log(this.a); }; var obj = {   a: 2,   foo: foo }; function bar(fn) {   fn(); }; var a = "biubiubiu"; bar(obj.foo);

運行結果: "biubiubiu"

解析:實際上參數也是隱式賦值,但是參數傳入函數中,併在函數中執行。此時也是直接引用了函數foo,因此也是單純的函數調用,採用了預設綁定。

3.把函數傳入語言內置函數。(與上面情況基本相似,將自己聲明函數改成語言內置函數)回調函數丟失this的情況比較常見,況且還有調用回調函數的函數可能還會修改this。

三、分頁插件顯式綁定

顯式綁定:直接將this綁定到指定對象上。Javascript中絕大多數函數和自己所創建的函數都可以使用這兩種顯式綁定的方法。

1、.call()
2、.apply()

這兩種綁定方法,第一個參數是this綁定的對象。(如果傳入的參數是原始值(字元串類型、布爾類型、數字類型),這個原始值就會被轉成對象形式(new String、new Boolean、new Number)這個稱為:裝箱)

舉個慄子:

?
1 2 3 4 5 6 7 function foo() {   console.log(this.a); }; var obj = {   a: 2 }; foo.call(obj);

運行結果: 2

 然鵝,顯示綁定並不能解決綁定丟失的問題。這個時候來了一位新朋友 -- 硬綁定(bind)。

3、.bind() (硬綁定是常見場景,故es5提供了該內置方法 Function.prototype.bind。)
bind()會返回一個新編碼函數,把this綁定在指定參數上,並調用函數。

舉個慄子:

?
1 2 3 4 5 6 7 8 9 10 function foo(e) {   console.log(this.a + e);   return this.a + e; }; var obj = {   a: 2 } var bar = foo.bind(obj); //新編碼函數 var b = bar(3); // 2 3 console.log(b); // 5

bind()還有一個功能:將除了第一個用於綁定this的參數之外的其他參數傳給下層的函數(部分應用,是“柯里化”的一種)。

這裡涉及到一個概念:把null或者undefined作為this的綁定對象傳入call、apply、bind,這些值在調用的時候會被忽略,實際應用預設綁定規則。
應用場景:

  1. 1、使用apply()展開一個數組,並作為參數傳遞給一個函數。
  2. 2、bind()對參數進行柯里化(預先設置一些參數)。

舉個慄子:

?
1 2 3 4 5 6 7 8 function foo(a,b) {   console.log("a:" + a + ",b:" + b); }; //數組“展開”成參數 foo.apply(null,[2,3]); //a:2,b:3 //bind()柯里化 var bar = foo.bind(null,2); bar(3); //a:2,b:3

解析:傳入一個參數作為this綁定對象,如果不傳則使用占位符(null),此時會使用預設綁定規則。

上面這個例子可能會產生一定的副作用,如果需要運用這種場景並且更加安全。可以創建一個空對象(可以用任意喜歡的名字來命名)。

?
1 2 3 4 5 var ∅ = Object.create(null); //上面這個例子就可以改寫為: foo.apply(∅,[2,3]); //a:2,b:3 var bar = foo.bind(∅,2); bar(3); //a:2,b:3

註意:硬綁定之後不能使用隱式綁定和顯式綁定對this進行修改
在這裡介紹一種軟綁定的方法softBind(),檢查this綁定到全局對象或者undefined後,綁定this到指定的預設對象。綁定後效果和硬綁定一樣,但是保留隱式綁定或者顯式綁定修改this的能力。

四、new綁定

Javascript中的new機制與面向類語言的完全不同。在Javascript中,構造函數只是一些使用new操作符時被調用的函數,不屬於一個類,也不會實例化一個類。稱為對函數的“構造調用”。

舉個慄子:

?
1 2 3 4 5 function foo(a) {   this.a = a; } var bar = new foo(2); console.log(bar.a); //2

使用new的過程會創建一個全新的對象,this會綁定這個新對象。如果函數沒有返回其他對象,則new表達式函數調用會返回該新對象。(這個新對象會連接prototype)

四種綁定規則的優先順序為:new>顯式>隱式>預設

箭頭函數

箭頭函數是根據外層作用域(函數或全局)來決定this。(詞法作用域取代this機制)
箭頭函數this會綁定調用時的對象,且箭頭函數的綁定無法修改(new也不行)。

其實可以理解為,箭頭函數的this在詞法上繼承的是它所在的作用域(函數或全局)的this,而它繼承的函數作用域的this綁定的是在該函數調用上下文對象,所以箭頭函數的this間接的綁定在調用上下文對象。

簡述: 箭頭函數this(綁定作用域this)-- 作用域this(綁定在調用上下文對象)。

故:箭頭函數this == 調用的上下文對象

舉個慄子:

?
1 2 3 4 5 6 7 8 function foo() {   setTimeout(function() {     //這裡的this在詞法上繼承自foo()     console.log(this.a);   },100); }; var obj = { a: 2 }; foo.call(obj); //2

其實這個慄子也等價於:

?
1 2 3 4 5 6 7 function foo() {   var that = this; //lexical capture of this   setTimeout(function() {     console.log(self.a)   },100); } ...與上面一樣

所以,有兩種風格:this風格(四種規則)和詞法作用域風格(that = this和箭頭函數)可供使用。使用時儘量避免混用,否則會造成難以維護的後果。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 事件綁定 1.bind(type,[data],fn) --type: 含有一個或多個事件類型的字元串,由空格分隔多個事件。比如"click"或"submit",還可以是自定義事件名。 --data:作為event.data屬性值傳遞給事件對象的額外數據對象 --fn:綁定到每個匹配元素的事件上面的 ...
  • 準備 語言:TypeScript 工具:Visual Studio Code 演示:Audio Visualiazer 小明告訴我,他希望打開一個網頁,立即聽到他喜歡的音樂,如果有視覺特效就更棒了。 第一節 本地音頻 “可是我的電腦里沒有 MP3 文件!” “為什麼需要打開一個本地文件?”小明問。 ...
  • webpack 什麼是webpack 官方解釋:webpack 是一個現代 JavaScript 應用程式的靜態模塊打包器(module bundler)。什麼意思呢?就是可以把你開發項目時用到的所有資源全部打包成一個js文件,然後項目在上線階段引入這個js文件就行了,它會自動幫你展出你... ...
  • js的運算符 算術運算符 加法 + 減法 - 乘法 * 除法 / 餘數 % number類型的和number類型的 number類型和boolean類型(true--1,false--0) number類型和string類型(+,-,*,/) string類型和string類型的數字(+,-,*,/ ...
  • 問題: 在網頁的發展歷程中,發現網頁不能對用戶的數據進行自動校驗,和提供一些特效。 解決: 使用javascript。 作用 可以讓網頁和用戶進行直接簡單的交互。 可以讓網頁製作特效和動畫。 聲明js代碼域 1.<script type="text/javascript"></script> 2.< ...
  • 本文介紹了Canvas引入跨域的圖片導致toDataURL()報錯的問題的解決,分享給大家,具體如下: 【場景】 用戶打開網頁,則請求騰訊COS(圖片伺服器)上的圖片js代碼。使用canvas繪圖。 然後,用戶可以重新選擇圖片、裁剪、上傳。 【問題】 圖片首次載入,選擇新圖片後裁剪、繪製都沒有問題。 ...
  • 以下為你簡略介紹javaScript語法中創建對象的內容。 主要包括:字面量模式創建、調用系統構造函數創建、工廠模式創建、自定義構造函數創建、原型模式創建共五種。 ...
  • 有一天我們的UI設計師找到我說,要把頁面中我自己用程式寫的動畫,換成他們給的json動畫,原因是有的動畫很複雜,自己寫起來達不到他們的預期效果(寫到這裡我突然想到一個問題,這麼複雜的動畫為什麼不使用gif。。。。坐我對面的安卓開發小哥答因為gif播放的時候可能質量不高不流暢,好吧我信了) 我:??? ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...