【JavaScript】你真的熟悉bind嗎

2023-07-14 18:00:27

引言

內容速遞

看了本文您能瞭解到的知識!

在本篇文章中,將帶你瞭解什麼是bindbind的用途如何手寫bind以及工作中實際使用bind的場景

在JavaScript中,bind()方法是用來建立一個新函數,並將其繫結到指定的物件上,從而在呼叫該函數時確保函數中的this關鍵字指向繫結的物件。

1、什麼是bind

bind()方法建立一個新的函數,在bind()被呼叫時,這個新函數的this被指定 bind()的第一個引數,而其餘引數將作為新函數的引數,供呼叫時使用。

MDN檔案Function.prototype.bind()

2、bind的語法

語法:

function.bind(thisArg[, arg1[, arg2[, ...]]])

引數:

  • thisArg:被繫結到函數上的物件,即當呼叫繫結後的函數時,函數中的this關鍵字會指向該物件。如果thisArg引數為nullundefined,則this關鍵字將指向全域性物件(在瀏覽器中通常是window物件)。

  • arg1, arg2, ...:要傳遞給函數的引數。這些引數將按照順序傳遞給函數,並在呼叫函數時作為函數引數使用。

返回值:

返回一個原函數的拷貝,並擁有指定的this值和初始引數。

3、淺試一下bind

程式碼:

this.name = 'guizimo'
let obj = {
  name: 'zimo',
  getName: function() {return this.name}
}

console.log(obj.getName())    // zimo

let newGetName = obj.getName
console.log(newGetName())     // guizimo

let bindGetName = newGetName.bind(obj)
console.log(bindGetName())     // zimo

簡述程式碼:

  1. 第一次列印zimo,可以理解為是列印物件內的一個屬性,此時的this是指向obj物件

  2. 第二次列印guizimo,因為當前環境是物件外,因為當前執行的函數是newGetName(),因此函數內部的this指向全域性物件。

  3. 通過bind生成一個新的拷貝函數,當前執行的函數bindGetName()this指向obj物件

4、手寫bind

這是面試官最喜歡的環節了

思路:

  • bind()方法返回一個新函數,因此需要定義一個函數來返回新函數。
  • 在新函數中,需要使用apply()call()方法來呼叫原始函數並傳遞正確的this值和引數。
  • 新函數需要接受一個thisArg引數來指定要繫結的物件,並可以接受任意數量的其他引數。

程式碼:

/**
 * 手寫bind
 * @returns {function(): any}
 */
Function.prototype.myBind = function () {
  // 處理常式
  let args = Array.from(arguments);
  let thisArg = args.shift();
  // 暫存this
  let thisFunc = this;
  // 因為需要建構函式,所以不能是匿名函數了
  const fBound = function () {
    const newArgs = args.concat(Array.from(arguments));
    // 判斷是否為建構函式
    thisArg = this instanceof fBound ? this : thisArg;
    return thisFunc.apply(thisArg, newArgs);
  }
  // 直接將原函數的prototype賦值給繫結函數
  fBound.prototype = this.prototype;
  // 返回
  return fBound;
}

簡述程式碼:

  1. 通過Array.from()arguments轉化為陣列物件,通過shift()取出thisArg
  2. 使用thisFunc暫存當前函數的this
  3. 建立一個閉包函數fBoundnewArgs接收合併處理的arguments
  4. 判斷fBound是否為建構函式,如果是建構函式,返回閉包的this,反之,返回外部拿到的thisArg,使用thisArg來接收。
  5. 使用thisFunc.apply傳遞thisArg值和引數newArgs
  6. 直接將原函數的prototype賦值給fBound
  7. 返回fBound

5、使用場景

5.1、建立繫結函數

這是bind最基本的一種使用方式了,也就是建立一個新的函數

程式碼:

this.name = 'guizimo'
let obj = {
  name: 'zimo',
  getName: function() {return this.name}
}

console.log(obj.getName())    // zimo

let newGetName = obj.getName
console.log(newGetName())     // guizimo

let bindGetName = newGetName.bind(obj)
console.log(bindGetName())     // zimo

簡述程式碼:

  1. 第一次列印zimo,可以理解為是列印物件內的一個屬性,此時的this是指向obj物件
  2. 第二次列印guizimo,因為當前環境是物件外,因為當前執行的函數是newGetName(),因此函數內部的this指向全域性物件。
  3. 通過bind生成一個新的拷貝函數,當前執行的函數bindGetName()this指向obj物件

5.2、建立偏函數

如果需要建立一個自定義函數,需要固定部分引數,那麼bind就有它獨特的作用了

程式碼:

function add (a, b) {
  return a + b
}

const res1 = add(1, 2)
console.log(res1)  // 3

// 建立一個偏函數,將1作為預設的引數
const addP = add.bind(null, 1)

const res2 = addP(2)
console.log(res2)  // 3

const res3 = addP(3)
console.log(res3)  // 4

const res4 = addP(4)
console.log(res4)  // 5

簡述程式碼:

  1. 建立了一個add 函數,呼叫add(1, 2),正常列印3
  2. 建立一個偏函數addP,將1作為預設的引數,呼叫addP(2),也可以正常列印3,後續呼叫addP(3)addp(4),皆列印正確的數值,實現了對一個引數的固定

6、在工作中有遇到bind的使用場景嗎

6.1、React中bind的場景

JSX中傳遞的事件不是一個字串,而是一個函數(如:onClick={this.handleClick}),此時onClick即是中間變數,所以處理常式中的this指向會丟失。

程式碼:

<button onClick={this.handleClick.bind(this)}>點選</button>

//此時this指向是當前範例物件
handleAdd = () => {
    console.log(this)
    this.setState({
        ...
    })
}

解決這個問題就是給呼叫函數時bind(this),從而使得無論事件處理常式如何傳遞,this指向都是當前範例化物件。或者使用箭頭函數宣告一個函數,這樣函數內的this也是指向當前範例。

6.2、在事件處理程式中存取事件目標的this值

在JavaScript中,需要在事件處理程式中存取事件目標的this值。在這種情況下,可以使用bind()方法將事件處理程式繫結到事件目標上,以便在呼叫事件處理程式時,this關鍵字始終指向事件目標。

程式碼:

const button = document.querySelector('#myButton');
button.addEventListener('click', function() {
  // 在這裡可以使用 this 存取按鈕元素的屬性和方法
}.bind(button));

部落格說明與致謝

文章所涉及的部分資料來自網際網路整理,其中包含自己個人的總結和看法,分享的目的在於共建社群和鞏固自己。

參照的資料如有侵權,請聯絡本人刪除!

感謝勤勞的自己個人部落格GitHub,公眾號【歸子莫】,小程式【子莫說】

如果你感覺對你有幫助的話,不妨給我點贊鼓勵一下,好文記得收藏喲!

幸好我在,感謝你來!