1. 程式人生 > 程式設計 >詳解React中的this指向

詳解React中的this指向

打算記流水賬一般細數一下 React 中的 this 指向問題,具體流程按事件三要素:起因,經過,結果。哈哈哈哈哈!

起因:

眾所周知,React 的設計是響應式的,使用者無需操縱 DOM,操縱資料,頁面就會渲染更新。

資料一變就更新,是更新所有的 DOM 嗎?當然不是,哪些變了就重新渲染哪些。那就要對資料變化前後的 DOM 進行比較。直接對比真實 DOM 嗎?這樣效能會很低,React 比較的是虛擬 DOM,虛擬 DOM 也是物件,只不過相較真實 DOM而言,少了很多屬性,更“輕”。

如何寫虛擬 DOM 呢?原生js我們可以使用 document.createElement() 方法,建立節點。React 中也可以通過 React.createElement(component,props,children),但是呢這種寫法遇見多層巢狀,就能讓人眼花繚亂。於是 JSX “橫空出世”,JSX 其實就是,React.createElement 的語法糖,但是我們用起來更加方便,可以直接寫成 <p id="test">hello</p> 這種形式。

但是呢問題又又來了!JSX 語法是不被 webpack 識別的,webpack 預設只能處理 .js 字尾名的檔案,所以需要藉助 Babel 這個 javascript 編譯器,而 babel 開啟了嚴格模式 **

import React,{ Component } from 'react'

export default class index extends Component {
    // speak(){
    //     console.log(this)//輸出undefined
    // }
    speak = () => console.log(this)//輸出該元件
    render() {
        return (
            <div>
                <button onClick={this.speak}>按鈕</button>
            </div>
        )
    }
}

this 本質上就是指http://www.cppcns.com向它的呼叫者,this 是在函式執行時才繫結,JS 裡邊普通函式都是 window 呼叫的,所以指向 window,開啟了嚴格模式之後是 undefined。

(function(){
    console.log(this)//window
})()

在 JSX 中傳遞的事件不是一個字串(在原生 JS 的中監聽事件,採用的是回撥函式的形式,在vue中給監聽事件傳遞的是字串變數),而是一個函式(如上面的:onClick={this.speak}),此時onClick即是中間變數,最終是由Rehttp://www.cppcns.com

act呼叫該函式,而因為開啟了嚴格模式的緣故,this 是undefined,所以處理函式中的this指向會丟失。

經過:

事實上我們需要的是 this 指向當前例項化物件,無疑會使程式碼編寫方便很多。類式元件裡邊有兩地方的this恰好指向當前例項化物件。

1.建構函式

類式元件裡面的構造器裡面的this是指向例項物件的,這是 ES6 類的特性,

眾所周知 javascript 裡面是沒有像 C+程式設計客棧+,JAVA 裡面的的類的概念,ES6 類的實現也是基於原型鏈來實現的,

在 ES6 以前例項化一個物件應該這樣:

function Animal(name,age) {
  this.name = name
  this.age = age
}
Animal.prototype.say = function () {
  console.log(this.name)
}
const Dog = new Animal('dog',3)
Dog.say()  //會在控制檯打印出dog

其中的 new 運算子,先產生了一個空物件 {},然後生成一個 this 指標,將 this 指標指向這個空物件;執行建構函式時,就相當於{}.name=dog,{}.age=3一樣的為這個物件動態新增屬性。最後將這個生成好的物件付給 Dog,

當我們使用 ES6 的 class 來宣告上面這個類的話,程式碼如下:

class Animal {
  constructor(name,age) {
    this.name = name
    this.age = age
  }
  say() {
    console.log(this.name)
  }
}
const Dog = new Animal('dog',3)
Dog.say()  //會在控制檯打印出dog

類實現和上面應該大差不差,所以this是指向例項物件的。

2.render 函式

render 函式裡面的 this,也是指向例項的。為啥呢?

