3

I have come across an interesting behavior when playing around and trying to override Function prototype.

Let's assume we have overriden toString() like this:

const funcToString = Function.prototype.toString;
Function.prototype.toString = function() { 
    console.log("lol"); 
    return funcToString.call(this); 
}

Now, lets take this into action and see what happens:

(function foo(){}).toString(); // TypeError

TypeError: Function.prototype.toString requires that 'this' be a Function

By doing some reading, I've learned that this has something to do with how internally Function wraps it in a Proxy - and it being indistinguishable from it's target, results in a TypeError.

But now, we can try doing this:

function boo(){};
boo.toString(); // prints "lol", as we wanted

To add to all this, I've only observed this behavior in browser runtimes. In Node.js, everything goes well in both scenarios.

EDIT: Confirmed to work in REPL without any errors whatsoever.

Personally I fail to understand what the difference is / what exactly happens. Would be grateful if someone could shine some light on this.

Kamil Solecki
  • 1,009
  • 21
  • 31
  • In my understanding, `()` creates an expression scope. So if you do `(function foo(){}); foo();` it will throw error as `foo` is not defined in this scope. – Rajesh Feb 07 '18 at 08:53
  • To my surprise, `(function foo(){}).toString()` throws error but `console.log((function foo(){}).toString())` doesn't. It actually prints function – Rajesh Feb 07 '18 at 08:59
  • 1
    @TheReason No. If `(function foo(){})` returns `undefined` how does IIFE works? It returns last value separated by comma. So in this case, it will return the function. – Rajesh Feb 07 '18 at 09:10
  • I had some people run this too, and it seems to vary from runtime to runtime - not only browser vs node, but between browsers too. Although it might be that some simply don't throw /handle the error how it's supposed to be? – Kamil Solecki Feb 07 '18 at 09:15
  • This doesn't have anything to do with overriding the prototype: you can do this with `Function.prototype.kek` - also works fine in Firefox – Benjamin Gruenbaum Feb 07 '18 at 09:50
  • I cannot reproduce. How exactly are you running this code, in which browser? – Bergi Feb 07 '18 at 10:07
  • @Bergi I reproduced it on Chrome 63 on MacOS. – Serge K. Feb 07 '18 at 10:10
  • @Bergi the behavior seems to differ between runtimes. I tested on Chrome (will put version as soon as I get back to my pc), node And REPL. Here is a fiddle as well: https://jsfiddle.net/kg2odL13/ – Kamil Solecki Feb 07 '18 at 10:10
  • duplicate of https://stackoverflow.com/q/10116122/1048572? – Bergi Feb 07 '18 at 10:20

1 Answers1

9

This is a problem of a missing semicolon:

Function.prototype.toString = function() { 
    …
}; /*
 ^ */
(function foo(){}).toString();

Otherwise it is interpreted as

Function.prototype.toString = function(){…}(function foo(){}).toString();

which calls the function expression that is supposed to override toString like an IIFE

… (function(){…}(function foo(){})) …

…in the global context, not on a function.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • 3
    I did not see this coming. – Rajesh Feb 07 '18 at 10:19
  • I disagree. Try and run `function(){}(function foo(){}).toString();` there is a syntax error. – Serge K. Feb 07 '18 at 10:19
  • @SergeK. Well the whole statement is not a syntax error? – Bergi Feb 07 '18 at 10:21
  • 1
    @SergeK. No, it's not strange. It's exactly [why we have to wrap IIFEs in parenthesis](https://stackoverflow.com/questions/1634268/explain-javascripts-encapsulated-anonymous-function-syntax). – Bergi Feb 07 '18 at 10:26
  • @Bergi Thanks ! – Serge K. Feb 07 '18 at 10:30
  • 3
    @Bergi if only I had set this up in my environment with a linter. But hey, it's always a good thing to tell to people who are against semicolons! – Kamil Solecki Feb 07 '18 at 10:34
  • 2
    @KamilSolecki People who are against semicolons know to put one at the begin of a line that starts with `(`, i.e. `;(function foo(){}).toString()` :-) – Bergi Feb 07 '18 at 16:45