JavaScript - Apply function call to object with another prototype

261 views Asked by At

The task (actually the problem) I'm facing is the following. I have an array (let’s call it arr). The first element of the array is a function and the rest are arguments. For example:

arr = [(a, b) => a + b, 5, 7];

So, I need to call function (a, b) => a + b with arguments 5 and 7. However, I don't want to use arr[0](...arr.slice(1)), instead I want to do it using Function.prototype.apply and Function.prototype.call (I can also use Function.prototype.bind if needed). So basically, I am allowed to access arr only once and no other variables may be used. Also, I'm not allowed to modify arr or it's properties, or to store any other data somewhere (like as property of some global object, etc). Is that even possible?

My attempt

I tried to figure it out, and this is what I came up with (it doesn't work):

Function.prototype.apply.call(this, ...arr);

However, it throws an error saying:

Uncaught TypeError: Function.prototype.apply was called on #Global, which is an object and not a function

Question

What is the correct way to do this?

2

There are 2 answers

5
guest271314 On BEST ANSWER

You can use destructuring to get the elements of arr array as variables, then call the function at index 0 with elements at indexes 1-N

let arr = [(a, b) => a + b, 5, 7];
let [fn, a, b] = arr;
console.log(fn(a, b));

For any number of elements at indexes 1-N you can use rest element

let arr = [(a, b) => a + b, 5, 7];
let [fn, ...rest] = arr;
console.log(fn.apply(null, rest));

Alternatively, using Function, template literal to convert array to string, .replace() with RegExp /\s/g with .replace() to remove space characters, .match() with RegExp /(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g to capture arrow function, negate first comma of characters that are not arrow function and arrow function captured at first group, we can reference arr once and not create additional variables save for immediately invoked arrow function parameters

let arr = [(a, b) => a + b, 5, 7];

console.log(
  (([a, b]) => new Function(`return (${a})(${b.split(/,/)})`)())
  (`${arr}`.replace(/\s/g, "").match(/(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g))
)

4
T.J. Crowder On

instead I want to do it using Function.prototype.apply and Function.prototype.call. Is that even possible?

Sure, but we have to repeat arr:

arr[0].call(...arr)

Live Example:

const arr = [(a, b) => a + b, 5, 7];
console.log(arr[0].call(...arr));

That wouldn't work if the function required a specific this, but your function doesn't, for two reasons, either of which would be sufficient: 1. It's an arrow function, so it doesn't care what this we call it with, it closes over the one where it was created; and 2. It doesn't use this anyway.

That said, probably better just to give yourself a helper function that does it instead.