Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Object.entries instead of for…in to iterate over arrays #62

Merged
merged 1 commit into from Nov 19, 2020

Conversation

TylerRick
Copy link
Contributor

@TylerRick TylerRick commented Sep 24, 2020

... because object may be an array and for…in cannot be safely used for arrays.

I ran into a bug in a project I'm working on where isValid was incorrectly returning false. I tried to reproduce in a sandbox and found that it returned true there.

When I dug deeper I found that getValues($touched) was returning some extra values here:

  const isValid = derived([errors, touched], ([$errors, $touched]) => {
    const allTouched = util
      .getValues($touched)
      .every((field) => field === IS_TOUCHED);
    const noErrors = util
      .getValues($errors)
      .every((field) => field === NO_ERROR);
    return allTouched && noErrors;
  });

Looking at those values, they were some functions which were defined on Array.prototype. Digging deeper I found that this was due to the for…in loop in getValues.

Apparently, it is recommended to never use for…in to loop over the elements of array (see here and here, for example)).

We could debate whether apps should be adding functions to Array.prototype. But regardless of how you feel about that, I think we should fix this library to loop over arrays properly and safely.

Object.entries() seems to be a reliable way to iterate over regular objects and arrays (so we don't have to have a branch based on Array.isArray like we have in assignDeep, for example), so that's the solution I used in this pull request. Let me know if there's a better way though.

This also removes the duplication in the object[key] lookups (we were looking it up 3 times in this function).

Here is a sandbox reproducing the bug: https://codesandbox.io/s/competent-thompson-6lcid?file=/App.svelte

... because object may be an array and for…in cannot be safely used for arrays.
@@ -27,9 +27,9 @@ function isEmpty(object) {

function getValues(object) {
let result = [];
for (const key in object) {
for (const [, value] of Object.entries(object)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe Object.values might be better suited here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tiaanduplessis ye I agree - browser support for Object.entries and Object.values appears to be the same, so we might as well use .values:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

An aside - do you know what the best practice is for using "new" Javascript specs in modules?

I generally iterate over Object.keys to avoid adding polyfills, at the expense of having to manually fetch values out of the object.

Is it up to the library to provide support for older browsers, or is it up to the user to add polyfills where necessary?

I imagine the responsible thing is to indicate which polyfills are necessary for older browsers when newer features are used in a module.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@larrybotha I'm not quite sure what is the current best practice for Svelte related modules, but I'd probably go with the user needing to add polyfilling where required and then indicating newer features used in the module README for full transparency.

The reason being that using core-js via babel will allow the user the flexibility to configure exactly what they want via browserlist and since Svelte does not compile down to older versions of JS out of the box the extra configuration will need to be added in any case to handle modern syntax and polyfiliing of popular browser APIs like Promises.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tiaanduplessis ye, sounds good to me!

@tiaanduplessis
Copy link
Collaborator

@TylerRick thank you for the very detailed explanation of the issue 👍

@larrybotha larrybotha merged commit facf04d into tjinauyeung:master Nov 19, 2020
@tjinauyeung
Copy link
Owner

🎉 This PR is included in version 1.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants