Function.prototype.bind()
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
语法
bind(thisArg)
bind(thisArg, arg1)
bind(thisArg, arg1, arg2)
bind(thisArg, arg1, arg2, /* …, */ argN)
参数
返回值
给定函数的副本,具有指定的 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 又会调用 fn。fn 最终收到的参数顺序是:由 boundFn 绑定的参数,由 boundFn2 绑定的参数,以及调用 boundFn2 时收到的参数。
"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 是透明的。)
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 的基类。
class Derived extends class {}.bind(null) {}
// TypeError: Class extends value does not have valid prototype property undefined
当使用绑定函数作为 instanceof 的右侧操作数时,instanceof 会查找目标函数(它存储在绑定函数内部)并读取其 prototype。
class Base {}
const BoundBase = Base.bind(null, 1, 2);
console.log(new Base() instanceof BoundBase); // true
绑定函数具有以下属性
示例
创建绑定函数
bind() 最常见的用途是创建一个函数,无论如何调用它,它都使用特定的 this 值进行调用。
新 JavaScript 程序员常犯的一个错误是提取对象的某个方法,然后稍后调用该函数,并期望它使用原始对象作为其 this(例如,在基于回调的代码中使用该方法)。
然而,如果不采取特殊措施,通常会丢失原始对象。使用原始对象从函数创建绑定函数可以很好地解决这个问题。
// 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
注意: 如果你在 严格模式 下运行此示例,retrieveX 的 this 参数将绑定到 undefined 而不是 globalThis,导致 retrieveX() 调用失败。
如果你在 ECMAScript 模块中运行此示例,顶层 this 将绑定到 undefined 而不是 globalThis,导致 this.x = 9 赋值失败。
如果你在 Node CommonJS 模块中运行此示例,顶层 this 将绑定到 module.exports 而不是 globalThis。然而,在非严格模式下(默认),retrieveX 的 this 参数仍将绑定到 globalThis,而在严格模式下将绑定到 undefined。因此,在非严格模式下(默认),retrieveX() 调用将返回 undefined,因为 this.x = 9 写入的对象(module.exports)与 getX 读取的对象(globalThis)不同。
事实上,一些内置的“方法”也是返回绑定函数的 getter - 一个值得注意的例子是 Intl.NumberFormat.prototype.format(),当访问它时,它会返回一个绑定函数,你可以直接将其作为回调传递。
部分应用函数
bind() 的另一个用途是创建一个具有预设初始参数的函数。
这些参数(如果有)紧跟在提供的 this 值之后,然后被插入到目标函数参数的开头,后面是调用绑定函数时传递的任何参数。
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 绑定到回调函数,以保持实例的正确性。
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()'
你也可以为此目的使用 箭头函数。
class LateBloomer {
bloom() {
// Declare bloom after a delay of 1 second
setTimeout(() => this.declare(), 1000);
}
}
用作构造函数的绑定函数
绑定函数自动适合与 new 操作符一起使用,以构造由目标函数创建的新实例。当使用绑定函数构造值时,提供的 this 会被忽略。但是,提供的参数仍会被添加到构造函数调用之前。
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.target、instanceof、this 等都像预期一样工作,就像构造函数从未被绑定过一样。唯一的区别是它不能再用于 extends。
反之,你也不需要做任何特殊处理来创建一个仅用 new 调用,或者仅在不使用 new 的情况下调用的绑定函数。如果你在没有 new 的情况下调用它,绑定的 this 将突然不再被忽略。
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() 会保留类的大部分语义,除了当前类的所有静态自有属性都会丢失。但是,因为原型链得以保留,你仍然可以访问从父类继承的静态属性。
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() 为例,你想用它将一个类数组对象转换为一个真正的数组。你可以创建一个快捷方式,如下所示
const slice = Array.prototype.slice;
// …
slice.call(arguments);
请注意,你不能保存 slice.call 并将其作为普通函数调用,因为 call() 方法也会读取其 this 值,该值是它应该调用的函数。在这种情况下,你可以使用 bind() 来绑定 call() 的 this 值。在下面的代码片段中,slice() 是 Function.prototype.call() 的绑定版本,其 this 值绑定到 Array.prototype.slice()。这意味着可以消除额外的 call() 调用
// 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 |
浏览器兼容性
加载中…