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();
5
  • Cannot reproduce your error (chrome, node, FF) Commented Nov 20, 2019 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. Commented Nov 20, 2019 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. Commented Nov 20, 2019 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. Commented Nov 20, 2019 at 22:42
  • 1
    Does this answer your question? Cannot read property 'forEach' of undefined Commented Nov 20, 2019 at 22:43

2 Answers 2

3

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.

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

2 Comments

Thanks, it worked after adding the semi-colon. This is weird!
@Umurİnan - I added a bunch more explanation so you can see why this is happening.
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();

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.