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

Helper runtime function for object property definitions #174

Open
overlookmotel opened this issue Apr 30, 2021 · 3 comments
Open

Helper runtime function for object property definitions #174

overlookmotel opened this issue Apr 30, 2021 · 3 comments
Labels
optimization Improvement to performance or output

Comments

@overlookmotel
Copy link
Owner

overlookmotel commented Apr 30, 2021

Livepack's output often includes a lot of verbose Object.defineProperties() calls like:

Object.defineProperties( obj, {
  x: { value: 123, writable: true, configurable: true },
  y: { value: 456, writable: true, configurable: true }
} );

Could instead use a runtime helper function which can be reused.

Aims

Ideally it would:

  1. Replace all of Object.assign(), Object.defineProperty() and Object.defineProperties()
  2. Take an object argument ({a:1} is 2 chars shorter than ['a',1]).
  3. Handle property called __proto__.
  4. Use a bitmap for descriptors i.e. values of 0-7 could represent all possible combinations of writable, enumerable, configurable.
  5. Have a shortcut form where descriptors are all true.
  6. Also be able to replace Object.create().
  7. Also be able to replace Object.setPrototypeOf().
  8. Simplify implementation of adding properties to objects, which currently is very complex.
  9. Make all assignments chainable, which will make issues like Function binding does not take into account when target function's prototype is set #127 and Inline assignments #130 easier to solve.

Implementation

Below is a possible API for this helper function which prioritizes the shortest possible calling signature over compexity + length of the helper function. In any app which is of above trivial size, there will be a lot of calls to this function, so that should lead to less bytes in total.

Set object properties

Helper function defineProperties() would be called as follows:

// Shortcut form where all descriptors `true`
defineProperties( obj, { x: 123, y: 456 } );

// Long form where all descriptors `false` - 2nd array item is bitmap
defineProperties( obj, { x: [ 123, 7 ], y: [ 456, 7 ] } );

// Array value - Disambiguated by wrapping value in another array
defineProperties( obj, { x: [ [ 1, 2, 3 ] ] } );

// Getter/setter - Disambiguated by 2nd array item being non-integer
defineProperties( obj, { x: [ function getter() {}, function setter() {} ] } );

// Getter/setter with all descriptors `false` - 3rd array item is bitmap
defineProperties( obj, { x: [ function getter() {}, function setter() {}, 7 ] } );

// Getter only - Disambiguated by 2nd array item not being number
defineProperties( obj, { x: [ function getter() {}, , ] } );

// Setter only - Disambiguated by 2nd array item not being number
defineProperties( obj, { x: [ , function setter() {} ] } );

// Property name is '__proto__' - signalled by 4th bit of bitmap
defineProperties( obj, { a: [ 'proto', 8 ] } );

defineProperties() would return same object so it's chainable.

No need to also provide a defineProperty() method for defining a single property as d(o,{x:123}) is same number of chars as d(o,'x',123).

Create object with prototype

1st arg 1 means create new Object with Object.create() using 2nd arg as prototype and 3rd arg as object props:

// Object with prototype `Klass.prototype` and no properties
defineProperties( 1, Klass.prototype )

// Object with prototype `Klass.prototype` with properties
defineProperties( 1, Klass.prototype, { x: 123, y: 456 } )

Only 0 or 1 args means create Object with null prototype, using 1st arg as object props:

// `null` prototype object with no properties
defineProperties()

// `null` prototype object with properties
defineProperties( { x: 123, y: 456 } )

Set prototype

3 args where 1st arg is Object means set prototype as 3rd arg:

// Just set prototype
defineProperties( obj, 0, Klass.prototype )

// Define props at same time
defineProperties( obj, { x: 123, y: 456 }, Klass.prototype )

1st arg with value 2 means set prototype to null:

// Just set prototype
defineProperties( 2, obj )

// Define props at same time
defineProperties( 2, obj, { x: 123, y: 456 } )

Simple cases

For really simple cases, could just use direct assignments as they're shorter i.e. o.a=1 is shorter than d(o,{a:1}). The functional form gets shorter only with 4 props or more (not including the helper function itself).

d(o,{a:1})
o.a=1 // Shorter

d(o,{a:1,b:2,c:3,d:4})
o.a=1,o.b=2,o.c=3,o.d=4 // Longer
@overlookmotel overlookmotel added the optimization Improvement to performance or output label Apr 30, 2021
@overlookmotel
Copy link
Owner Author

overlookmotel commented Apr 30, 2021

@overlookmotel
Copy link
Owner Author

Actually, it's unnecessary to overload this function to also replace Object.create() and Object.setPrototypeOf(). They can be combined with it using defineProps( Object.create( null ), { /* ... */ } ) / Object.setPrototypeOf( defineProps( { /* ... */ } ) , proto ).

Only overload should be where base is an empty object, in which case first arg can be omitted. defineProperties( { x: [ 1, 7 ] } ) is equivalent to defineProperties( {}, { x: [ 1, 7 ] } ).

There's also Object.freeze(), Object.seal() and Object.preventExtensions() to consider - again these should be used separately rather than bloating defineProps() with this functionality.

Should have a separate helper for deleting properties.

Also need to deal with where intent is not to overwrite value, only set descriptors. This can be indicated with single-item definition array e.g. defineProperties( [ 1, 2, 3 ], { 0: [ 7 ] } ) sets all property descriptors for first array item to false but leaves its value as 1.

These changes made on define-props branch.

@overlookmotel
Copy link
Owner Author

The special handling of a property called __proto__ can be removed. See #565.

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

No branches or pull requests

1 participant