call
The call() method calls a function with a specified this value and one or more arguments given separately.
grammar
function.call(thisArg, arg1, arg2, …)
parameter | instructions |
---|---|
thisArg | Optional. The this value used when function is run. Note that this may not be the actual value that the method sees: if the function is inNonstrict modeWhen specified as null or undefined, it is automatically replaced with a reference to the global object, and the original value is wrapped. |
arg1, arg2, … | Specifies the argument list. |
thinking
var people={
sex:'man'.age:27
}
function sayPeople(a,b){
console.log(this.sex,this.age,a,b)
return false
}
sayPeople.call(people,2.3) //man 27 2 3
Copy the code
The effect of the call implementation here is that external functions can call internal properties of the object (this refers to changes). It’s easy to understand: you can call an object’s internal properties by setting an external function as an object’s internal method.
var people={
sex:'man'.age:27
}
function sayPeople(a,b){
console.log(this.sex,this.age,a,b)
}
sayPeople.call(people,2.3)//man 27 2 3
// If you want to rewrite the code, you can find that the output is consistent
var people2={
sex:'man'.age:27.sayPeople2:sayPeople2
}
function sayPeople2(a,b){
console.log(this.sex,this.age,a,b)
}
people2.sayPeople2(4.5)//man 27 4 5
Copy the code
Analog implementation
The first step is to implement the _function_. Call (thisArg) form,
var people={
sex:'man'.age:27
}
function sayPeople(){
console.log(this.sex,this.age)
}
Function.prototype.newCall=function(obj) {
// Set the calling function to the built-in function of obj
obj.fn=this
obj.fn()
// Delete fn to avoid additional fn methods on the object
delete obj.fn
}
sayPeople.call(people) //man 27
sayPeople.newCall(people) //man 27
Copy the code
In addition to the _function_. Call (thisArg) form, we also need to consider _function_. Call (thisArg, arg1, arg2…). Key points:
- Gets all parameters except the first one
- How do I pass the obtained parameters into the obj.fn function
The first one is easy to solve by iterating over arguments. The second one is that we can’t pass the array of arguments directly to the fn function, so we can use eval(). The specific code is as follows:
The **eval() ** function executes the string passed as JavaScript code
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
}
Function.prototype.newCall = function (obj) {
// Set the calling function to the built-in function of obj
obj.fn = this
// Get all parameters except the first one
var arr = []
for (var i = 1; i < arguments.length; i++) {
// Note that we are getting strings, not concrete values, because eval() is executing strings as code
arr[i - 1] = 'arguments[' + i + '] '
}
// Use eval() to call fn methods with multiple arguments
eval('obj.fn(' + arr + ') ')
// Delete fn to avoid additional fn methods on the object
delete obj.fn
}
sayPeople.call(people,1.2) //man 27 1 2
sayPeople.newCall(people,1.2)//man 27 1 2
Copy the code
Step 3 In addition, we need to consider two issues. The first is if the sayPeople function returns a return; The second is what happens if _thisArg_ is passed null or undefined. The first one is easy to handle as long as you return the result of calling the function. If null or undefined is passed in, point to window. The final result
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
return false
}
Function.prototype.newCall=function(obj) {
// Check whether the object passed in is null or undefined, or if it points to window
obj=obj || window
// Set the calling function to the built-in function of obj
obj.fn=this
// Get all parameters except the first one
var arr=[]
for(var i=1; i<arguments.length; i++) { arr[i-1] ='arguments['+i+'] '
}
// Use eval() to call fn methods with multiple arguments
var result=eval('obj.fn('+arr+') ')
// Delete fn to avoid additional fn methods on the object
delete obj.fn
// Consider calling the function sayPeople with a return
return result
}
console.log(sayPeople.call(people,1.2))
//man 27 1 2
//false
console.log(sayPeople.newCall(people,1.2))
//man 27 1 2
//false
Copy the code
apply
The apply() method calls a function with a given this value and arguments in the form of an array (or array-like object). Note: Call () is similar to apply() except that call() accepts a list of parameters, while apply() accepts an array of parameters.
grammar
func.apply(thisArg, [argsArray])
parameter | instructions |
---|---|
thisArg | Will be selected. The this value used when the func function is run. Note that this may not be the actual value that the method sees: if the function is inNonstrict modeWhen specified as null or undefined, it is automatically replaced with a reference to the global object, and the original value is wrapped. |
argsArray | Optional. An array or array-like object whose array elements are passed as individual arguments to the func function. If the value of this parameter isnull 或 undefined”, no parameters need to be passed. Array-like objects can be used starting with ECMAScript 5.Browser compatibilitySee the bottom of this article. |
thinking
The difference between Apply and Call is that the second parameter is implemented differently
var people = {
sex: 'man'.age: 27
}
function sayPeople(. args) {
console.log(this.sex, this.age,... args);return false
}
Function.prototype.newApply = function (obj, arr) {
var result,args=[];
// Check whether the object passed in is null or undefined, or if it points to window
obj = obj || window
// Set the calling function to the built-in function of obj
obj.fn = this
// Apply has only two parameters. If arr is null, execute fn
if(! arr) { result = obj.fn() }else {
for (var i = 0; i < arr.length; i++) {
args[i] = 'arr[' + i + '] '
}
// Use eval() to call fn methods with multiple arguments
result = eval('obj.fn(' + args + ') ')}// Delete fn to avoid additional fn methods on the object
delete obj.fn
// Consider the case where the function return is called
return result
}
console.log(sayPeople.apply(people, [1.2.8.9.5]))
//man 27 1 2 8 9 5
//false
console.log(sayPeople.newApply(people, [1.2.8.9.5]))
//man 27 1 2 8 9 5
//false
Copy the code
bind
The bind() method creates a new function, and when bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function.
grammar
function_.bind(thisArg[, arg1[, arg2[, …]]])_
parameter | instructions |
---|---|
thisArg | The value passed to the target function as the this parameter when the binding function is called. If you are usingnewOperator to construct a binding function, the 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. |
arg1, arg2, | Arguments that are preset into the argument list of the binding function when the target function is called. |
The return value | Returns a copy of the original function with the specified this value and initial arguments. |
---|
Analog implementation
Bind will return a copy of the original function :this refers to the same change principle, so we can get the following code from the above thinking
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
return false
}
Function.prototype.newBind=function(obj){
// Save this in case this is lost
var that = this
var args=Array.prototype.slice.call(arguments.1)
return function () {
return that.apply(obj, args)
}
}
sayPeople.bind(people,2.2) ()//man 27 2 2
sayPeople.newBind(people,2.2) ()//man 27 2 2
Copy the code
We found that Bind also accepts arguments that return functions, so we need to merge the arguments
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
return false
}
Function.prototype.newBind=function(obj){
// Prevent this from being lost
var that = this
var args=Array.prototype.slice.call(arguments.1)
return function () {
// Arguments refer to the arguments passed in by the function returned by bind
// Note that arguments don't need to be split at this point, just convert the class array to an array
return that.apply(obj, args.concat(Array.prototype.slice.call(arguments)))
}
}
sayPeople.bind(people,2) (2)//man 27 2 2
sayPeople.newBind(people,2) (2)//man 27 2 2
Copy the code
The second step is to create an instance of the bind return function using new.
var sex= 'woman';
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
return false
}
var sayPeople2 = sayPeople.bind(people,2)
var people2=new sayPeople2(2)
console.log(people2)
//undefined undefined 2 2
//sayPeople {}
Copy the code
A binding function can also use the new operator to create objects: this behavior is like treating the original function as a constructor. The supplied this value is ignored, and the arguments to the call are supplied to the mock function.
We can see that the “this” pointer inside sayPeople is missing. It does not point to people or window. But the parameters passed in are not affected. The reason for this is the use of new. So the solution is to modify the prototype and the first parameter passed into apply.
The key point
- When did you use new
- Change the return function’s prototype to point to sayPeople
thinking
- If new is used, the current this reference is an instance of the return function, so we can determine the use of new by whether the current this prototype is a return function
- Change the prototype by modifying the Prototype property
The specific implementation
var sex= 'woman';
var people = {
sex: 'man'.age: 27
}
function sayPeople(a,b) {
console.log(this.sex, this.age,a,b)
return false
}
Function.prototype.newBind=function(obj){
// Prevent this from being lost
var that = this
var args=Array.prototype.slice.call(arguments.1)
function newFn () {
// If this instanceof newFn is true, new is called
return that.apply(this instanceof newFn ? this:obj, args.concat(Array.prototype.slice.call(arguments)))}// Change the return function's prototype to the binding function's (in this case sayPeople)prototype
newFn.prototype=that.prototype
return newFn
}
var sayPeople2 = sayPeople.newBind(people,2)
var people2=new sayPeople2(2)
console.log(people2)
Copy the code
Code optimization
If we change prototype in newFn, it will also affect prototype in the binding function sayPeople, so we need to use an empty function to mediate.
Function.prototype.newBind=function(obj){
// If bind is not a function, an error is reported
if (typeof this! = ="function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
// Prevent this from being lost
var that = this
var emptyFn=function (){}
var args=Array.prototype.slice.call(arguments.1)
function newFn () {
// If this instanceof newFn is true, new is called
return that.apply(this instanceof newFn ? this:obj, args.concat(Array.prototype.slice.call(arguments)))}// Change the return function's prototype to the binding function's (in this case sayPeople)prototype
emptyFn.prototype=that.prototype
newFn.prototype=new emptyFn()
return newFn
}
Copy the code
summary
- The key to understanding call/apply/bind is to understand call. Call: Call enables external functions to call internal properties of an object. So how can we use internal properties of objects — internal methods; So a new method is added to the object, which is the same as the external function. The method is deleted after the call, and the result is returned. This is just a simple understanding, please correct if it is not considered.
- The key to the apply implementation is the handling of the second argument (note that the second argument can be passed as an array or an array-like object)
- The key to implementing BIND is to understand new and stereotypes
The above is my learning ideas and learning ideas, if wrong, please help correct.