Function.prototype.bind()

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

bind() 方法是 Function 例項上的一種方法,它建立一個新函式,當使用此新函式呼叫時,會以提供的 this 值以及給定的一系列引數(在呼叫新函式時提供的引數之前)來呼叫該函式。

試一試

const module = {
  x: 42,
  getX() {
    return this.x;
  },
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// Expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// Expected output: 42

語法

js
bind(thisArg)
bind(thisArg, arg1)
bind(thisArg, arg1, arg2)
bind(thisArg, arg1, arg2, /* …, */ argN)

引數

thisArg

當呼叫繫結的函式時,要作為 this 引數傳遞給目標函式 func 的值。如果函式不在 嚴格模式 下,nullundefined 將會被替換為全域性物件,原始值將被轉換為物件。如果使用 new 運算子構造繫結的函式,則此值將被忽略。

arg1, …, argN 可選

在呼叫 func 時,要新增到傳遞給繫結函式的引數之前的引數。

返回值

給定函式的副本,具有指定的 this 值和初始引數(如果提供)。

描述

bind() 函式建立一個新的繫結函式。呼叫繫結函式通常會導致執行其包裝的函式,該函式也稱為目標函式。繫結函式會將其接收到的引數(包括 this 的值和一些初始引數)儲存為其內部狀態。這些值是預先儲存的,而不是在呼叫時傳遞的。通常可以將 const boundFn = fn.bind(thisArg, arg1, arg2) 看作等同於 const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs)(在呼叫時的效果是等同的,但在構造 boundFn 時不等同)。

繫結函式可以透過呼叫 boundFn.bind(thisArg, /* more args */) 進一步繫結,這將建立一個另一個繫結函式 boundFn2。新繫結的 thisArg 值將被忽略,因為 boundFn2 的目標函式 boundFn 已經綁定了 this。當呼叫 boundFn2 時,它會呼叫 boundFn,而 boundFn 又會呼叫 fnfn 最終收到的引數順序是:由 boundFn 繫結的引數,由 boundFn2 繫結的引數,以及呼叫 boundFn2 時收到的引數。

js
"use strict"; // prevent `this` from being boxed into the wrapper object

function log(...args) {
  console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6

如果目標函式是可構造的,繫結函式也可以使用 new 運算子進行構造。這樣做就如同目標函式被構造一樣。預置的引數會像平常一樣傳遞給目標函式,而提供的 this 值將被忽略(因為構造會準備自己的 this,正如 Reflect.construct 的引數所示)。如果繫結函式是直接構造的,new.target 將會是目標函式。(也就是說,繫結函式對 new.target 是透明的。)

js
class Base {
  constructor(...args) {
    console.log(new.target === Base);
    console.log(args);
  }
}

const BoundBase = Base.bind(null, 1, 2);

new BoundBase(3, 4); // true, [1, 2, 3, 4]

但是,因為繫結函式沒有 prototype 屬性,所以它不能用作 extends 的基類。

js
class Derived extends class {}.bind(null) {}
// TypeError: Class extends value does not have valid prototype property undefined

當使用繫結函式作為 instanceof 的右側運算元時,instanceof 會查詢目標函式(它儲存在繫結函式內部)並讀取其 prototype

js
class Base {}
const BoundBase = Base.bind(null, 1, 2);
console.log(new Base() instanceof BoundBase); // true

繫結函式具有以下屬性

length

目標函式的 length 減去繫結的引數數量(不包括 thisArg 引數),最小值為 0。

name

目標函式的 name 前面加上 "bound " 字首。

繫結函式還繼承了目標函式的原型鏈。但是,它沒有目標函式的其他自有屬性(例如,如果目標函式是類,則沒有靜態屬性)。

示例

建立繫結函式

bind() 最常見的用途是建立一個函式,無論如何呼叫它,它都使用特定的 this 值進行呼叫。

新 JavaScript 程式設計師常犯的一個錯誤是提取物件的某個方法,然後稍後呼叫該函式,並期望它使用原始物件作為其 this(例如,在基於回撥的程式碼中使用該方法)。

然而,如果不採取特殊措施,通常會丟失原始物件。使用原始物件從函式建立繫結函式可以很好地解決這個問題。

js
// Top-level 'this' is bound to 'globalThis' in scripts.
this.x = 9;
const module = {
  x: 81,
  getX() {
    return this.x;
  },
};

// The 'this' parameter of 'getX' is bound to 'module'.
console.log(module.getX()); // 81

const retrieveX = module.getX;
// The 'this' parameter of 'retrieveX' is bound to 'globalThis' in non-strict mode.
console.log(retrieveX()); // 9

// Create a new function 'boundGetX' with the 'this' parameter bound to 'module'.
const boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81

注意: 如果你在 嚴格模式 下執行此示例,retrieveXthis 引數將繫結到 undefined 而不是 globalThis,導致 retrieveX() 呼叫失敗。

如果你在 ECMAScript 模組中執行此示例,頂層 this 將繫結到 undefined 而不是 globalThis,導致 this.x = 9 賦值失敗。

如果你在 Node CommonJS 模組中執行此示例,頂層 this 將繫結到 module.exports 而不是 globalThis。然而,在非嚴格模式下(預設),retrieveXthis 引數仍將繫結到 globalThis,而在嚴格模式下將繫結到 undefined。因此,在非嚴格模式下(預設),retrieveX() 呼叫將返回 undefined,因為 this.x = 9 寫入的物件(module.exports)與 getX 讀取的物件(globalThis)不同。

事實上,一些內建的“方法”也是返回繫結函式的 getter - 一個值得注意的例子是 Intl.NumberFormat.prototype.format(),當訪問它時,它會返回一個繫結函式,你可以直接將其作為回撥傳遞。

部分應用函式

bind() 的另一個用途是建立一個具有預設初始引數的函式。

這些引數(如果有)緊跟在提供的 this 值之後,然後被插入到目標函式引數的開頭,後面是呼叫繫結函式時傳遞的任何引數。

js
function list(...args) {
  return args;
}

function addArguments(arg1, arg2) {
  return arg1 + arg2;
}

console.log(list(1, 2, 3)); // [1, 2, 3]

console.log(addArguments(1, 2)); // 3

// Create a function with a preset leading argument
const leadingThirtySevenList = list.bind(null, 37);

// Create a function with a preset first argument.
const addThirtySeven = addArguments.bind(null, 37);

console.log(leadingThirtySevenList()); // [37]
console.log(leadingThirtySevenList(1, 2, 3)); // [37, 1, 2, 3]
console.log(addThirtySeven(5)); // 42
console.log(addThirtySeven(5, 10)); // 42
// (the last argument 10 is ignored)

與 setTimeout() 一起使用

預設情況下,在 setTimeout() 中,this 關鍵字將被設定為 globalThis,在瀏覽器中是 window。當使用需要 this 指向類例項的類方法時,你可能需要顯式地將 this 繫結到回撥函式,以保持例項的正確性。

js
class LateBloomer {
  constructor() {
    this.petalCount = Math.floor(Math.random() * 12) + 1;
  }
  bloom() {
    // Declare bloom after a delay of 1 second
    setTimeout(this.declare.bind(this), 1000);
  }
  declare() {
    console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
  }
}

const flower = new LateBloomer();
flower.bloom();
// After 1 second, calls 'flower.declare()'

你也可以為此目的使用 箭頭函式

js
class LateBloomer {
  bloom() {
    // Declare bloom after a delay of 1 second
    setTimeout(() => this.declare(), 1000);
  }
}

用作建構函式的繫結函式

繫結函式自動適合與 new 運算子一起使用,以構造由目標函式建立的新例項。當使用繫結函式構造值時,提供的 this 會被忽略。但是,提供的引數仍會被新增到建構函式呼叫之前。

js
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return `${this.x},${this.y}`;
};

