Function.prototype.bind()
bind()
方法是 Function
实例的一个方法,它创建一个新的函数,当该函数被调用时,会调用此函数,并将 this
关键字设置为提供的值,以及在调用新函数时提供的给定参数序列。
试一试
语法
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
写入的对象与 getX
读取的对象(globalThis
)不同(module.exports
)。
事实上,一些内置的“方法”也是返回绑定函数的 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
的情况下调用它,则绑定的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 语言规范 # sec-function.prototype.bind |
浏览器兼容性
BCD 表格仅在浏览器中加载