網(wǎng)站首頁 編程語言 正文
為什么this會是undefined
初學React類組件時,最不爽的一點應該就是 this
指向問題了吧!初識React的時候,肯定寫過這樣錯誤的demo。
import React from 'react'; export class ReactTestClass extends React.Component { constructor(props) { super(props); this.state = { a: 1 }; } handleClick() { this.setState({a: 2}) } render() { return <div onClick={this.handleClick}>{this.state.a}</div>; } }
上面的代碼在執(zhí)行 onClick
時,就會如期遇到如下的錯誤...
?? this
丟失了。編譯React類組件時,會將 jsx
轉(zhuǎn)成 React.createElement
,并onClick
事件用對象包裹一層傳參給該函數(shù)。
// 編譯后的結(jié)果 class ReactTestClass extends _react.default.Component { constructor(props) { super(props); this.state = { a: 1 }; } handleClick() { this.setState({ a: 2 }); } render() { return /*#__PURE__*/ _react.default.createElement( "div", { onClick: this.handleClick // ? 鬼在這里 }, this.state.a ); } } exports.ReactTestClass = ReactTestClass;
寫到這里肯定會讓大家覺得是 React
在埋坑,其實不然,官方文檔有澄清:
這并不是 React
自身的行為: 這是因為 函數(shù)在 JS 中就是這么工作的。通常情況下,比如?onClick={this.handleClick}
?,你應該 bind 這個方法。
經(jīng)受過面向?qū)ο缶幊痰南炊Y,為什么還要在類中手動綁定 this
? 我們參考如下代碼
class TestComponent { logThis () { console.log(this); // 這里的 `this` 指向誰? } privateExecute (cb) { cb(); } execute () { this.privateExecute(this.logThis); // 正確的情況應該傳入 this.logThis.bind(this) } } const instance = new TestComponent(); instance.execute();
上述代碼如期打印了 undefined
。就是在 privateRender
中執(zhí)行回調(diào)函數(shù)(執(zhí)行的是 logThis
方法)時,this
變成了 undefined
。寫到這里可能有人會提出疑問,就算不是類的實例調(diào)用的 logThis
方法,那 this
也應該是 window
對象。
沒錯!在非嚴格模式下,就是 window
對象,但是(知識點) 使用了 ES6 的 class 語法,所有在 class 中聲明的方法都會自動地使用嚴格模式,故 this
就是 undefined
。
所以,在非React類組件內(nèi),有時候也得手動綁定 this
。
優(yōu)雅的@bindThis
使用 .bind(this)
render() { return <div onClick={this.handleClick.bind(this)}>{this.state.a}</div>; }
或箭頭函數(shù)
handleClick = () => { this.setState({a: 2}) }
都可以完美解決,但是早已習慣面向?qū)ο蠛拖矚g搞事情的我總覺得處理的不夠優(yōu)雅而大方。最終期望綁定this的方式如下,
import React from 'react'; import { bindThis } from './bind-this'; export class ReactTestClass extends React.Component { constructor(props) { super(props); this.state = { a: 1 }; } @bindThis // 通過 `方法裝飾器` 自動綁定this handleClick() { this.setState({ a: 2 }); } render() { return <div onClick={this.handleClick}>{this.state.a}</div>; } }
對于 方法裝飾器,該函數(shù)對應三個入?yún)?,依次?/p>
export function bindThis( target: Object, propertyKey: string, descriptor: PropertyDescriptor, ) { // 如果要返回值,應返回一個新的屬性描述器 }
target
對應的是類的 prototype
propertyKey
對應的是方法名稱,字符串類型,例如 "handleClick"
descriptor
屬性描述器
對于 descriptor
能會比較陌生,當前該屬性打印出來的結(jié)果是,
{ value: [Function: handleClick], writable: true, enumerable: false, configurable: true }
參看 MDN 上的 Object.defineProperty,我們發(fā)現(xiàn)對于屬性描述器一共分成兩種,data descriptor
和 accessor descriptor
,兩者的區(qū)別主要在內(nèi)在屬性字段上:
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
data descriptor | ? | ? | ? | ? | ? | ? |
accessor descriptor | ? | ? | ? | ? | ? | ? |
? 可以存在的屬性,? 不能包含的屬性
其中,
configurable
,表示兩種屬性描述器能否轉(zhuǎn)換、屬性能否被刪除等,默認 false
enumerable
,表示是否是可枚舉屬性,默認 false
value
,表示當前屬性值,對于類中 handleClick
函數(shù),value就是該函數(shù)本身
writable
,表示當前屬性值能否被修改
get
,屬性的 getter 函數(shù)。當訪問該屬性時,會調(diào)用此函數(shù)。執(zhí)行時不傳入任何參數(shù),但是會傳入?this
?對象(由于繼承關系,這里的this
并不一定是定義該屬性的對象)
set
,屬性的 setter 函數(shù)。當屬性值被修改時,會調(diào)用此函數(shù)。該方法接受一個參數(shù)(也就是被賦予的新值),會傳入賦值時的?this
?對象。
既然 get
函數(shù)有機會傳入 this
對象,我們就從這里入手,通過 @bindThis 裝飾器給 handleClick
函數(shù)綁定真正的 this
。
export function bindThis( target: Object, propertyKey: string, descriptor: PropertyDescriptor, ) { const fn = descriptor.value; // 先拿到函數(shù)本身 return { configurable: true, get() { const bound = fn.bind(this); // 這里的 this 是當前類的示例 return bound; } } }
bingo~~~
一個優(yōu)雅又不失功能的 @bindThis 裝飾器就這么愉快地搞定了。
參考
考慮邊界條件更為詳細的 @bindThis 版本可參考:autobind-decorator
原文鏈接:https://juejin.cn/post/7170338422165340173
相關推薦
- 2022-04-12 React工作流程及Error?Boundaries實現(xiàn)過程講解_React
- 2022-03-30 C語言中循環(huán)語句練習實例_C 語言
- 2022-05-17 springboot打包為jar
- 2022-09-06 pandas?如何將字符串映射為數(shù)字_python
- 2022-10-07 Android開發(fā)Jetpack組件Lifecycle原理篇_Android
- 2022-10-25 python庫-dotenv包?及?.env配置文件詳解_python
- 2022-07-09 JQuery中this的指向詳解_jquery
- 2022-02-14 centos7系統(tǒng)部署k8s集群詳細介紹_Linux
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支