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

Avoid Object.prototype collisions with defaults #3843

Merged
merged 1 commit into from
Feb 4, 2016

Conversation

jridgewell
Copy link
Collaborator

This is definitely an underscore concern, but it's easier to demo in Backbone code.

Fixes #3842.

});
model = new Defaulted({two: undefined});
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why'd you get rid of this test case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's testing the same thing as the one above it, no?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No... it's testing that passing undefined is thrown away, not that the key isn't passed at all.

Copy link
Collaborator

Choose a reason for hiding this comment

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

var defaults = {foo: true};

_.defaults({}, defaults, {foo: undefined}); // { foo: true }
_.defaults({}, defaults, {bar: undefined}); // { foo: true, bar: undefined }

_.extend({}, defaults, {foo: undefined}); // { foo: undefined }
_.extend({}, defaults, {bar: undefined}); // { foo: true, bar: undefined }

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No... it's testing that passing undefined is thrown away, not that the key isn't passed at all.

Isn't that this test?

var defaults = {foo: true};

With my "defaults" implementation:

defaults = {foo: true};

defaults({}, defaults, {foo: undefined}); // { foo: true }
defaults({}, defaults, {bar: undefined}); // { foo: true, bar: undefined }

The only difference with mine is it won't respect properties that are already on the dest object:

_.defaults({foo: false}, {foo: true}); // { foo: false }
defaults({foo: false}, {foo: true}); // { foo: true }

Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't that this test?

Sorry, missed that in the diff.

The only difference with mine is it won't respect properties that are already on the dest object

Then I'm confused. That changes _.default's behavior completely, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We're only using defaults with an "empty" destination object, so it works the same. Externally, this wouldn't be acceptable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah but I dunno. Either we should fix this in Underscore or we shouldn't fix this at all. What's good for the goose is good for the gander...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we made it return a new instance instead of modifying the destination, it would work perfectly. We're kind of headed down that path with jashkenas/underscore#2232.

@jridgewell jridgewell force-pushed the defaults-prototype-collision branch 2 times, most recently from b8bd90f to aa421d9 Compare October 30, 2015 15:54
@jdalton
Copy link
Contributor

jdalton commented Dec 15, 2015

For reference here's the current fix in lodash lodash/lodash@8a3842b.
It preserves the existing defaults iteration order and behavior of not overwriting non-undefined values.

I think it's fine to have this in Backbone temporarily until it can be addressed in a released version of Underscore.

@akre54
Copy link
Collaborator

akre54 commented Dec 15, 2015

I dunno. I feel like this is something that should be fixed in Underscore, if at all. Who names their keys hasOwnProperty? Attribute keys and collection ids really shouldn't be arbitrary like this.

@jdalton
Copy link
Contributor

jdalton commented Dec 15, 2015

I dunno. I feel like this is something that should be fixed in Underscore, if at all. Who names their keys hasOwnProperty?

It's less those classic Object.prototype method names and more of the new ones like watch in Firefox. I'm cool with punting on it for now as as wontfix too.

@akre54
Copy link
Collaborator

akre54 commented Dec 15, 2015

To be sure, watch is a pretty dumb decision on FF's part, but it's not insurmountable. Unless there's a really good reason I'd rather not introduce a subtle backwards compatibility bug like this.

@jdalton
Copy link
Contributor

jdalton commented Dec 15, 2015

Yep, though I think it can be tackled without back compat pains. The likely issues I see are with watch, unwatch, constructor, toString, and toSource properties. However, since this hasn't been tackled yet, several years in, it makes sense for the fix to be designated as a lower priority (not critical for 1.2.4).

@jridgewell
Copy link
Collaborator Author

Repinging this: I've updated the PR to use _.extends and _.defaults to work around the conflicts. What does everyone else think?

@jridgewell jridgewell changed the title Use modified defaults algorithm to avoid Object.prototype collisions Avoid Object.prototype collisions with defaults Feb 3, 2016
@jridgewell
Copy link
Collaborator Author

Ping @akre54 since you're reviewing things today. 😉

@akre54
Copy link
Collaborator

akre54 commented Feb 3, 2016

Hows the perf hit when there are a few keys?

@jridgewell
Copy link
Collaborator Author

@jridgewell
Copy link
Collaborator Author

Ping. I want to get this one in.

@akre54
Copy link
Collaborator

akre54 commented Feb 4, 2016

For the record, I think this (_.defaults(_.extend({}, defaults, attrs), defaults)) is a really ugly hack for a minor to nonexistent problem, but the perf hit isn't terrible. Hope this is the last of it.

@akre54 akre54 added the change label Feb 4, 2016
akre54 added a commit that referenced this pull request Feb 4, 2016
Avoid Object.prototype collisions with defaults
@akre54 akre54 merged commit 08a486c into jashkenas:master Feb 4, 2016
@@ -989,8 +994,8 @@

QUnit.test('`previous` for falsey keys', function(assert) {
assert.expect(2);
var model = new Backbone.Model({0: true, '': true});
model.set({0: false, '': false}, {silent: true});
var model = new Backbone.Model({'0': true, '': true});
Copy link
Collaborator

Choose a reason for hiding this comment

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

Whys this needed? JS automatically casts keys to strings

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was a liter warning for inconsistent string usage. Must have committed it by accident. 😊

Copy link
Contributor

Choose a reason for hiding this comment

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

Linting rules are not perfect 😞

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All good, they're much better now than before.

jrmils89 added a commit to jrmils89/backbone that referenced this pull request Apr 10, 2016
This first commit here makes the changes made in pull jashkenas#3843 unnecessary and
we can revert those changes. Since it will be using prototypeless objects
we don't have to worry about the collisions
@jashkenas jashkenas added the fixed label Sep 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants