icedam icon indicating copy to clipboard operation
icedam copied to clipboard

Improve deepFreeze function a bit

Open romanenko opened this issue 8 years ago • 7 comments

Check for null properties and freeze functions as well

romanenko avatar Aug 10 '15 01:08 romanenko

Hey, thanks for the PR! Silly question - why freeze functions? With the JSON serialization/cloning, seems like they'll be stripped out-

image

winkler1 avatar Aug 10 '15 11:08 winkler1

Also, I believe freezing functions requires some more care as you cannot freeze caller, callee, or arguments in strict mode.

Not freezing null is a must though, it'll throw in browsers/phantomjs if you don't.

It's unfortunate that https://github.com/substack/deep-freeze and https://github.com/jsdf/deep-freeze are buggy. If they weren't, we wouldn't need to roll our own

Maybe it'd be worth pulling out a common implementation for just the freeze that updeep and icedam can depend on?

aaronjensen avatar Aug 12 '15 05:08 aaronjensen

As an aside, @winkler1, I noticed you used Object.getOwnPropertyNames instead of Object.keys. The former gets non-enumerable properties and the latter does not. In your experience, is there need for the former? I'm wondering if I should switch mine... the types of things updeep deals w/ should be plain objects/arrays, so I don't feel like I need to worry about read-only properties, but that could be naive

aaronjensen avatar Aug 12 '15 05:08 aaronjensen

@aaronjensen Adding the null check and an explicit test, thanks. The Redux devtools make a point of saying to use serializable data structures for its time travel abilities. Will beef up the tests for null and function.

A common deepFreeze sounds good if you want. My only concern would be that the import/require can be guarded by a if dev block:

var deepFreeze, shallowEqual;

if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
  deepFreeze = require('commonDeepFreeze');
  shallowEqual = require('react-pure-render/shallowEqual');
}

Really want to ensure that in production this is a 3-line no-op, no dependencies...

winkler1 avatar Aug 12 '15 11:08 winkler1

@aaronjensen re:enumerable - now that you mention it... non-enumerable props are stripped by JSON.stringify cloning, so Object.keys would work just as well.

image

I'll add a test to make this explicit:

  it('Removes non-enumerable properties', function () {
    var input = {};
    Object.defineProperties(input, {
      a: {enumerable: true, value: 'a'},
      b: {enumerable: false, value: 'b'}
    });
    var output = freeze(input);
    expect(Object.keys(output)).toEqual(['a']);
  });

winkler1 avatar Aug 12 '15 12:08 winkler1

:+1:

wrt to dev vs prod, a couple things:

You might consider looking for NODE_ENV !== 'production' as the sign to include things as this matches react: https://github.com/facebook/react/blob/d16481d0e7d23771c5d94e5a0eeaf64e02f07979/packages/react/react.js#L9

Also, for this lib it'd remove a lot of conditionals and may make the code a bit easier if you did something like:

if (process.env.NODE_ENV === 'production') {
  module.exports = function makeFreezer() {
    function(obj) { return obj; }
  }
} else {
  // existing code
  module.exports = function...
}

aaronjensen avatar Aug 13 '15 06:08 aaronjensen

Actually, I went to do this on updeep and found that everything in freeze.js was already dead code eliminated by uglify--it's pretty smart.

aaronjensen avatar Aug 13 '15 14:08 aaronjensen