有關this

来源:https://www.cnblogs.com/linxiyue/archive/2019/04/01/10638809.html
-Advertisement-
Play Games

`this Javascript this this`的綁定和函數聲明的位置沒有任何關係,只取決於函數的調用方式。 綁定規則 綁定根據函數的調用方式基本上有四種規則: 全局性調用 函數的最通常用法, 代表全局對象: 作為對象的方法調用 函數作為某個對象的方法調用時, 指向這個上級對象: 當存在多重調 ...


thisJavascript函數內部的一個特殊對象,引用的是函數運行時的環境對象,也就是說,this是動態的(箭頭函數除外),是在運行時進行綁定的,並不是在編寫時綁定(箭頭函數是編寫時綁定)。 this的綁定和函數聲明的位置沒有任何關係,只取決於函數的調用方式。

綁定規則

this綁定根據函數的調用方式基本上有四種規則:

全局性調用

函數的最通常用法,this代表全局對象:

function sayColor() {
  console.log(this.color)
}
var color = 'red'
sayColor()   // red

作為對象的方法調用

函數作為某個對象的方法調用時,this指向這個上級對象:

let car = {
  color: 'black',
  sayColor
}
car.sayColor()  // black

當存在多重調用時,最後一層會決定調用位置:

let house = {
  color: 'white',
  car
}
house.car.sayColor()  // black

使用apply、call、bind方法綁定調用對象

函數可以使用apply、call、bind方法來改變調用對象。這些方法的第一個參數是一個對象,它們會把這個對象綁定到this

let bindObj = {
  color: 'green'
}
sayColor.apply(bindObj)  // green
sayColor.call(bindObj)  // green
sayColor.bind(bindObj)()  // green

第一個參數為空時,預設綁定全局對象:

sayColor.apply()  // red

構造函數調用

當函數作為構造函數被調用時,this指向創建的新對象:

function Person(color) {
  this.color = color
}
let p = new Person('yellow')
p.color  // yellow

使用new操作符調用構造函數時,首先會創建一個新對象,然後將構造函數的作用域賦給新對象,this就指向了新對象,過程與下麵代碼類似:

let o = new Object()
Person.call(o, 'yellow')
o.color  // yellow

當然,構造函數不通過new調用時,與普通函數無區別,可以理解為實際上並不存在所謂的構造函數,只有對於函數的構造調用

Person('yellow')
// this指向了全局對象window
window.color  // yellow

以上就是this綁定的四種規則,函數調用時,先找到調用位置,然後判斷需要應用四條規則中的哪一條。

應用

現在來看下實際應用的幾種特殊情況。

回調函數

當做回調函數調用時,一般是全局調用:

setTimeout(sayColor) // red 指向window

let obj = {
    color: 'green',
    say () {
        sayColor()
    }
}
obj.say() // red  指向window

但在一些上下文中會進行隱式綁定,比如事件中的this是指向於事件的目標元素的,還有一些數組的操作方法可以使用第二個參數來綁定this

[1, 2, 3].forEach(function () { console.log(this.color) })  // red red red  指向window
[1, 2, 3].forEach(function () { console.log(this.color) }, car)  // black black black  指向car對象

綁定丟失

function sayColor() {
  console.log(this.color)
}
let car = {
  color: 'black',
  sayColor
}
var color = 'red'

let alias = car.sayColor
alias() // red

上述代碼中將對象的方法賦值給新變數aliasalias函數執行時,this指向了全局對象。
函數的名字僅僅是一個包含指向函數對象的指針的變數,car對象的sayColor屬性保存在一個屬性描述符對象中:

Object.getOwnPropertyDescriptor(car, 'sayColor')
//  {value: ƒ, writable: true, enumerable: true, configurable: true}

其中描述符對象的value屬性保存了指向sayColor函數的指針,let alias = car.sayColor語句將指向sayColor函數的指針賦值給變數alias,執行alias函數就是在全局對象中執行函數sayColor
當做回調函數時:

setTimeout(car.sayColor, 500)  // red  指向window

因為Javascript中的函數參數都是按值傳遞的,上述代碼將指向sayColor函數的指針賦值給了setTimeout函數的參數,也相當於在全局環境中執行sayColor函數。

閉包

匿名函數的執行一般具有全局性,在閉包中由於編寫方式可能不會那麼明顯:

let obj = {
  color: 'green',
  say () {
    return function() {
      console.log(this.color)
    }
  }
}
obj.say()()  // red   this指向全局window

內部匿名函數是有自己的this變數的,所以無法訪問到外部函數saythis變數,我們可以將外部this變數保存於一個閉包能夠訪問的變數之中:

let obj = {
  color: 'green',
  say () { 
    let self = this
    return function() {
      console.log(self.color)
    }
  }
}
obj.say()()  // green

當然,現在可以用箭頭函數來綁定this:

let obj = {
  color: 'green',
  say () { 
    return () => {
      console.log(this.color)
    }
  }
}
obj.say()()  // green

優先順序

全局性調用優先順序是最低的。
使用apply等函數綁定this的優先順序高於對象調用:

let car = {
  color: 'black',
  sayColor
}
let bindObj = {
  color: 'green'
}

car.sayColor()  // black
car.sayColor.apply(bindObj)  // green

使用new操作符綁定高於使用apply等函數:

