├── .gitignore
├── LICENSE.txt
├── README.md
├── out
├── index.html
├── spec.css
└── spec.js
├── package.json
├── reference-implementation
├── README.md
└── index.js
├── spec.html
└── test
└── built-ins
└── Object
└── getOwnPropertyDescriptors
└── has-accessors.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | npm-debug.log
3 | out/
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Andrea Giammarchi
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | The views and conclusions contained in the software and documentation are those
25 | of the authors and should not be interpreted as representing official policies,
26 | either expressed or implied, of the FreeBSD Project.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `Object.getOwnPropertyDescriptors` Proposal ([Polyfill](https://www.npmjs.com/package/object.getownpropertydescriptors))
2 |
3 |
4 | ## Champion
5 |
6 | At stage 0 [Rick Waldron](https://github.com/rwaldron) agreed to champion this proposal.
7 | However the **current** official Champion is **[Jordan Harband](https://github.com/ljharb)**.
8 |
9 |
10 |
11 | ## Status
12 |
13 | This proposal is currently in [stage 4](https://github.com/tc39/proposals/blob/master/finished-proposals.md) of [the TC39 process](https://github.com/tc39/ecma262/).
14 |
15 | This proposal could be identical to a `Reflect.getOwnPropertyDescriptors` one but for consistency with other plural versions it's described as an `Object` public static method.
16 |
17 | ## Motivation
18 |
19 | There is not a single method in ECMAScript capable of simplifying a proper copy between two objects.
20 | In these days more than ever, where functional programming and immutable objects are essential parts of complex applications, every framework or library is implementing its own boilerplate in order to properly copy properties between composed objects or prototypes.
21 |
22 | There is a lot of confusion and most of the time undesired behavior when it comes to fallback to `Object.assign` because it copies in a way that swallows behavior: it directly accesses properties and symbols instead of their descriptors, discarding possible accessors which could result into an hazard when it come to composing more complex objects or classes’ prototypes.
23 |
24 | Retrieving all descriptors, enumerable or not, is also key to implementing composition over `class`es and their prototypes, since by default they have non-enumerable methods and accessors.
25 |
26 | Also decorators could easily grab at once all descriptors from another class or mixin and assign them through `Object.defineProperties`.
27 | Filtering undesired descriptors would be simpler too, as well as less repetitive each time is needed.
28 |
29 | Last, but not least, a shallow copy between two unknown objects would be free of surprises compared to what `Object.assign` would do.
30 |
31 |
32 | ## FAQs
33 |
34 | ### Should there be a `Reflect.getOwnPropertyDescriptors` ?
35 |
36 | Since the main goal of this proposal is to simplify some common boilerplate and be consistent with the fact there is a singular version of the method but not a plural one, it might be further consistent to have the plural version of the current [Reflect.getOwnPropertyDescriptor](http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.getownpropertydescriptor) method too.
37 |
38 | Update: The committee has previously decided that `Reflect` is solely to mirror `Proxy` traps, so this is not an option.
39 |
40 |
41 | ## Proposed Solution
42 |
43 | As plural version of `Object.getOwnPropertyDescriptor`, this proposal is about retrieving in one single operation all possible own descriptors of a generic object.
44 |
45 | A **polyfill** of such proposal would look like the following:
46 | ```js
47 | if (!Object.hasOwnProperty('getOwnPropertyDescriptors')) {
48 | Object.defineProperty(
49 | Object,
50 | 'getOwnPropertyDescriptors',
51 | {
52 | configurable: true,
53 | writable: true,
54 | value: function getOwnPropertyDescriptors(object) {
55 | return Reflect.ownKeys(object).reduce((descriptors, key) => {
56 | return Object.defineProperty(
57 | descriptors,
58 | key,
59 | {
60 | configurable: true,
61 | enumerable: true,
62 | writable: true,
63 | value: Object.getOwnPropertyDescriptor(object, key)
64 | }
65 | );
66 | }, {});
67 | }
68 | }
69 | );
70 | }
71 | ```
72 |
73 |
74 | ## Illustrative Examples
75 |
76 | The polyfill shows an alternative, ES2015 friendly, way that improves the boilerplate needed for engines compatible with ES5 or partially with ES2015.
77 |
78 | Now that `Object.getOwnPropertyDescriptors` is in place, all it's needed in order to make a real shallow copy or clone operation between two objects, is shown in the following example:
79 | ```js
80 | const shallowClone = (object) => Object.create(
81 | Object.getPrototypeOf(object),
82 | Object.getOwnPropertyDescriptors(object)
83 | );
84 |
85 | const shallowMerge = (target, source) => Object.defineProperties(
86 | target,
87 | Object.getOwnPropertyDescriptors(source)
88 | );
89 | ```
90 |
91 | Possible objects based mixin solutions could also benefit from this proposal:
92 | ```js
93 | let mix = (object) => ({
94 | with: (...mixins) => mixins.reduce(
95 | (c, mixin) => Object.create(
96 | c, Object.getOwnPropertyDescriptors(mixin)
97 | ), object)
98 | });
99 |
100 | // multiple mixins example
101 | let a = {a: 'a'};
102 | let b = {b: 'b'};
103 | let c = {c: 'c'};
104 | let d = mix(c).with(a, b);
105 | ```
106 |
107 |
108 | Let's say you wanted a version of Object.assign that uses `[[DefineOwnProperty]]`/`[[GetOwnProperty]]` instead of `[[Set]]`/`[[Get]]`, to avoid side effects and copy setters/getters, but still use enumerability as the distinguishing factor.
109 |
110 | Before this proposal, such a method would look like:
111 | ```js
112 | function completeAssign(target, ...sources) {
113 | sources.forEach(source => {
114 | // grab keys descriptors
115 | let descriptors = Object.keys(source).reduce((descriptors, key) => {
116 | descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
117 | return descriptors;
118 | }, {});
119 | // by default, Object.assign copies enumerable Symbols too
120 | // so grab and filter Symbols as well
121 | Object.getOwnPropertySymbols(source).forEach(sym => {
122 | let descriptor = Object.getOwnPropertyDescriptor(source, sym);
123 | if (descriptor.enumerable) {
124 | descriptors[sym] = descriptor;
125 | }
126 | });
127 | Object.defineProperties(target, descriptors);
128 | });
129 | return target;
130 | }
131 | ```
132 |
133 | However, if `Object.getOwnPropertyDescriptors` was available, above boilerplate would look like:
134 | ```js
135 | var completeAssign = (target, ...sources) =>
136 | sources.reduce((target, source) => {
137 | let descriptors = Object.getOwnPropertyDescriptors(source);
138 | Reflect.ownKeys(descriptors).forEach(key => {
139 | if (!descriptors[key].enumerable) {
140 | delete descriptors[key];
141 | }
142 | });
143 | return Object.defineProperties(target, descriptors);
144 | }, target);
145 | ```
146 |
--------------------------------------------------------------------------------
/out/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
When the `getOwnPropertyDescriptors` function is called, the following steps are taken:
18 |
19 |
20 | 1. Let _obj_ be ? ToObject(_O_).
21 | 1. Let _ownKeys_ be ? _obj_.[[OwnPropertyKeys]]().
22 | 1. Let _descriptors_ be ! ObjectCreate(%ObjectPrototype%).
23 | 1. Repeat, for each element _key_ of _ownKeys_ in List order,
24 | 1. Let _desc_ be ? _obj_.[[GetOwnProperty]](_key_).
25 | 1. Let _descriptor_ be ! FromPropertyDescriptor(_desc_).
26 | 1. Perform ! CreateDataProperty(_descriptors_, _key_, _descriptor_).
27 | 1. Return _descriptors_.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/built-ins/Object/getOwnPropertyDescriptors/has-accessors.js:
--------------------------------------------------------------------------------
1 | var a = {get a() {}};
2 | var b = Object.getOwnPropertyDescriptors(a);
3 |
4 |
5 | assert(b.a.get === Object.getOwnPropertyDescriptor(a, 'a').get,
6 | 'Expected descriptors.a.get to be exact same of Object.getOwnPropertyDescriptor(object, "a").get');
--------------------------------------------------------------------------------