0

For learning purpose, I wrote a piece of (meaningless) code like this:

Function.prototype.invoke = function(...args) {
    return this(...args);
}
const foo = (a, b) => a + b;
console.log(foo.invoke(1, 2)); // 3, all right!

const arr = [];
arr.push.invoke(1,2); // TypeError: Cannot convert undefined or null to object
console.log(arr);

Here I define a method on Function's prototype named invoke, which is used to just invoke a function by foo.invoke(args)(instead of the conventional way foo(args)). I wonder why foo runs while arr.push can't.

As far as I know, this result from the this problem. this in my invoke method is push, but this's this(push's caller) is window, which is not a array-like object(?). But why the error is Cannot convert undefined or null to object?

What on earth is the reason for this error? Is there any way to correct it?

2
  • ""but push's this is window" - no, it's not. That's a myth that comes from sloppy-mode functions. The this argument is undefined when you don't pass a receiver. Try yourself with (function() { "use strict"; console.log("this value:", this); }).invoke() Commented Nov 12, 2019 at 9:42
  • @Bergi OK, yes, I realize I'm wrong. And window, from "has a length property" perspective, can be regarded as an array-like object, but still it can't be called using push, though it is another story. Thank you~! Commented Nov 12, 2019 at 9:54

1 Answer 1

1

arr.push is the same function as Array.prototype.push, and Array.prototype.push requires a calling context to identify which array the argument(s) are going to be pushed to. With

arr.push(1, 2);

the calling context (what comes before the . when the function is invoked) is arr. But with your invoke there is no calling context:

return this(...args);

For the same reason, you can't call push on undefined:

Array.prototype.push.call(undefined, 1, 2);

If you want to "invoke" a function which requires a calling context, maybe pass the this value as the first argument and use .call:

Function.prototype.invoke = function(thisVal, ...args) {
    return this.apply(thisVal, args);
}
const foo = (a, b) => a + b;
console.log(foo.invoke(undefined, 1, 2)); // 3, all right!

const arr = [];
arr.push.invoke(arr, 1,2);
console.log(arr);

You can also bind the arr.push to arr before calling invoke:

Function.prototype.invoke = function(...args) {
    return this(...args);
}
const foo = (a, b) => a + b;
console.log(foo.invoke(1, 2)); // 3, all right!

const arr = [];
arr.push.bind(arr).invoke(1,2);
console.log(arr);

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.