Three ways to list keys — and when each is right
All three methods list properties of an object itself (not the prototype chain). The differences are about string vs symbol keys and enumerability.
| Method | String keys | Symbols | Enumerable only |
|---|---|---|---|
Object.keys | yes | no | yes |
Object.getOwnPropertyNames | yes | no | no |
Object.getOwnPropertySymbols | no | yes | no |
Reflect.ownKeys | yes | yes | no |
Example
const sym = Symbol("id");
const o = {
visible: 1,
hidden: 2,
[sym]: 3,
};
Object.defineProperty(o, "computed", { enumerable: false, value: 4 });
Object.keys(o); // ["visible", "hidden"]
Object.getOwnPropertyNames(o); // ["visible", "hidden", "computed"]
Object.getOwnPropertySymbols(o); // [Symbol(id)]
Reflect.ownKeys(o); // ["visible", "hidden", "computed", Symbol(id)]
Key ordering rules
All of these follow the same ordering: integer-like keys first, in ascending numeric order; then string keys in creation order; then symbols in creation order.
const k = { 10: "a", 2: "b", b: 1, a: 2 };
Object.keys(k); // ["2", "10", "b", "a"] — note 2 before 10
Practical guidance
- Serializing data for JSON:
Object.keys(matchesJSON.stringify, which skips non-enumerable and symbol keys). - Deep cloning or diffing:
Reflect.ownKeysso hidden and symbol-keyed fields are not silently dropped. - Inspecting library internals:
getOwnPropertyNames, because private state is often stored in non-enumerable properties.
Prototype chain is never included
const base = { inherited: 1 };
const o = Object.create(base);
o.own = 2;
Object.keys(o); // ["own"]
Object.keys(base); // ["inherited"]
For a full chain walk combine with
Object.getPrototypeOf.