0

I'm trying to add a custom method on the prototype of the Array object:

Array.prototype.demo = function(){
    this.forEach(i=>console.log(i))
}

But, I'm receiving the error below when I'm calling the method like this:

[1,2,3].demo()

// Error: TypeError: Cannot read property 'demo' of undefined

However, it runs successfully when I change it to:

const arr = [1,2,3];
arr.demo()

// Output: 1, 2, 3

PS. This is in nodejs To reproduce the error in the browser, copy/paste the full block at once and click enter.

UPDATE: It sounds like we need to add a semicolon to make it work:

Array.prototype.demo = function(){
    this.forEach(i=>console.log(i))
};   <=== added semicolon here to work @jfriend00

[1,2,3].demo();

However, now this next code works WITHOUT semicolon!!

String.prototype.demo = function(){
    this.split('').forEach(c=>console.log(c))
}

'hello'.demo();
Umur İnan
  • 143
  • 2
  • 12
  • Cannot reproduce your error (chrome, node, FF) – ASDFGerte Nov 20 '19 at 22:39
  • The error is in nodejs, but If you copy/paste the full block of code in the browser you will see the error. – Umur İnan Nov 20 '19 at 22:41
  • The error is most definitely not in Node. If I copy-paste your code, it runs perfectly fine. The same goes for any browser. – Mike 'Pomax' Kamermans Nov 20 '19 at 22:41
  • 1
    My glass ball is telling me you are missing a semicolon, and ASI will not trigger. Therefore the "array" is seen as bracket property access. Add a semicolon at the end of your prototype assignment. – ASDFGerte Nov 20 '19 at 22:42
  • 1
    Does this answer your question? [Cannot read property 'forEach' of undefined](https://stackoverflow.com/questions/38908243/cannot-read-property-foreach-of-undefined) – Tareq Nov 20 '19 at 22:43

2 Answers2

2

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.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

The reason is that functions are objects, so if we don't add a semicolon, JavaScript will try to access a property on the function object, after it evaluates the comma operator like this:

function() { ... }[1,2,3].demo();
function() { ... }[3].demo();
undefined.demo();
Umur İnan
  • 143
  • 2
  • 12