Call simulation

Posted Jun 15, 20203 min read

call:

The call() method calls a function or method under the premise of using a specified this value and several specified parameter values

Examples:

var foo = {
    value:1
};

function bar() {
    console.log(this.value);
}
bar.call(foo); //1

According to the above case, we can notice two points

  • call changed this to point to foo
  • bar function is executed

Simulation first step

Seeing the above effect, how can we achieve it?
First, we can rewrite the code into the following form

var foo = {
    value:1,
    bar:function(){
        console.log(this.value)
    }
}
foo.bar()

After the transformation, we can see that this at this time also points to foo, but this is a function that adds a property bar to the foo object, which is not suitable, but we can use the delete method Delete it, so the steps can be divided into the following three steps:

  • Set the function as the property of the object
  • Execute the function
  • Delete the function

The above three steps can be written:

Note:Because the attribute is added to the object, the fn attribute name can be written casually, and it must be deleted anyway.

  foo.fn = bar
  foo.fn()
  delete foo.fn

Implement the first version according to the above ideas:

Function.prototype.call2 = function(context) {
    //Who calls this to whom, this time the function bar calls call2, because this points to bar
    context.fn = this
    context.fn()
    delete context.fn
}
var foo = {
    value:1
}
function bar() {
    console.log(this.value) //1
}
bar.call2(foo)

Simulation to achieve the second step

We know that the call method can also give parameters, you can see the following case:

var foo = {
    value:1
}
function bar(name, age) {
    console.log(name) //Zhang San
    console.log(age) //18
    console.log(this.value) //1
}
bar.call(foo,'Zhang San', 18)

But we must know that the parameter passing is not fixed, so we can use the value in argument, take the second parameter we pass to the last parameter, add any to an array, the implementation code is as follows

var arg = []
for(var i = 1; i <argument.length; i++){
    args.push('arguments[' + i +']');
}
Note:arguments is a pseudo array, so use a for loop

Solved the problem of indefinite length of passed parameters, so how should the parameters be implemented in the function parameters that need to be executed?

context.fn(arg.join(''))
This method will definitely not work

We can achieve it through eval in ES3, so that we can achieve function parameter passing and calling

eval('context.fn(' + args +')')

So the second version is implemented as follows

Function.prototype.call2 = function(context) {
    context.fn = this
    var args = []
    for(var i = 1; i <arguments.length; i++) {
        args.push('arguments[' + i +']')
    }
    eval('context.fn(' + args +')')
    delete context.fn
}

Simulate the third step

  • This parameter can pass null, when it is null, it is regarded as pointing to the window

    var value = 1
    function bar() {

      console.log(this.value)

    }

  • The function has a return value

    var obj = {

      value:1

    }
    function bar(name, age) {

      return {
        value:this.value,
        name,
        age
      }

    }
    console.log(bar.call(obj,'Zhang San', 18))

But this is very easy to achieve, directly on the final version

Function.prototype.call2 = function(context) {
    var context = context || window
    context.fn = this
    var args = []
    for(var i = 1; i <arguments.length; i++) {
        args.push('arguments[' + i +']')
    }
    var result = eval('context.fn(' + args +')')
    delete fn
    return result
}
var obj = {
    value:1
}
function bar(name, age) {
    return {
      value:this.value,
      name,
      age
    }
}
console.log(bar.call2(obj,'Zhang San', 18))

You can also use ES6 syntax

Function.prototype.call2 = function(context, ...args) {
    context = context || window
    //Symbol in order to prevent the same function name
    let fn = Symbol()
    context.fn = this
    const result = context.fn(...args)
    delete context.fn
    return result
}
var foo = {
    value:1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}

Main reference: https://github.com/mqyqingfen...