Quick Fix - Add Semi-colon
Add a semi-colon at the end of your function definition:
Array.prototype.demo = function(){
this.forEach(i=>console.log(i))
}; // <======
[1,2,3].demo();
And, it will work.
What's Happening?
The problem is that the [1,2,3]
is being combined with the previous function (whitespace between them collapsed). In that circumstance, the [1,2,3]
becomes just [3]
and tries to read the [3]
property from the function object. If you put the semi-colon at the end of the function definition, then that signals the end of the function definition statement and the [1,2,3]
can then be interpreted as a static array definition.
It's all about context. In some circumstances in Javascript, [x]
is a property access. In other circumstances, it's a static array definition. Without the semi-colon, it was getting interpreted as the property access instead of the array definition.
Remember that functions are objects in Javascript so they can have properties and can respond to [x]
as a property access on them.
So, without the semi-colon at the end of the function you essentially have this:
Array.prototype.demo = function() {...}[3].demo();
Because the whitespace is collapsed between the end of your function and the [1,2,3]
. That means the JS interpreter is expecting the []
to be a property name so it evaluates the statement inside the []
and in that context [1,2,3]
turns into [3]
(the 1,2,3
is evaluated which takes the value of the last comma separated statement which is 3
).
More Detailed Explanation
Think of it like this:
// defines function
let f = function() {};
// attempts to read a property from that function object
let o = f [1,2,3]; // this is the same as let o = f[3]
// tries to call `.demo()` on the value read from that property
// which was undefined so this throws
o.demo();
Functions Are Objects
As a demonstration of how functions are objects, see this example that actually works!
// defines function
let f = function() {};
f[3] = {demo: function() { console.log("demo!!!");}}
// attempts to read a property from that function object
let o = f[1,2,3]; // this is the same as let o = f[3]
// tries to call `.demo()` on the value read from that property
// which was undefined so this throws
o.demo();
Here, we actually put a property on the [3]
property of the function so when f[1,2,3]
reads that property, it actually gets an object with a .demo()
method on it so when we then call it, it all works. I'm not suggesting one would ever code this way, but I am trying to illustrate how f[1,2,3]
is just reading the [3]
property from the function object.
Good Reason Not to Leave out the Semi-colons
These odd cases are a good reason not to leave out semi-colons, even though you usually (but not always) get away with it.