const p = new Point(1, 2);
p.toString();
// '1,2'

// The thisArg's value doesn't matter because it's ignored
const YAxisPoint = Point.bind(null, 0 /* x */);

const axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true

請注意,你無需做任何特殊處理即可建立用於 new 的繫結函式。new.targetinstanceofthis 等都像預期一樣工作,就像建構函式從未被繫結過一樣。唯一的區別是它不能再用於 extends

反之,你也不需要做任何特殊處理來建立一個僅用 new 呼叫,或者僅在不使用 new 的情況下呼叫的繫結函式。如果你在沒有 new 的情況下呼叫它,繫結的 this 將突然不再被忽略。

js
const emptyObj = {};
const YAxisPoint = Point.bind(emptyObj, 0 /* x */);

// Can still be called as a normal function
// (although usually this is undesirable)
YAxisPoint(13);

// The modifications to `this` is now observable from the outside
console.log(emptyObj); // { x: 0, y: 13 }

如果你希望限制繫結函式只能使用 new 呼叫,或者只能在不使用 new 的情況下呼叫,目標函式必須強制執行該限制,例如透過檢查 new.target !== undefined 或使用

繫結類

對類使用 bind() 會保留類的大部分語義,除了當前類的所有靜態自有屬性都會丟失。但是,因為原型鏈得以保留,你仍然可以訪問從父類繼承的靜態屬性。

js
class Base {
  static baseProp = "base";
}

class Derived extends Base {
  static derivedProp = "derived";
}

const BoundDerived = Derived.bind(null);
console.log(BoundDerived.baseProp); // "base"
console.log(BoundDerived.derivedProp); // undefined
console.log(new BoundDerived() instanceof Derived); // true

將方法轉換為實用函式

在某些情況下,bind() 也有助於將需要特定 this 值的方法轉換為普通實用函式,該函式將先前的 this 引數作為普通引數接受。這類似於通用實用函式的工作方式:而不是呼叫 array.map(callback),你使用 map(array, callback),這允許你將 map 用於類陣列物件,而不是陣列(例如,arguments),而無需修改 Object.prototype

Array.prototype.slice() 為例,你想用它將一個類陣列物件轉換為一個真正的陣列。你可以建立一個快捷方式,如下所示

js
const slice = Array.prototype.slice;

// …

slice.call(arguments);

請注意,你不能儲存 slice.call 並將其作為普通函式呼叫,因為 call() 方法也會讀取其 this 值,該值是它應該呼叫的函式。在這種情況下,你可以使用 bind() 來繫結 call()this 值。在下面的程式碼片段中,slice()Function.prototype.call() 的繫結版本,其 this 值繫結到 Array.prototype.slice()。這意味著可以消除額外的 call() 呼叫

js
// Same as "slice" in the previous example
const unboundSlice = Array.prototype.slice;
const slice = Function.prototype.call.bind(unboundSlice);

// …

slice(arguments);

規範

規範
ECMAScript® 2026 語言規範
# sec-function.prototype.bind

瀏覽器相容性

另見