From: Lady Date: Sun, 3 Dec 2023 19:51:19 +0000 (-0500) Subject: Add methods for own entries and values to object.js X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/HEAD?ds=inline;hp=54b756dbfe3e86b528253082215a9354e14313f0 Add methods for own entries and values to object.js --- diff --git a/object.js b/object.js index bd310ca..3221e1d 100644 --- a/object.js +++ b/object.js @@ -10,6 +10,7 @@ import { IS_CONCAT_SPREADABLE, isAccessorDescriptor, + isDataDescriptor, SPECIES, toFunctionName, toLength, @@ -17,6 +18,7 @@ import { UNDEFINED, } from "./value.js"; +const createArray = Array; const { isArray } = Array; const object = Object; const { @@ -357,6 +359,8 @@ export const getOwnPropertyDescriptors = (O) => { const keys = ownKeys(obj); const descriptors = {}; for (let k = 0; k < keys.length; ++k) { + // Iterate over the keys of the provided object and collect its + // descriptors. const key = keys[k]; defineOwnDataProperty( descriptors, @@ -368,7 +372,29 @@ export const getOwnPropertyDescriptors = (O) => { }; /** - * Returns an array of property keys on the provided value. + * Returns an array of own property entries on the provided value, + * using the provided receiver if given. + */ +export const getOwnPropertyEntries = (O, Receiver = O) => { + const obj = toObject(O); + const keys = ownKeys(obj); + const target = Receiver === UNDEFINED ? obj : toObject(Receiver); + const result = createArray(keys.length); + for (let k = 0; k < keys.length; ++k) { + // Iterate over each key and add the corresponding entry to the + // result. + const key = keys[k]; + defineOwnDataProperty( + result, + k, + [key, getOwnPropertyValue(obj, keys[k], target)], + ); + } + return result; +}; + +/** + * Returns an array of own property keys on the provided value. * * ※ This is effectively an alias for `Reflect.ownKeys`, except that * it does not require that the argument be an object. @@ -397,6 +423,53 @@ export const getOwnPropertyStrings = (O) => getOwnPropertyNames(O); export const getOwnPropertySymbols = (O) => objectGetOwnPropertySymbols(O); +/** + * Returns the value of the provided own property on the provided + * value using the provided receiver, or undefined if the provided + * property is not an own property on the provided value. + * + * ※ If the receiver is not provided, it defaults to the provided + * value. + */ +export const getOwnPropertyValue = (O, P, Receiver = UNDEFINED) => { + const obj = toObject(O); + const desc = getOwnPropertyDescriptor(O, P); + if (desc === UNDEFINED) { + // The provided property is not an own property on the provided + // value; return undefined. + return UNDEFINED; + } + if (isDataDescriptor(desc)) { + // The provided property is a data property; return its value. + return desc.value; + } else { + // The provided property is an accessor property; get its value + // using the appropriate receiver. + const target = Receiver === UNDEFINED ? obj : toObject(Receiver); + return call(desc.get, target, []); + } +}; + +/** + * Returns an array of own property values on the provided value, using + * the provided receiver if given. + */ +export const getOwnPropertyValues = (O, Receiver = O) => { + const obj = toObject(O); + const keys = ownKeys(obj); + const target = Receiver === UNDEFINED ? obj : toObject(Receiver); + const result = createArray(keys.length); + for (let k = 0; k < keys.length; ++k) { + // Iterate over each key and collect the values. + defineOwnDataProperty( + result, + k, + getOwnPropertyValue(obj, keys[k], target), + ); + } + return result; +}; + /** * Returns the value of the provided property key on the provided * value. diff --git a/object.test.js b/object.test.js index 348d06d..cba44b9 100644 --- a/object.test.js +++ b/object.test.js @@ -30,9 +30,12 @@ import { getMethod, getOwnPropertyDescriptor, getOwnPropertyDescriptors, + getOwnPropertyEntries, getOwnPropertyKeys, getOwnPropertyStrings, getOwnPropertySymbols, + getOwnPropertyValue, + getOwnPropertyValues, getPropertyValue, getPrototype, hasOwnProperty, @@ -840,6 +843,58 @@ describe("getOwnPropertyDescriptors", () => { }); }); +describe("getOwnPropertyEntries", () => { + it("[[Call]] gets own (but not inherited) property entries", () => { + assertEquals( + getOwnPropertyEntries({ success: true }), + [["success", true]], + ); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertEquals( + getOwnPropertyEntries("foo"), + [["0", "f"], ["1", "o"], ["2", "o"], ["length", 3]], + ); + }); + + it("[[Call]] uses the provided receiver", () => { + const target = {}; + assertEquals( + getOwnPropertyEntries({ + get success() { + return this; + }, + }, target), + [["success", target]], + ); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getOwnPropertyEntries(null)); + assertThrows(() => getOwnPropertyEntries(undefined)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getOwnPropertyEntries({})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getOwnPropertyEntries.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + getOwnPropertyEntries.name, + "getOwnPropertyEntries", + ); + }); + }); +}); + describe("getOwnPropertyKeys", () => { it("[[Call]] gets own (but not inherited) property keys", () => { assertEquals(getOwnPropertyKeys({ success: true }), ["success"]); @@ -950,6 +1005,115 @@ describe("getOwnPropertySymbols", () => { }); }); +describe("getOwnPropertyValue", () => { + it("[[Call]] gets the own property value", () => { + assertStrictEquals( + getOwnPropertyValue({ success: true }, "success"), + true, + ); + }); + + it("[[Call]] returns undefined for non‐own properties", () => { + assertStrictEquals( + getOwnPropertyValue(Object.create({ success: true }), "success"), + undefined, + ); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertStrictEquals(getOwnPropertyValue("foo", "length"), 3); + }); + + it("[[Call]] uses the provided receiver", () => { + const target = {}; + assertStrictEquals( + getOwnPropertyValue( + { + get success() { + return this; + }, + }, + "success", + target, + ), + target, + ); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getOwnPropertyValue(null)); + assertThrows(() => getOwnPropertyValue(undefined)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getOwnPropertyValue({})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getOwnPropertyValue.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + getOwnPropertyValue.name, + "getOwnPropertyValue", + ); + }); + }); +}); + +describe("getOwnPropertyValues", () => { + it("[[Call]] gets own (but not inherited) property values", () => { + assertEquals(getOwnPropertyValues({ success: true }), [true]); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertEquals( + getOwnPropertyValues("foo"), + ["f", "o", "o", 3], + ); + }); + + it("[[Call]] uses the provided receiver", () => { + const target = {}; + assertEquals( + getOwnPropertyValues({ + get success() { + return this; + }, + }, target), + [target], + ); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getOwnPropertyValues(null)); + assertThrows(() => getOwnPropertyValues(undefined)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getOwnPropertyValues({})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getOwnPropertyValues.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + getOwnPropertyValues.name, + "getOwnPropertyValues", + ); + }); + }); +}); + describe("getPropertyValue", () => { it("[[Call]] gets property values on the provided object", () => { assertStrictEquals(