This is a simple love song ~~~

Jade case · Yuan Xi

East wind night flowers thousands of trees. More blown, stars like rain BMW carved car incense full road. Flute sound, jade pot light turn, night fish dragon dance moth son snow willow gold thread. Laughter yingying fragrance to find him thousands of baidu. Suddenly look back, that person is in, lights dimCopy the code

Native method

Small eggs

With this, you can place the address of the demo sample directly in the blog, and click on it to see the effect.

Call and apply

Native Call and Apply do basically the same thing, except that when used, their parameters are different

// apply fun.apply(thisArg, [argsArray]); // call fun.call(thisArg, arg1, arg2, ...) // The second argument to apply is an array or array-like object, which is called by fun. The list of parametersCopy the code

Call and apply both have thisArg parameters, refer to the MDN definition

The this value specified when fun is run. Note that the specified this value is not necessarily the actual this value when the function is executed. If the function is in non-strict mode, this values specified as null and undefined will automatically point to global objects (window objects in browsers) and original values (numbers, strings, This points to the auto-wrapped object of the original value.

Summarized below

  • callapplyCan specify when a function is executedthisValue specified by the first argument
  • Non-strict mode, the first parameter isnullundefinedthisPoint to a global object;
  • The first argument is the raw value (number, string, Boolean),thisAn auto-wrapped object that points to the original value
  • applyPass the parameters in an array,callPass parameters by enumeration

In the JavaScript- this binding, you can use apply to bind, so all you need to do is simulate call and apply

In view of the similar function of Call and Apply, this paper only does the simulation of Apply. Interested students can implement the simulation of Call by themselves

starts

Let’s start with an easy one

//var name = 'global name';
function foo(){
    console.log(this.name);
}

var obj1 = {
    name: 'xiaodaidai'
};

var obj2 = {
    name: 'xiaopangzi'
};

Function.prototype.applyNew = function(thisArg){
    thisArg.fn = this;
    thisArg.fn();
    delete this.fn;
}

foo.applyNew(obj1); // xiaodaidai
foo.applyNew(obj2); // xiaopangzi
Copy the code

This edition is the simplest, we can find it has many shortcomings, such as

  • ThisArg is null, program error, should default to global object
  • When thisArg is primitive, this does not point to the auto-wrapped object of the original value
  • Unable to pass parameters to a function
  • The function has no return value

With the following code, take a look at the output of these cases with native Apply

function foo(name){ console.log(name); console.log(this); console.log(this.toString()); return name; } // thisArg is null var name1 = foo.apply(); // name1 = undefined /* console output undefined Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ,... } [object Window] */ // thisArg is the basic type var name2 = foo.apply(123); // name2 = undefined /* Console output undefined Number {[[PrimitiveValue]]: Var name3 = foo.apply(123,['xiaodaidai']); // name3 = xiaodaidai /* Console Output Xiaodaidai Number {[[PrimitiveValue]]: 123} 123 */Copy the code
ThisArg is null or undefined

This is a little bit easier, as long as we add a judgment, if it’s null or undefined, we just define a function directly on the global object and call it

(function(global){
	Function.prototype.applynew = function(thisArg){  
	
		thisArg = thisArg || global; 

		thisArg.fn = this;

		thisArg.fn();

		delete thisArg[fn]; 
	}; 
})(window);

Copy the code
ThisArg is the base type

For the primitive type, we can call the wrapper function for the primitive type to box it (I used the eval function)

(function (global) {function. The prototype. Applynew = function (thisArg) {/ / string capitalize the first letter const capotalize = ([rest] first,...) =>{ return first.toUpperCase() + rest.join(''); }; // Get thisArg type and capitalize the first letter; If 'number' -> 'number' let objtype = capotalize(typeof thisArg); // If it is a basic type (excluding null and undefined), box if(objType! == 'Object' && objtype ! =='Undefined' ){ thisArg = eval('new ' + objtype + '('+ thisArg +')'); }else{ thisArg = thisArg || global; } thisArg.fn = this; thisArg.fn(); delete thisArg[fn]; }; })(window); // Pass in the global object by executing the function immediately, in this case globalCopy the code

Pass parameters and return values

(function(global){ Function.prototype.applynew = function(thisArg,args){ args = args || []; const capotalize = ([first,...rest]) =>{ return first.toUpperCase() + rest.join(''); }; let objtype = capotalize(typeof thisArg); if(objtype ! == 'Object' && objtype ! =='Undefined' ){ thisArg = eval('new ' + objtype + '('+ thisArg +')'); }else{ thisArg = thisArg || global; } var fn = Symbol('callback'); thisArg[fn] = this; var returnValue = thisArg[fn](... args); delete thisArg[fn]; return returnValue; }; })(window);Copy the code

At this point, apply’s simulation basically ends.


Use applyNew to simulate BIND

Here’s a direct reference to JavaScript- for the code in this binding, just replace apply -> applyNew

(function(global){ Function.prototype.applynew = function(thisArg,args){ args = args || []; const capotalize = ([first,...rest]) =>{ return first.toUpperCase() + rest.join(''); }; let objtype = capotalize(typeof thisArg); if(objtype ! == 'Object' && objtype ! =='Undefined' ){ thisArg = eval('new ' + objtype + '('+ thisArg +')'); }else{ thisArg = thisArg || global; } var fn = Symbol('callback'); thisArg[fn] = this; var returnValue = thisArg[fn](... args); delete thisArg[fn]; return returnValue; }; Function.prototype.bindnew = function(obj){ var fn = this; return function(){ return fn.applynew(obj,arguments); }}; })(window);Copy the code

At the end

There are many problems with the above implementation

  • ES6 syntax is used
  • Base type asthisArgThe outputthisProperty, you’ll see the function
  • BindNew should also have a lot of loose ends

Limited capacity and time, we’ll deal with it for now.

perfect

This is a complete version of callNew and Apply

function capotalize(str){
    	var classArry = {};
    	"Boolean Number String".split(" ").forEach(function(item){
			classArry[item.toLowerCase()] = item;
    	}); 
    	return classArry[str];
    }


    Function.prototype.callNew = Function.prototype.callNew ||  function(context) {
    	var contextClass = capotalize(typeof context);
    	if(contextClass){
    		context = eval('new ' + contextClass + '(context)');
    	}else{
    		context = context || window;
    	}
        
        var args = [],
            result;
        context._fn_ = context._fn_ || this;
        if (arguments.length > 1) {
        	for(var i = 1; i < arguments.length; i++){
        		 args.push('arguments[' + i + ']');
        	} 
            result = eval('context._fn_(' + args + ')');
        }else{
        	 result = context._fn_();
        }  
        delete context._fn_;
        return result;
    }
    var obj = {
        val: 'obj1'
    };

    var person = {
        name: 'xiaodaidai',
        age: 19
    }

    function test(person) {
    	if(person){
    		console.log(person.name);
        	console.log(person.age);
    	} 
    	console.log(this);
        return this.val;
    }
    var value = test.callNew(obj, person);
    console.log(value);
 	console.log(test.callNew(obj));


    Function.prototype.applyNew = Function.prototype.applyNew ||  function(context, arry) {
        var contextClass = capotalize(typeof context);
    	if(contextClass){
    		context = eval('new ' + contextClass + '(context)');
    	}else{
    		context = context || window;
    	}
        var result, args = [];
        context._fn_ = context._fn_ || this;
        if (arry) {
        	for(var i = 0; i < arry.length; i++){
        		args.push('arry[' + i + ']');
        	}  
            result = eval('context._fn_(' + args + ')');

        } else {
            result = context._fn_();
        }
        delete context._fn_;
        return result;
    }
    var param = [];
    param.push(person);
    value = test.applyNew(obj, param);
    console.log(value);
    console.log(test.applyNew(obj));
Copy the code

The demo show

The source code

Reference documentation

es5-apply

Small tight ring at the front end