Simulating and Implementing Bid, call and apply Functions

Keywords: Javascript Attribute ECMAScript

Recently, after reading "Deep Understanding JavaScript" and seeing about Function constructors and bind functions, I suddenly felt an urge to implement the bind function by myself, so I began to try to write the bind function.
The first version I tried to write was too cumbersome, and there were still some problems.
 
    Function.prototype.binds = function(obj){
        var This = this;
        //var args = Array.prototype.slice.call(arguments, 1);
        if(typeof obj != 'object'){
            throw new Error('The first argument must be Object!');
        }
        var args = [];
        if(arguments.length > 0){
            for(var i = 1; i < arguments.length; i++){
                args.push(arguments[i]);
            }
        }
    
        function fun(){
    
            var arr = clone(args);
    
            for(var i=0; i < arguments.length; i++){
                arr.push(arguments[i]);
            }
            
            var name = 'fun' + String(Math.random()).slice(2,-1);
    
            obj[name] = This;
    
            var params = [];
    
            for(i = 0; i < arr.length; i++){
                params.push('arr['+i+']');
            }
    
            params = params.join();
    
            var newFun = new Function('arr','obj["'+name+'"]('+ params +')');
    
            newFun(arr);
    
            delete obj[name];
        }
    
        return fun;
    
    };
    
    function clone(o){
        var obj;
        switch(Object.prototype.toString.call(o).slice(8, -1)){
            case 'Array':
                obj = [];
                for(var i = 0; i < o.length; i++){
                    obj.push(clone(o[i]));
                }
                return obj;
                break;
            case 'Object':
                obj = {};
                for(var i in o){
                    if(o.hasOwnProperty(i)){
                        obj[i] = clone(o[i]);
                    }
                }
                return obj;
                break;
            default:
                return o;
                break;
        }
    }
    
    
    function say(){
        console.log(this.name, arguments);
    }
    
    var obj = {
        name:'tom'
    };
    
    var fun = say.binds(obj,1,2,3);
    fun(4,5);
    fun(4,5,7,8);
    fun(4);

  

......
......
......
......
 
After several days, I saw the object impersonation in the implementation of ECMAScript inheritance mechanism in W3C high-level documents, which made me feel awkward. In fact, the bind function does not need to be written so complex. The principle is to create a new attribute on the object to be relied on to carry the function, and then delete the attribute after execution. Using the Function constructor, you just want to pass the members of the array into the function one by one.
Following is the second version, basically no problem, is a perfect implementation of the bind function.
 
    Function.prototype.newBind = function(){
        var name = 'random' + String(Math.random()).slice(2,-1);//Random generation of an attribute name to prevent conflicts.
        var args = [];//Use to store incoming parameters
        var This = this;//Save this pointer
        var obj = arguments[0];//Get the object to bind
        //Save parameters in args
        for(var i = 1; i < arguments.length; i++){
            args.push(arguments[i]);
        }
        //Returns a new function
        return function(){
            //Loop to get the incoming parameters and store them in args
            for(var i = 0; i < arguments.length; i++){
                args.push(arguments[i]);
            }
            //Generate a string of calls based on the number of parameters
            var params = [];
            for(var i = 0; i < args.length; i ++){
                params.push('args['+ i +']');
            }
            //Use the Function constructor to construct a function that passes in parameters individually and calls them
            var newFun = new Function('obj','name', 'This','args','obj[name] = This;obj[name]('+ params.join() +');delete obj[name]');
            newFun(obj, name, This, args);
        }
    };
     var b = {
        0:1,
        1:2,
        2:3,
        length:3
    }
    var c = Array.prototype.forEach.bind(b, function(){
        console.log(arguments)
    })
    c();

  

For example, call and apply are written only on the basis of bind.
If you call, you don't need to return a new function here, you just need to call the function returned by bind to execute it immediately.
 
    Function.prototype.newCall = function(){
        var name = 'random' + String(Math.random()).slice(2,-1);//Random generation of an attribute name to prevent conflicts.
        var args = [];//Use to store incoming parameters
        var This = this;//Save this pointer
        var obj = arguments[0];//Get the object to bind
        //Save parameters in args
        for(var i = 1; i < arguments.length; i++){
            args.push(arguments[i]);
        }
        (function(){
            //Loop to get the incoming parameters and store them in args
            for(var i = 0; i < arguments.length; i++){
                args.push(arguments[i]);
            }
            //Generate a string of calls based on the number of parameters
            var params = [];
            for(var i = 0; i < args.length; i ++){
                params.push('args['+ i +']');
            }
            //Use the Function constructor to construct a function that passes in parameters individually and calls them
            var newFun = new Function('obj','name', 'This','args','obj[name] = This;obj[name]('+ params.join() +');delete obj[name]');
            newFun(obj, name, This, args);
        })()
    };
     var b = {
        0:1,
        1:2,
        2:3,
        length:3
    }
    var c = Array.prototype.forEach.newCall(b, function(){
        console.log(arguments)
    })

  

apply modifies a little more on the basis of call, so there is no need to intercept arguments, just use arguments[1] directly.

    Function.prototype.newApply = function(){
        var name = 'random' + String(Math.random()).slice(2,-1);//Random generation of an attribute name to prevent conflicts.
        var args = arguments[1] || [];//Use to store incoming parameters
        var This = this;//Save this pointer
        var obj = arguments[0];//Get the object to bind
        //Execute the function immediately
        (function(){
            //Loop to get the incoming parameters and store them in args
            for(var i = 0; i < arguments.length; i++){
                args.push(arguments[i]);
            }
            //Generate a string of calls based on the number of parameters
            var params = [];
            for(var i = 0; i < args.length; i ++){
                params.push('args['+ i +']');
            }
            //Use the Function constructor to construct a function that passes in parameters individually and calls them
            var newFun = new Function('obj','name', 'This','args','obj[name] = This;obj[name]('+ params.join() +');delete obj[name]');
            newFun(obj, name, This, args);
        })()
    };
     var b = {
        0:1,
        1:2,
        2:3,
        length:3
    }
    var c = Array.prototype.forEach.newApply(b, [function(){
        console.log(arguments)
    }])

At this point, the implementation of bind, call and apply is over. Sure enough, with the increase of knowledge, some of the problems that were very troublesome will be solved.

Posted by parboy on Wed, 17 Jul 2019 17:12:09 -0700