function Person(color) {
  this.color = color
}
let obj = {}
let bindPerson = Person.bind(obj)
let p = new bindPerson('yellow')

p.color  // yellow  this指向了創建的對象p
obj.color  // undefined  沒有指向obj

箭頭函數與this

ES6中引進了箭頭函數,可以簡化匿名函數的語法:

setTimeout(() => { console.log(this.color) }, 50)

箭頭函數內部是沒有thisargumentssupernew.target特殊變數的,訪問它們時會指向最近的外層非箭頭函數的相應變數:

function sayColor() {
  return () => {
    return () => {
      console.log(this.color)
    }
  }
}

sayColor.call({ color: 'red' })()()  // red 指向了外層sayColor函數的this對象
sayColor.call({ color: 'red' }).call({ color: 'green' })()  // red 依然指向外層sayColor函數的this對象
sayColor.call({ color: 'red' }).call({ color: 'green' }).call({ color: 'yellow' })  // red箭頭函數使用call是無法綁定this的

所以,箭頭函數可以起到固定化this指向的效果,一定程度上可以說this是靜態的,參考上面閉包的代碼:

// ES6箭頭函數
let obj = {
  color: 'green',
  sayColor () { 
    return () => {
      console.log(this.color)
    }
  }
}

// ES5
let obj = {
  color: 'green',
  sayColor () { 
    let self = this
    return function() {
      console.log(self.color)
    }
  }
}

當然,靜態並不意味著箭頭函數的this是永遠不變的,而是隨著外層函數的this變化而變化:

let obj = {
  color: 'green',
  sayColor () { 
    return () => {
      console.log(this.color)
    }
  }
}

obj.sayColor()()  // green
obj.sayColor.call({ color: 'red' })()  // red

不適用情況

在事件中想將this指向目標元素時,箭頭函數是不適用的:

btn.addEventListener('click', () => {
  console.log(this)
})

上述代碼中this指向了全局對象,而不是事件的目標元素。

將函數當做對象的方法調用並且想將this指向對象時,也是不適用的:

let obj = {
  color: 'green',
  sayColor: () => { 
    console.log(this.color)
  }
}

上述代碼中this也指向了全局對象。

總之,需要this動態時使用非箭頭函數,需要this靜態時使用箭頭函數:

function Person() {
  this.color = 'yellow'
  setTimeout(() => { console.log('person color is',this.color) }, 50)
  setTimeout(function() { console.log('global color is',this.color) }, 50)
}
var color = 'red'
new Person()
//  輸出
person color is yellow
global color is red

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

-Advertisement-
Play Games
更多相關文章
  • 寫在前面 用Python加上一些數據分析,來證明《海王》好看。 《海王》一部電影帶你重溫《馴龍高手》《變形金剛》《星球大戰》《星河戰隊》《鐵血戰士》《安德的游戲》《異形》可能還借鑒了對手的《鋼鐵俠》與《黑豹》劇情,再稍稍帶一點《大魚海棠》的味道,配上一丟丟溫子仁式恐怖片套路,優秀的商業片,應該是DC ...
  • 粘貼自Christian.Cao 博客園地址 : https://www.cnblogs.com/QQ862668193/p/6893797.html 輸入框景背景透明:<input style="background:transparent;border:1px solid #ffffff"> 鼠 ...
  • 註 : 本文章按照菜鳥教程 Flex佈局語法教程為原型稍加修改,以方便自己學習. 菜鳥教程地址:http://www.runoob.com/w3cnote/flex-grammar.html 2009年,W3C提出了一種新的方案—-Flex佈局,可以簡便、完整、響應式地實現各種頁面佈局。目前,它已經 ...
  • 2019年4月1日,我註冊了博客園的賬戶,開始記錄我的代碼日常。 學習web前端,是我之前沒有考慮過的事情。在大學渾渾噩噩度過了四年後,終於意識到就業的危機,剛入學的時候聽到學長學姐們說畢業即失業,感覺離自己很遙遠,但臨近畢業我卻親身體會到了。可能是習慣了高中老師們的督促,初入大學校園卻迷失了自我, ...
  • 使用eclipse打開,若用瀏覽器,且出現亂碼,將格式<meta charset="UTF-8">改為<meta charset="GB2316">規格顯示 博客.html(增加了即使驗證的功能) 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset ...
  • 變數的聲明 在JavaScript程式中,使用一個變數之前應當先聲明,變數時使用關鍵字var來聲明的,如下所示:var num;var sum; 也可以寫成var num,sum,avg;如果只是聲明變數而沒有給變數賦值,預設的值是undefined 可以將變數的初始賦值和變數聲明合寫在一起如下所示 ...
  • JavaScript中創建數組有兩種方式 (一)使用 Array 構造函數: (二)使用數組字面量表示法: 數組的方法有數組原型方法,也有從object對象繼承來的方法,這裡我們只介紹數組的原型方法,數組原型方法主要有以下這些: join()push()和pop()shift() 和 unshift ...
  • 前陣子一直忙著找實習,發現已經有一段時間沒寫博客了,面試很多時候會被問到模塊化,今天就讓我們一起來總結下把 一、什麼是模塊化 在js出現的時候,js一般只是用來實現一些簡單的交互,後來js開始得到重視,用來實現越來越複雜的功能,而為了維護的方便,我們也把不同功能的js抽取出來當做一個js文件,但是當 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...