lukeed / klona

A tiny (240B to 501B) and fast utility to "deep clone" Objects, Arrays, Dates, RegExps, and more!
MIT License
1.62k stars 43 forks source link

replace `__proto__` with `Object.getPrototypeOf` #46

Open Xuhv opened 3 months ago

Xuhv commented 3 months ago

When I use zag ui with deno, some components doesn't work because deno doesn't implement __proto__.

Object.getPrototypeOf also has good compatibility, so I think replacing __proto__ is ok.

https://github.com/chakra-ui/zag/issues/1717

Nevro commented 3 months ago

To not waste too many words...

import { klona as klonaLite } from 'klona/lite';
const obj = Object.create(null, Object.getOwnPropertyDescriptors({
  key1: 'val1', 
  key2: 'val2' 
}));
assert.deepStrictEqual(klonaLite(obj), obj);
/*
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ {
- [Object: null prototype] {
    key1: 'val1',
    key2: 'val2'
  }
  generatedMessage: true,
  code: 'ERR_ASSERTION',
  actual: { key1: 'val1', key2: 'val2' },
  expected: [Object: null prototype] { key1: 'val1', key2: 'val2' },
  operator: 'deepStrictEqual'
}*/
import { klona as klonaLite } from 'klona/lite';
const obj = {
  key1: 'val1',
  key2: 'val2',
  __proto__: {
    key3: 'val3-parent',
    key4: 'val4-parent',
  }
};
assert.deepStrictEqual(klonaLite(obj), obj);
/*
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
  {
    key1: 'val1',
    key2: 'val2',
+   key3: 'val3-parent',
+   key4: 'val4-parent'
  }
  generatedMessage: true,
  code: 'ERR_ASSERTION',
  actual: {
    key1: 'val1',
    key2: 'val2',
    key3: 'val3-parent',
    key4: 'val4-parent'
  },
  expected: { key1: 'val1', key2: 'val2' },
  operator: 'deepStrictEqual'
}*/

Object.prototype.__proto__ is an accessor property with attribute Enumerable: false

The for...in statement iterates over all enumerable string properties of an object including inherited enumerable properties.

patch for lite/basic:

...
var k, proto, tmp, str=Object.prototype.toString.call(x);

if (str === '[object Object]') {
    if (x.constructor === undefined) {
        tmp = Object.create(null);
        for (k in x)
            tmp[k] = klona(x[k]);
    } else {
        proto = x.__proto__ || Object.getPrototypeOf(x);
        tmp = (proto !== Object.prototype)
            ? Object.create(proto)
            : {};
        for (k in x)
            if (Object.prototype.hasOwnProperty.call(x, k) === true)
                tmp[k] = klona(x[k]);
    }
    return tmp;
}
...

Now implement this to full version..