This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.
preface
Another year of crazy handwriting, let’s implement the bind function in JavaScript.
I. Sorting out ideas
Before implementing handwritten BIND, let’s review the scenarios in which bind is used. Normally when we use bind, we need two objects (everything is an object 🙈) :
- The function you want to implement (let’s say foo).
- We want to be able to call foo’s object (let’s say obj).
So our main breakthrough when we implement bind is to be able to get foo and obj.
There is one other difference between bind and apply or call. Bind returns a function, and executing it again is the true result of our target function. So there are three ways to pass the parameters that the target function foo would have needed:
- Passed when the bind function is called
- Passed when the function returned by bind is called
- Some is passed when you call bind and some when you call the function that bind returns.
This is another area of concern when implementing the bind function.
So let’s implement bind with that in mind.
2. Implementation of bind method
function jBind(thisArg,... args) {
return (. argus) = > {
// This points to window when null and undefined are passed in
thisArg = thisArg ?? window
// Make sure that Object is passed in
thisArg = Object(thisArg)
// Generate a unique key to avoid duplicating the key of the passed object
const key = Symbol()
thisArg[key] = this
// Get the execution result of the function
constresult = thisArg[key](... [...args, ...argus])// Remove the manually added attributes to make thisArg back to its original appearance
delete obj[key]
// Returns the result of the function's execution
return result
}
}
Function.prototype.jBind = jBind
Copy the code
3. Display of test results
function foo(x, y, z) {
console.log(x + y + z, this);
}
const obj = {
name: 'ZJoker',
}
foo.jBind()()
foo.jBind(null.1.2) (3)
foo.jBind(undefined.1.2.3)
foo.jBind(123.1.2.3)()
foo.jBind('123'.1.2.3)()
foo.jBind(false.1.2.3)()
foo.jBind([],1.2.3)()
foo.jBind({},1.2.3) ()console.log('--------- I'm divider ----------');
foo.bind()()
foo.bind(null.1.2) (3)
foo.bind(undefined.1.2.3)()
foo.bind(123.1.2.3)()
foo.bind('123'.1.2.3)()
foo.bind(false.1.2.3)()
foo.bind([],1.2.3)()
foo.bind({},1.2.3) ()Copy the code
Four, the implementation of detailed explanation
4.1 Get the object to call the target function
// This points to window when null and undefined are passed in
thisArg = thisArg ?? window
// Make sure that Object is passed in
thisArg = Object(thisArg)
Copy the code
This is the first argument to bind, so we’ll use thisArg to get obj.
However, we have no control over whether or not a parameter is passed when using bind or what type the first parameter is passed, so we need to manually process the parameters passed down.
4.2 Getting the target function to be called
// Generate a unique key to avoid duplicating the key of the passed object
const key = Symbol()
thisArg[key] = this
Copy the code
Bind ()(), so the bind method is invoked by foo. That is, this in bind refers to function foo, our target.
At this point we have fully retrieved the target function foo and the target object obj. But there’s a boundary case that we have to deal with.
To be able to make obj (parameter thisArg) call our foo, that is, bind foo’s this to obj (what the bind method actually means, change the function’s this to point to). We need to execute the target function as obj.foo(). So let’s add a new method to obj which is our foo. However, how to name key is a problem worth considering. We want to avoid the same name as obJ’s original method, so Symbol is used here to ensure the uniqueness of key value.
4.3 Obtaining the target function parameter
return (. argus) = >{... .// Get the execution result of the function
constresult = thisArg[key](... [...args, ...argus])// Remove the manually added attributes to make thisArg back to its original appearance
delete obj[key]
// Returns the result of the function's execution
return result
}
Copy the code
We mentioned that Foo may have its own parameters and can be passed in three ways. So in the bind method, there may or may not be other arguments to Foo besides the first argument to the target object.
Here we use the Rest Parameters concept in ES6 to accept Parameters other than the first parameter in the bind method, namely the Parameters in the jBind function… The args. In the following line of code… args
function jBind(thisArg,... args)
Copy the code
And in order to be able to pass foo’s arguments when you execute the function returned by bind, we also take arguments in the returned function.
Here we also use the concept of Rest Parameters in ES6 to get another part of foo’s Parameters (… Argus) in the following line of code… argus
return (. argus) = > {
Copy the code
Finally, we use the concept of Spread syntax in ES6 to pass all the received arguments to Foo and execute them, as in the following line of code… […args, …argus]
thisArg[key](... [...args, ...argus])Copy the code
Finally we remove the method foo that was manually added to obj, make thisArg back to its original appearance, and return the execution result of obj.foo().
conclusion
As you can see, closures are a major part of bind’s implementation
The above is the implementation process of handwritten bind, a learning record from JS small white, welcome to correct the wrong place.
————— I’m the splitter —————
Concise code, detailed explanation, each front-end ER will be able to handwritten bind method.
Concise code, detailed explanation, each front-end ER will be able to handwritten apply method.
Concise code, detailed explanation, each front-end ER should be able to handwritten call method