# Reimplementation of call() method in JavaScript

I was try to reimplement the native call method in JavaScript. Here we need to handle the object which we pass in the call function and additional params. I handle the additional params using eval() method. Is there any more efficient way to write it?

var name = {
name: "JavaScript",
version: "6",
}

function printName(location, district){
alert(this.name + ", " + this.version + ", " + location + ", " + district);
}

Function.prototype.myCall = function(...args){
var param = args.slice(1),
paramLength = param.length,
paramString = "JSONarg.myFun(";
for(var i = 1; i <= paramLength; i++){
paramString += "args["+i+"],";
}
paramString += ")";

if(typeof this != 'function'){
throw new Error(this + " is not a Function");
}

var JSONarg =  {
...args[0],
myFun: this
}
return eval(paramString);
}

printName.myCall(name, "Chrome", "browser");

• Hey this is an interesting post! It appears that the code above shows an alert with the message undefined, undefined, Chrome, browser - is that what you expected, or was there something else you intended to have it do? – Sᴀᴍ Onᴇᴌᴀ Apr 29 '19 at 17:01

## Review

// if you add to a built in prototype use Object.defineProperty or Object.defineProperties
// using the default settings
// Function.prototype.myCall = function(...args){ // use 2 parameters
Function.prototype.myCall = function(name, ...args){

// var param = args.slice(1),  // is now second parameter

// Should be a constant
//    paramLength = param.length,
const  paramLength = args.length;

// not really a param string. Rather its a eval function
//  paramString = "JSONarg.myFun(";
var evaledFunc = "JSONarg.myFun(";

// The variable if defined in the loop should be let
// Now starts at index 0
//for(var i = 1; i <= paramLength; i++){
for (let i = 0; i < paramLength; i++) {  // add space for ( and ) {

evaledFunc += "args[" + i + "],";  // use spaces between operators
}
evaledFunc += ")";

// This function is a prototype of Function. You would have to go hard
// to have the next statement be true. Just let the native exception
// generate the error
/*if(typeof this != 'function'){
throw new Error(this + " is not a Function");
}*/

// Does not change, thus should be a const
// var JSONarg =  {
const JSONarg =  {  // would be best defined at the top of the function

// args[0] is now the param name
//  ...args[0],
...name,
myFun: this
// }  // there should be a ; here
};
return eval(evaledFunc);
}


Removing the comments and some minor mods we get...

function myCaller(name, ...args) {
const JSONarg =  {...name, myFun: this};
var func = "JSONarg.myFun(";
for (let i = 0; i < args.length; i++) {
func += "args[" + i + "],";
}
func += ")";
return eval(func);
}


Rather than eval you could also have use new Function

## Object.defineProperty

To prevent problems (myCall showing up in strange places) and as it is in a very often used native type.( All functions get the new property). enumerable = true can slow every function call just a little depending on JS engine and version.

Add to prototype safely using Object.defineProperty. Default accessor configurable, enumerable, writable are false

if (! Function.prototype.myCall) {
Object.defineProperty(Function.prototype, myCall, {value: myCaller});
}


## Function.bind

With all that said there is a much easier way to do the same. Function.bind will bind a functions with an object (sets the functions this)

function printName(location, district){
alert(this.name + ", " + this.version + ", " + location + ", " + district);
}
printName.bind({...name})( "Chrome", "browser");

// or just use original name obj

printName.bind(name)( "Chrome", "browser");


The bound function is a new reference, the original function remains unbound

const printNameBound = printName.bind(name);

printNameBound("Chrome", "browser");

printName("Chrome", "browser"); // will fail as not bound to name object


## Function.call or Function.apply

You can also use Function.call or Function.apply to do the same. In this case there is no new reference to the function created. The binding is temporary.

printName.call(name, "Chrome", "browser");

// or

printName.apply(name, ["Chrome", "browser"]); // args as array

• How can i use new Function instead of eval()? – Gmv Apr 30 '19 at 4:55
• @Kallis function myCaller(name, ...args) { return (new Function("name", "func", "args", "return ({...name, func})(...args);")(name, this, ...args);} – Blindman67 Apr 30 '19 at 13:43
• can you check this jsfiddle.net/gowtham25/m1gnLd6x/12 – Gmv Apr 30 '19 at 17:44