call, bind, apply are all methods on the Function prototype to change the direction of this
Custom Functions
call, bind and apply in JS are implemented in c++. Here we use js code to make a pattern, not to take all the boundary conditions into account, but to make a simple implementation. The three functions have some things to pay attention to when they are used, which need to be taken into account when they are defined
- When an incoming value is a basic data type, the call, apply, and bind methods convert it to a reference data type, such as when the incoming string becomes a String type, which can be done with Object().
- When no this point is passed that needs to be changed, the this of the function points to window (in non-strict mode)
- When the passed this points to null, undefined, the function this points to window (in non-strict mode)
Implementation of call
Note when defining the call function
- The first parameter receives the changed this pointing to, starting with the second parameter, the parameters required for the function to execute
The implementation code is as follows
Function.prototype.iCall = function (thisArg, ...args) { // 1. Get the execution function var fn = this // 2. Bind the function to the passed object thisArg = thisArg || thisArg.toString() ? Object(thisArg) : window thisArg.fn = fn var result = thisArg.fn(...args) // 3. Delete the fn function of the passed object delete thisArg.fn // 4. Return results return result } function foo(...args) { console.log('Bound this For:', this) console.log('The parameters passed are:', args) } var user = { name: 'alice' } // Change the foo function this to user and pass the parameters 1, 2, 3 foo.iCall(user, 1, 2, 3)
The result of execution is
Implementation of apply
Notes for defining apply functions
- The first parameter receives the changed this pointing to, and the second parameter receives the array of parameters required for the function to execute
The implementation code is as follows
Function.prototype.iApply = function(thisArg, args){ // 1. Get the execution function var fn = this // 2. Bind the function to the passed object thisArg = thisArg || thisArg.toString() ? Object(thisArg) : window thisArg.fn = fn var result = thisArg.fn(...args) // 3. Delete the fn function of the passed object delete thisArg.fn // 4. Return results return result } function foo(...args){ console.log('Bound this For:', this) console.log('The parameters passed are:', args) } var str = "hello js" var arr = ['a', 'b', 'c'] foo.iApply(str, arr)
The results are as follows
Implementation of bind
Note when defining the bind function
- The first parameter receives the changed this pointing, and the second parameter receives the parameters needed for the function to execute
- Instead of calling the function immediately, the bind function returns a new function that can still continue to pass parameters
Function.prototype.iBind = function (thisArg, ...args) { // 1. Get the execution function var fn = this // 2. Bind the function to the passed object thisArg = thisArg || thisArg.toString() ? Object(thisArg) : window thisArg.fn = fn return function (...params) { // 3. Get the result of the function execution var result = thisArg.fn(...args, ...params) // 4. Delete the fn function of the passed object delete thisArg.fn // 5. Return results return result } } function foo(...args) { console.log('Bound this For:', this) console.log('The parameters passed are:', args) } var num = 0 var fn = foo.iBind(num, 20, 40) fn(30, 50)
The results are as follows
That's how call, bind, apply are implemented. There's not much code, but you need to understand this point. You can also see my other blog about this point.