首先 render 方法是在類式元件的原型上邊的,React發現元件是使用類定義的時候,後邊就會 new 出來該類的例項,注意這個例項是 React 幫你 new 出來的,隨後例項呼叫 render 方法,將虛擬 DOM 轉換為真實 DOM,所以 render 中的this 就是指向例項咯,畢竟是他呼叫的嘛!,類似的呢,render 是一個生命週期鉤子,那其他的生命週期鉤子裡面的 this也是指向例項元件的。

3.bind 和箭頭函式

解決 this 問題呢,要有兩個知識儲

(1)bind
call apply bind 都是定義在函式原型上邊的,用來改變函式 this 指向,傳入的第一個引數是 this,後面的引數就是fun1的引數

區別:

  • call 和 bind 傳給呼叫的函式是可以傳多個 apply 則是將引數放進一個數組
  • call 和 apply 返回立即執行函式,bind 返回新的函式,bind()() 也是立即執行
  • 使用 bind 繫結 this 後,該函式裡面的 this 不能變化了,不論是誰呼叫
let aa = {
    fun1: function(a,b){
        console.log(this)
        console.log(a-b);
    }
}        
let bb = {
    fun2: function(a,b){
        console.log(this)
        console.log(a+b);
    }
}

aa.fun1.call(bb,11,22);//bb-11
bb.fun2.apply(aa,[11,22]);//aa 33
aa.fun1.bind(bb,22)();//bb -11

(2)箭頭函式
箭頭函式:箭頭函式並不會建立自己的執行上下文,所以箭頭函式中的this都是外層的this,會向外作用域中,一層層查詢this,直到有 this 的定義

const A = {
    arrow:() =>{
        console.log(this)//window
    },func:function(){
        this.arrow()//window
        console.log(this)//A
        setTimeout(() => {
            console.log(this)//A
        });
    }
}
A.arrow()
A.func()

結果:

解決方法俺會兩,嘿嘿!

方法一:在建構函式中使用bind

import React,{ Component } from 'react'

export default class index extends Component {
    constructor(){
        super()
        this.speak = this.speak.bind(this)
        /*解決類中的this問題:this.speak = this.speak.bind(this),構造器裡面的this預設指向例項物件,
      例項物件通過原型鏈在類的原型上找著fnc函式,通過bind函http://www.cppcns.com數將其this指向改為例項物件,並返回一個新的函式
      再將這個新的函式給例項,並取名為fnc*/
    }
    speak(){
        console.log(this)//輸出當前例項物件
    }
    render() {
        return (
            <div>
                <button onClick={this.speak}>按鈕</button>
            </div>
        )
    }
}

方法二:將箭頭函式賦值給類的屬性

import React,{ Component } from 'react'

export default class index extends Component {
    speak = () =>{
        console.log(this)
    }
    render() {
        return (
            <div>
                <button onClick={this.speak}>按鈕</button>
            </div>
        )
    }
}//需要傳參的話,可以使用函式柯里化的思想

注意:性AsBhqEvfA能存在差異

使用箭頭函式來解決效能會比較低,因為箭頭函式不是方法,它們是匿名函式表示式,所以將它們新增到類中的唯一方法是賦值給屬性。前面介紹ES6的類的時候可以看出來,ES 類以完全不同的方式處理方法和屬性

方法被新增到類的原型中,而不是每個例項定義一次。

類屬性語法是為相同的屬性分配給每一個例項的語法糖,實際上會在 constructor裡面這樣實現:

    constructor(){
        super()
        this.speak = () => {console.log(this)}
    }

這意味著新例項被建立時,函式就會被重新定義,丟失了JS例項共享原型方法的優勢。而方法一,只是在生成例項時多了一步 bind 操作,在效率與記憶體佔用上都有極大的優勢

以上就是詳解React中的this指向的詳細內容,更多關於React中的this指向的資料請關注我們其它相關文章!