This is the 8th day of my participation in the August Text Challenge.More challenges in August
Write a call | apply | bind
01. call
-
call(thisArg, arg1, arg2)
- The first parameter is the one you want to change
this
Point to the - The second argument takes a list of arguments
let myCall = function(context){ // 1. Get the this pointer that needs to be changed // If so, the specified object; if not, window // (Of course, this is not exact) context = context || window // 2. Get target function // The target function here is this // Because myCall is called as a method, for example [].slice.mycall () // Therefore, inside the function this of course refers to the calling object, which is the target function f slice(). // So we make the target function a member method of the context object // context.fn = this // 3. Obtain parameters let args = [...arguments].slice(1) // 4. Call this function to change the context to which this refers letresult = context.fn(... args)// delete this function delete context.fn // 6. Return the result return result } Copy the code
For the context = context | | window
-
This with null and undefined will automatically point to the global object (window in browsers)
-
This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
-
That is, the Object (context)
if (context === null || context === undefined) { context = window } else { context = Object(context) } Copy the code
For context.fn = this
fn
May conflict with the original property of the following object- You can use
Symbol
To avoid this conflict
const fn = Symbol('myCall') context[fn] = this; Copy the code
- The first parameter is the one you want to change
02. apply
-
apply(thisArg, [arg1,arg2])
-
The first parameter is the this pointer that needs to be changed
-
The second argument takes an Array of arguments (apply takes an Array Array, both beginning with A)
This is implemented in the same way as call, but with different arguments
let myApply = function(context){ let context = context || window context.fn = this let result // Check if the second argument is an array if(arguments[1]){ result = context.fn(... arguments[1])}else { result = context.fn() } delete context.fn return result } Copy the code
Note: There are a lot of details that are not addressed, so here is a simplified version of apply, as above
-
03. bind
-
bind(thisArg)
- The first parameter is the one you want to change
this
- This method returns a function directly
- The first parameter is the one you want to change
-
Simple version of the
Function.prototype.myBind = function (context) { // Check if this is a function if (typeof this! ='function') { throw new TypeError('Error')}// Save this let that = this // Save the parameters let args = Array.prototype.slice.call([...arguments]) // Define a function to return later let returnFunction = function () { // Return the function that changed this // The context above is the function called after changing this // Also pass in all the parameters return that.apply(context, [...args, ...arguments]) } // Return this function return returnFunction } Copy the code
-
specifically
The function returned by bind, which can also be a constructor
So there’s a judgment to be made here, because the constructor’s this is not modified in any way, it always points to the instance object it creates
Function.prototype.myBind = function (context) { // Check if this is a function if (typeof this! ='function') { throw new TypeError('Error')}// Save this let that = this // Save the parameters let args = Array.prototype.slice.call([...arguments]) // Define a function to return later let returnFunction = function () { // When used as a constructor, this refers to the instance, i.e. there is no need to change the this reference // When used as a normal function, this refers to context // This is the this that calls it, not the this of the outer myBind if(this instanceof returnFunction){ // This is an instance of returnFunction return that.apply(this, [...args, ...arguments]) }else { return that.apply(context, [...args, ...arguments]) } } // Then change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype returnFunction.prototype = this.prototype // Return this function return returnFunction } Copy the code
-
After the optimization:
Function.prototype.myBind = function (context) { // Check if this is a function if (typeof this! ='function') { throw new Error("It's not a function that's bound.")}// Save this let that = this // Save the parameters let args = Array.prototype.slice.call([...arguments]) // Define a function to return later let returnFunction = function () { // When used as a constructor, this refers to the instance, i.e. there is no need to change the this reference // When used as a normal function, this refers to context // This is the this that calls it, not the this of the outer myBind return that.apply( this instanceof returnFunction ? this : context, [...args, ...arguments] ) } // Then change the prototype of the return function to the prototype of the instance of the binding function returnFunction.prototype = Object.create(this.prototype) // Return this function return returnFunction } Copy the code
Of course, there may still be some details that haven’t been considered… , hope understanding
I front-end side dish chicken, if there is wrong, please understand +