Skip to content
This repository was archived by the owner on Jul 23, 2021. It is now read-only.

Fix #159: support isPlainObj in IE11 and constructor.name props #194

Merged
merged 1 commit into from
Nov 20, 2020

Conversation

bdurrer
Copy link

@bdurrer bdurrer commented Nov 19, 2020

Deals with IE9-IE11 and correctly evaluates { constructor: { name: 'bamboozled' }}
I am not that happy with exporting isPlainObject, if you see a better alternative to test it, be my guest

@bdurrer
Copy link
Author

bdurrer commented Nov 19, 2020

To my suprise, it's even faster in Chrome and FF: https://jsben.ch/ApnQr

value.constructor.name === 'Object')
);
export default function isPlainObject(value) {
if (!value || typeof value !== 'object') {
Copy link

@Methuselah96 Methuselah96 Nov 19, 2020

Choose a reason for hiding this comment

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

Should we use toString to fail more quickly for some objects (e.g., dates, array, etc.)?

Suggested change
if (!value || typeof value !== 'object') {
if (!value || typeof value !== 'object' || Object.prototype.toString.call(value) !== '[object Object]') {

Choose a reason for hiding this comment

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

Actually I think this change is necessary in order for these test cases (from the lodash tests) to pass:

it('should return `false` for non-Object objects', () => {
  expect(isPlainObject(arguments)).toBe(false);
  expect(isPlainObject(Math)).toBe(false);
});

it('should return `false` for objects with a read-only `Symbol.toStringTag` property', () => {
  const object = {};
  Object.defineProperty(object, Symbol.toStringTag, {
    'configurable': true,
    'enumerable': false,
    'writable': false,
    'value': 'X'
  });

  expect(isPlainObject(object)).toBe(false);
});

Copy link
Author

@bdurrer bdurrer Nov 20, 2020

Choose a reason for hiding this comment

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

The toString method is a weak workaround for IE8 and lower, because it does not support getPrototypeOf.
But calling functions on unknown objects is a stupid way to deal with anything. It is likely slowing things down needlessly and depends on the output of toString, which is not only dependent on browser type and version, but can be customized by everyone! Existence/Absence and internas of such a function are simply not reliable.

'[object Object]' is what non- ES5 browsers like IE8 used to return

Copy link
Member

Choose a reason for hiding this comment

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

Just stop yourself right there. IE8 and lower! What year is it again? We're supposed to be living in a world with Replicants where LA is a megacity with corporations in pyramids!

The thing is, immutable doesn't even really work right on IE8 anyway. It's always been a collection of godawful hacks. And it's appreciable that they've done it, but anyone still stuck with IE8 can also be stuck with immutable@3. Given that we can't have it both ways without making sacrifices, I think it's absolutely clear what we should sacrifice : )

Copy link
Member

Choose a reason for hiding this comment

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

Furthermore using Object.getPrototypeOf isn't even dropping support for IE8. IE8 had __proto__ and thus is capable of receiving a polyfill for that functionality. Anyone still supporting it had BETTER be including those polyfills through CoreJS, and if they aren't, well you'd think that the major version number (and having to change the package name) might tip them off to the potential for breaking changes. We just need to update the docs and also we really aught to have a CHANGELOG, though I don't relish the thought of trying to create one retroactively for changes that I didn't make.

Copy link
Author

Choose a reason for hiding this comment

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

Besides a changelog we should somewhere mention what we think we support. I mean it's already suprising to have an NPM lib that works without babel :)

Choose a reason for hiding this comment

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

Shouldn't isPlainObject(arguments) and isPlainObject(Math) return false? This is not just an IE8 issue, this is relevant in the latest Chrome:

image

Copy link
Author

@bdurrer bdurrer Nov 20, 2020

Choose a reason for hiding this comment

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

I would generally claim that it's your own fault if you expect Immutable.Map(Math) or fromJS(Math) to produce anything useful. I added the Object.prototype.toString call as it is not affected by custom prototypes/objects having a custom toString function.
I could imagine someone overwriting Object.prototype.toString to prettyprint stuff, but I guess they would be equally to blame as the guy writing fromJS(Math). At least fromJS(arguments) could be remotely useful.

Comment on lines 13 to 14
const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || proto === null;
Copy link

@Methuselah96 Methuselah96 Nov 19, 2020

Choose a reason for hiding this comment

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

I noticed that lodash is walking up the prototype chain when comparing the prototype. Do we have worry about that? Looks like it was added in this PR although I didn't dig into differing contexts enough to understand why it's necessary.

Copy link
Member

Choose a reason for hiding this comment

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

Oh because if you have multiple frames you can essentially have Object.prototype !== Object.prototype. That code ensures that an object can still be considered "plain" even if it has a different prototype than your realm's Object.prototype. I'd say there's no reason not to incorporate that improvment.

@conartist6 conartist6 linked an issue Nov 20, 2020 that may be closed by this pull request
@conartist6
Copy link
Member

Doesn't look like github picks up issue numbers in the PR title. I think they have to be in the commit message body with appropriate verbiage, e.g. Fixes #.... I linked the issue.

I vote we just copy pasta the lodash implementation.

@bdurrer
Copy link
Author

bdurrer commented Nov 20, 2020

I vote we just copy pasta the lodash implementation.

I disagree, and you kinda do too in the comments above?

Lodash has a different target. It checks the whole tree, which is not the task at hand. isPlainObject is used in fromJS to determine if this is a key-value template we can convert or something we should not touch and return unchanged.

The second place it is used is in isDataStructure, which has the same target as in the fromJS code: Check whether something is a key-value data structure which allows immutable's function to be used on.

BTW the lodash implementation fails to detect Object.create(null).. as plain object.

@bdurrer bdurrer changed the title FIX: support isPlainObj in IE11 and constructor.name props (#159) Fix #159: support isPlainObj in IE11 and constructor.name props Nov 20, 2020
@bdurrer
Copy link
Author

bdurrer commented Nov 20, 2020

Last push just changed the message to link the issue using the correct verb

@Methuselah96
Copy link

Pretty sure the lodash implementation correctly detects Object.create(null) as a plain object because it checks for Object.getPrototypeOf(value) === null here.

@conartist6
Copy link
Member

conartist6 commented Nov 20, 2020

Lodash doesn't have a different target, and it doesn't actually check the whole tree. It just walks to the bottom to find an Object.prototype, specifically because the value obtained by evaluating Object.prototype may not === the prototype at the bottom of an object's prototype chain if the object was created in a different realm.

@bdurrer bdurrer force-pushed the fix-159 branch 4 times, most recently from 8e5496b to cfeebcd Compare November 20, 2020 20:23
Copy link

@Methuselah96 Methuselah96 left a comment

Choose a reason for hiding this comment

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

Thanks! Might as well have @conartist6 approve it as well.

Copy link
Member

@conartist6 conartist6 left a comment

Choose a reason for hiding this comment

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

Ship it.

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

Successfully merging this pull request may close these issues.

[Bug] isPlainObj fn not compatible for IE11
3 participants