0

I am running Electron 4.0.6 and react-scripts 3.0.1, which, I believe, includes babel. I wrote this code:

const data = [1, 2, 3]
for( const idx in data ) console.log(idx)  

Expected output is:

0
1
2

Actual output is:

0
1
2
peek
last

with the last two indices mapping to functions. I have a large program written using for/in loops, and I am daunted by the possibility of putting in error checking in every one of my loops to make sure I'm not accidentally consuming a function index.

My questions:

  • Why am I seeing this?
  • How do I fix it?

Edit:

Thanks for everyone's input. To be clear, the program used to work until I reinstalled it on a new computer. I used to be able to use for/in to iterate over only the non-default indices in an array. I'm not sure why I can't do that anymore, and I'm wondering what changed that made it work the way it does now. Do I need to install an older version of react-scripts? Simply rewriting the whole program to use of loops doesn't work, either, because I already do that when I can. I use for/in loops when I need the index.

Edit 2:

I appreciate everybody's help. Thanks to the accepted answer, I was able to track down that the npm package dirty-json is overriding all arrays with .peek and .last without setting them as non-enumerable. I am now able to address this issue.

Final edit: dirty-json is fixed, so this exact problem should no longer occur. If you have arrived here because of a similar issue, check your packages; it is not dirty-json as of version 0.9.0.

sg.cc
  • 1,726
  • 19
  • 41
  • 2
    https://stackoverflow.com/questions/242841/javascript-for-in-vs-for – epascarello Jun 02 '20 at 21:06
  • Does this answer your question? [What is the difference between ( for... in ) and ( for... of ) statements in JavaScript?](https://stackoverflow.com/questions/29285897/what-is-the-difference-between-for-in-and-for-of-statements-in-jav) – VLAZ Jun 02 '20 at 21:07
  • @VLAZ, @epascarello: thank you, I'm aware of this difference. My problem is that the program used to work with `for/in` loops iterating over array indices only, and not its default functions. Now I have a huge program that doesn't work because of this. I would like to know whether it's Babel or React or what that's doing this, and how to change it back so the code works again. – sg.cc Jun 02 '20 at 21:10
  • Also relevant: [Why is using “for…in” with array iteration a bad idea?](https://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea) – VLAZ Jun 02 '20 at 21:11
  • I appreciate the stylistic advice. Do you know how I can fix my problem without rewriting every piece of code that is now broken because of this? – sg.cc Jun 02 '20 at 21:12
  • 1
    @sg.cc you have some code that adds `peek` and `last` to the global array prototype. – VLAZ Jun 02 '20 at 21:12
  • That sounds about right. Any clue how I could narrow down what might be adding it? There are a lot of dependencies, and I'm not sure what could be doing it. – sg.cc Jun 02 '20 at 21:14
  • 1
    Create a minimal project that uses all dependencies. Check if those properties are enumerable. Ideally, they would be. Write a simple unit test that checks it. Start excluding dependencies and re-running the unit test. Eventually you'll find it. The alternative is monkey patching the property to be non-enumerable but 1. it's not guaranteed to work 2. it's going to fail with the next added property. I'd really suggest eventually re-writing all `for..in` loops as they are just ticking bug bombs as you've already seen. – VLAZ Jun 02 '20 at 21:18
  • @sg.cc It's not babel or react. It's javascript. Your old user agent was probably not handling for in loops correctly. Try rolling back to whatever user agent you were using before your upgrade. – Charlie Bamford Jun 02 '20 at 21:18
  • @VLAZ I kind of mentioned that in my answer. I see you saw the same issue as me, that it is not somethnig that cannot be fixed by using `for...of` – Niklas E. Jun 02 '20 at 21:19
  • @CharlesBamford `peek` and `last` are non-standard properties for an array. Since they are also set as enumerable, it seems like something is modifying the array prototype. It wouldn't be the user agent as I really doubt it would add enumerable properties to a global object. – VLAZ Jun 02 '20 at 21:21
  • @CharlesBamford it used to not do that with the same stuff I had before. A bad dependency seems to be adding these properties to all arrays, marking them as non-enumerable. VLAZ that sounds like an enormous pain in the neck, but I guess it's the best option – sg.cc Jun 02 '20 at 21:21
  • @VLAZ My point was that if the code is unchanged since the upgrade, but it worked before, the user agent is the most likely cause for the code breaking. I agree that the best step forward would be to fix the code. – Charlie Bamford Jun 02 '20 at 21:22

3 Answers3

2

A for-in loop iterates over an object’s enumerable properties. I think you want a for-of which iterates or entries in an iterable, or forEach, which iterates over the elements in an array.

If there’s code in your app that’s modifying Array.prototype and adding peek and last and not marking them as non-enumerable, a for-in loop is going to see them.

Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method),

Object.defineProperty will let you add those methods with enumerable: false (the default).

For starters I’d probably just search the codebase (including node_modules) for ‘peek =‘ to see if that turns it up somewhere. Or look at your dependency list for some utility that seems like a likely culprit.

ray
  • 26,557
  • 5
  • 28
  • 27
  • Any idea how I can find **what** might be adding `peek` and `last` to arrays? – sg.cc Jun 02 '20 at 21:15
  • Saw your edit; I was not the one who added these personally, I believe it was a dependency that was installed at some point. Do you know of a way to trace down where these properties are being assigned from? – sg.cc Jun 02 '20 at 21:18
  • 1
    For starters I’d probably just search the codebase (including node_modules) for ‘peek =‘ to see if that turns it up somewhere. Or look at your dependency list for some utility that seems like a likely culprit. – ray Jun 02 '20 at 21:20
  • Yeah, I'm doing that now. Unfortunately, `peek` is a rather common keyword, but hopefully this will be faster than just removing dependencies by elimination. – sg.cc Jun 02 '20 at 21:27
  • I searched github for `Array.prototype.peek` and came up with only a handful or results. (I’m on my phone so it’s hard to hunt this down.) Are you using launcher.user.js or bot.user.js? – ray Jun 02 '20 at 21:28
  • If you’re in a hurry you could probably also just set enumerable to false on Array.prototype for those properties. – ray Jun 02 '20 at 21:31
  • Neither; it was `dirty-json`. I will be editing my answer to reflect the real cause, and will file an issue with the maintainer of that package. If you would update your answer with your recommendation to search my dependencies (since that's what led me to the real issue), I will accept your answer. – sg.cc Jun 02 '20 at 21:31
1

You are seeing this because for-in does not do what you expect. You should use for-of instead.

From MDN "The for...in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties."

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

For the record everyone who learns javascript eventually get bit by this as its very non intuitive. It even used to work in browsers because they recognized that people make this mistake.

Deadron
  • 5,135
  • 1
  • 16
  • 27
  • It looks like some dependency is adding non-standard properties to arrays. For the record, I had used `for/in` for quite a while without any problems. This appears to be an issue with a bad dependency, and not a problem specific to `for/in`. – sg.cc Jun 02 '20 at 21:24
  • This is the perfect example of why you should never use for-in with arrays as its behavior is not what you intuitively expect it to be. Just find and replace all of the instances with for-of and you are protected against this in the future. – Deadron Jun 02 '20 at 21:28
  • I already use `for/of` where I can. `for/in` is where I actually need the index itself. Regardless, I have found the source of the issue in a dependency that was recently installed, and will be addressing it. – sg.cc Jun 02 '20 at 21:29
0

Try using a for ... of ... loop instead. for in loops over all enumerable properties of an object, even those inherited from the prototype chain, whereas for of only iterates over the object's own properties, in this case, array indices.

bigblind
  • 12,539
  • 14
  • 68
  • 123