let counter = makeCounter(); let counter2 = makeCounter();
alert( counter() ); // 0 alert( counter() ); // 1
/** `counter` and `counter2` are created by different invocations of `makeCounter`. (not the same with Object, which based on the reference) So they have independent outer Lexical Environments, each one has its own count. */
// user becomes `this`, and "Hello" becomes the first argument say.call( user, "Hello" ); // John: Hello
for object, we can modify the above example by changing to let result = func.call(this, x); // "this" is passed correctly
call forwarding
syntax func.apply(this, args)
args: array-like object.
For objects that are both iterable and array-like, like a real array, we can use any of them, but apply will probably be faster, because most JavaScript engines internally optimize it better.
1 2 3
let wrapper = function() { return original.apply(this, arguments); };
method borrowing
In this example. we borrow a join method from a regular array [].join and use this func to func.call the context of arguments
Usually used in taking array methods and apply them to arguments
Example
Debounce decorator: runs the function once after the “cooldown” period. Good for processing the final result. e.g. show the result of typing.
1 2 3 4 5 6 7
functiondebounce(func, ms) { let timeout; returnfunction() { clearTimeout(timeout); // cancel previous such timeout. timeout = setTimeout(() => func.apply(this, arguments), ms); // After the last call, then runs its function }; }
Throttle decorator: runs it not more often than given time. Good for regular updates that shouldn’t be very often. e.g. trace mouse movement. ??
Function binding
Solution 1: a wrapper
1 2 3 4 5 6 7 8 9 10
let user = { firstName: "John", sayHi() { alert(`Hello, ${this.firstName}!`); } };
but if the value changes within the timeout, it’ll call the wring object.
solution 2: bind
Syntax let boundFunc = func.bind(context);
boundFunc: return a function-like “exotic object”, that is callable as function but doesn’t have other properties.
context: set this=context.
A function cannot be re-bound.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
let user = { firstName: "John", sayHi() { alert(`Hello, ${this.firstName}!`); } };
let sayHi = user.sayHi.bind(user); // (*)
// can run it without an object sayHi(); // Hello, John!
setTimeout(sayHi, 1000); // Hello, John!
// even if the value of user changes within 1 second // sayHi uses the pre-bound value which is reference to the old user object user = { sayHi() { alert("Another user in setTimeout!"); } }; // not affected by this statement
Partial functions func.bind(context, ...args)
When we have a very generic function and want a less universal variant of it for convenience.
e.g. send(from, to, text) => sendTo(to, text) using partial function.