How to use call, apply and bind
Function: All three functions change the direction of this
Call Usage
fn.call(thisArg, arg1, arg2, arg3 …)
function fn1() {
console.log(this)}const obj = { a: 1 }
fn1.call(obj, 1.2.3.4) // {a: 1}
Copy the code
Apply the usage of the
fn.apply(thisArg, [arg1, arg2, arg3…] )
function fn1() {
console.log(this)}const obj = { a: 1 }
fn1.apply(obj, [1.2.3.4]) // {a: 1}
Copy the code
The use of the bind
fn.bind(thisArg, arg1, arg2, arg3)
function fn1() {
console.log(this)}const obj = { a: 1 }
fn1.bind(obj, 1.2.3.4) // Can't print anything.
Copy the code
At first glance, the usage of the three is almost identical, but it is important to note that the bind function is called without executing fn1
The only difference between call and apply is that they are passed arguments one by one. Call receives an array of arguments while Apply receives an array of arguments
Next, to implement our call and apply functions, we find that we need to implement two things through usage:
- Change this to the first argument of the executing function
- Execute the original function
The second point is easy to implement. The first point is that normally, when a function is called as a method of an object, this refers to that object. You can use this feature to implement your call function
To realize the call
// All functions need to be callable, so it needs to be written on the prototype of Function
Function.prototype.myCall(context) {
// Check whether the context exists. If it does not, set it to Window
context = context ? Object(context) : window
// Process parameters
const args = [...arguments].slice(1)
// To change this to context, you need to call context
context.fn = this // This is the antifunction
constresult = context.fn(... args)// Execute the original function, since the context is called, so this refers to the context
delete context.fn
return result
}
Copy the code
To realize the apply
To implement your own Apply function, you just need to change the way you pass parameters
// All functions need to be callable, so it needs to be written on the prototype of Function
Function.prototype.myCall(context) {
// Check whether the context exists. If it does not, set it to Window
context = context ? Object(context) : window
// To change this to context, you need to call context
context.fn = this // This is the antifunction
let result
// Process parameters
if (arguments[1]) { result = context.fn(... arguments[1])}else {
result = context.fn()
}
delete context.fn
return result
}
Copy the code
To realize the bind
Bind next implements the bind function, which differs from the other two in that it creates a new function and its first argument is specified as this for the new function
- Returns a new function
- The first argument is specified as this for the new function
- The remaining arguments are treated as arguments to the new function
/ / the first edition
Function.prototype.myBind(context) {
const args = [...arguments].slice(1)
const that = this
return function() { that.apply(context, args.concat(... arguments)) } }Copy the code
The basic BIND functionality has been implemented up to this point, but according to MDN:
The value passed to the target function as the this parameter when the binding function is called. If the new operator is used to construct the binding function, this value is ignored. When using bind to create a function in setTimeout (provided as a callback), any raw values passed as thisArg are converted to Object. If bind’s argument list is empty, or thisArg is null or undefined, this executing the scope will be treated as thisArg for the new function.
When you use the new operator to operate on a function that bind generated, bind’s this will be invalidated, and this will refer to the instance that new generated. Let’s look at the new operator
The use of the new
function Animal(type) {
this.type = type
this.age = age
}
const animal1 = new Animal('the cat'.2)
const animal2 = new Animal('dog'.2)
Copy the code
When executing New Animal(), the following things are done
- Create an empty object
- Change this to point to the empty object and give the object access to properties on the constructor prototype
- The constructor is executed, where this points to the newly created object
The simulation generates the new function
function _new() {
// Create a new object
let obj = {}
// Get the constructor passed in
let constructor = [].shift.call(arguments// Give this object access to properties on the constructor prototypeobj.__proto__ = constructor.prototype/ / changethisPoint to and execute the constructorconstructor.apply(obj, aruguments) // HereargumentsAlready intercepted // Returns the created instance objectreturn obj
}
Copy the code
So let’s go back to the question above, and let’s reframe it
/ / the second edition
Function.prototype.myBind(context) {
const args = [...arguments].slice(1)
const that = this
return function resFn () {
// Check if this is an instance of the constructor, if so, the new operator is used
that.apply(this instanceof resFn ? this: context , args.concat(... arguments)) } }Copy the code
This is a new instance, but this instance has no relation to the original function, so it can’t access the properties of the original function
/ / the third edition
Function.prototype.myBind(context) {
const args = [...arguments].slice(1)
const that = this
const Fn = new Function(a)// To avoid direct modifications to the prototype, use transition functions
function resFn () {
// Check if this is an instance of the constructor, if so, the new operator is used
that.apply(this instanceof resFn ? this: context , args.concat(... arguments)) } Fn.prototype =this.prototype
resFn.prototype = new Fn()
return resFn
}
Copy the code
conclusion
- Call, apply, and bind are all used to change the direction of this
- The difference between Call and apply is that each parameter of call is passed in sequence, while apply is passed in as an array
- Calling call and apply executes the function, and bind returns a new function
- So the idea is “Call a function from an object, and this refers to that object.”