Function Binding
JavaScript Function Binding (JavaScript函数绑定)
In this chapter, we are going to represent the function binding: a method creating a new function that, when called, has its this keyword set to the provided value.
Now, imagine that you pass object methods as callbacks, for example, to setTimeout, and there exists a known issue: losing this.
Let’s see how to fix it. (让我们看看如何修复它。)
Losing “this”
Losing “this” (失去“这个”)
You have already got acquainted with the examples of losing this. If a method is passed separately from the object, then this is lost. (您已经熟悉了丢失此功能的示例。如果方法与对象分开传递,则会丢失。)
In the example below, it is shown what can happen with setTimeout:
let site = {
siteName: "w3cdoc",
welcome() {
console.log(`Welcome to ${this.siteName}!`);
}
};
setTimeout(site.welcome, 1000); // Welcome to undefined!
The output doesn’t show “w3cdoc” as this.siteName. It shows it as undefined. The reason is that setTimeout received the function site.welcome, distinctly from the object. So, you may rewrite the last line, as follows:
let f = site.welcome;
setTimeout(f, 1000); // lost site context
The First Solution: a Wrapper
The First Solution: a Wrapper
The most natural solution is to use a wrapper, like this:
let site = {
siteName: "w3cdoc",
welcome() {
console.log(`Welcome to ${this.siteName}!`);
}
};
setTimeout(function () {
site.welcome(); // Welcome to w3cdoc!
}, 1000);
It operates as it gets the site from the external lexical environment, then calls the method regularly. (它在从外部词法环境获取站点时运行,然后定期调用该方法。)
Here is a shorter option:
setTimeout(() => site.welcome(), 1000); // Welcome to w3cdoc!
In this case, a slight vulnerability may appear in your code structure. And, if the setTimeout triggers site changes value, it can call the wrong object. Look at the following example, carefully:
let site = {
siteName: "w3cdoc",
welcome() {
console.log(`Welcome to ${this.siteName}!`);
}
};
setTimeout(() => site.welcome(), 1000);
// site’s the value changes within 1 second
site = {
welcome() {
console.log("Another site in setTimeout!");
}
};// Another site in setTimeout!
The Second Solution: Bind
The Second Solution: Bind
This solution guarantees that the problem mentioned above will not occur. (此解决方案保证上述问题不会发生。)
Functions provide a built-in method bind, allowing to fix this. (函数提供了一个内置的方法绑定,允许修复此问题。)
You can use the following basic syntax:
// more complex syntax will appear a bit later
let bindingFn = fn.bind(context);
The result of func.bind(context) is a unique function-like object, callable as function. It transparently passes the call to func setting this=context . (Func.bind (context)的结果是一个独特的类函数对象,可作为函数调用。它透明地将调用传递给func setting this = context。)
For instance:
let site = {
siteName: "w3cdoc"
};
function func() {
console.log(this.siteName);
}
let funcSite = func.bind(site);
funcSite(); // w3cdoc
All the arguments are passed to the initial func “as is.” (所有参数都传递给初始函数“按原样”。)
For example:
let site = {
siteName: "w3cdoc"
};
function func(message) {
console.log(message + ' to ' + this.siteName);
}
// bind this to site
let funcSite = func.bind(site);
funcSite("Welcome"); // Welcome to w3cdoc (argument "Welcome" is passed, and this=site)
Another example uses an object method, as follows:
let site = {
siteName: "w3cdoc",
welcome() {
console.log(`Welcome to ${this.siteName}!`);
}
};
let welcome = site.welcome.bind(site); // (*)
// it can run without an object
welcome(); // Welcome to w3cdoc!
setTimeout(welcome, 1000); // Welcome to w3cdoc!
// even if the value of site changes within 1 second
// welcome site the pre-bound value
site = {
welcome() {
console.log("Another site in setTimeout!");
}
};
In the () line, the method site.welcome is taken binding it to the site. The welcome belongs to bound functions. (在()行中,方法site.welcome将其绑定到站点。欢迎属于绑定函数。)
In the example below, you can notice that arguments are passed “as is”, and only this is fixed by bind . (在下面的示例中,您可以注意到参数按“原样”传递,只有此参数通过bind修复。)
For instance:
let site = {
siteName: "w3cdoc",
welcome(message) {
console.log(`${message} to ${this.siteName}!`);
}
};
let welcome = site.welcome.bind(site);
welcome("Welcome"); // Welcome to w3cdoc ("Welcome to" argument is passed to welcome)
Partial Functions
Partial Functions (仅部分职能)
It is possible to bind not only this but also arguments. Developers use it rarely, but at times, it can be useful. (不仅可以绑定this ,还可以绑定arguments。开发人员很少使用它,但有时它可能很有用。)
Here is the full syntax:
let bound = func.bind(context, [arg1], [arg2], ...);
It allows binding context as this and starting function arguments. (它允许绑定上下文作为this和start函数参数。)
For example, here is a multiplication function sum(a, b):
function sum(a, b) {
return a + b;
}
You can use bind for creating a function twoSum:
function sum(a, b) {
return a + b;
}
let twoSum = sum.bind(null, 2);
console.log(twoSum(3)); // = sum(2, 3) = 5
console.log(twoSum(4)); // = sum(2, 4) = 6
console.log(twoSum(5)); // = sum(2, 5) = 7
The sum.bind(null, 2) call initiates a new function twoSum, which passes calls to sum and fixes null as the context and 2 as the first argument. Upcoming arguments are passed “as is.” (Sum.bind (null, 2)调用启动一个新函数twoSum ,它将调用传递给sum ,并将null作为上下文,将2作为第一个参数。即将到来的参数将“按原样”传递。)
In the example below, the value is tripled by the threeSum function:
function sum(a, b) {
return a + b;
}
let threeSum = sum.bind(null, 3);
console.log(threeSum(3)); // = sum(3, 3) = 6
console.log(threeSum(4)); // = sum(3, 4) = 7
console.log(threeSum(5)); // = sum(3, 5) = 8
The benefit of making a partial function is that you can create an independent function using a readable name. You can use it not providing the first argument anytime it’s fixed with bind. (创建部分函数的好处是可以使用可读的名称创建独立函数。您可以在使用bind修复时使用它,而不提供第一个参数。)