Object keys can be strings or Symbol, while values can be any data types.
const obj = {
// Keys (automatically converted to strings)
"string": "value",
42: "number as key",
true: "boolean as key",
// Values (any types)
name: "string",
age: 25,
isActive: true,
items: [1, 2, 3],
nested: { a: 1 },
method: function() { return "function"; }
};
// Symbol as key
const sym = Symbol('id');
obj[sym] = "Symbol value";
Important features:
Strings are the natural type for object keys:
const obj = {
"firstName": "John",
"last-name": "Doe",
"age in years": 30,
"": "empty string is also valid",
" ": "space as key",
"123": "string of numbers"
};
console.log(obj["firstName"]); // "John"
console.log(obj["last-name"]); // "Doe"
console.log(obj[""]); // "empty string is also valid"
Numbers are automatically converted to strings:
const obj = {
42: "value",
3.14: "pi",
0: "zero",
-5: "negative"
};
// All numeric keys become strings
console.log(obj["42"]); // "value"
console.log(obj[42]); // "value" (also works)
console.log(typeof Object.keys(obj)[0]); // "string"
// Conversion check
console.log("42" in obj); // true
console.log(42 in obj); // true (auto-conversion)
Boolean values are also converted to strings:
const obj = {
true: "truth",
false: "falsy"
};
console.log(obj["true"]); // "truth"
console.log(obj[true]); // "truth"
console.log(obj["false"]); // "falsy"
console.log(obj[false]); // "falsy"
// Keys became strings
console.log(Object.keys(obj)); // ["true", "false"]
Even undefined and null become strings:
const obj = {
undefined: "not defined",
null: "emptiness"
};
console.log(obj["undefined"]); // "not defined"
console.log(obj[undefined]); // "not defined"
console.log(obj["null"]); // "emptiness"
console.log(obj[null]); // "emptiness"
Symbol is the only key type that is NOT converted to string:
const sym1 = Symbol('id');
const sym2 = Symbol('id');
const sym3 = Symbol.for('global');
const obj = {
[sym1]: "first symbol",
[sym2]: "second symbol",
[sym3]: "global symbol",
"Symbol(id)": "this is NOT a symbol, but a string!"
};
console.log(obj[sym1]); // "first symbol"
console.log(obj[sym2]); // "second symbol"
console.log(obj["Symbol(id)"]); // "this is NOT a symbol, but a string!"
// Symbol keys don't appear in Object.keys()
console.log(Object.keys(obj)); // ["Symbol(id)"]
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id), Symbol(id), Symbol(global)]
Objects as keys are converted via toString():
const obj1 = { name: "object1" };
const obj2 = { name: "object2" };
const arr = [1, 2, 3];
const container = {
[obj1]: "value for obj1",
[obj2]: "value for obj2", // Will overwrite the previous!
[arr]: "value for array"
};
// All objects became "[object Object]"
console.log(Object.keys(container)); // ["[object Object]", "1,2,3"]
console.log(container[obj1]); // "value for obj2" (!)
console.log(container["[object Object]"]); // "value for obj2"
Any primitives can be values:
const primitives = {
stringValue: "string",
numberValue: 42,
booleanValue: true,
undefinedValue: undefined,
nullValue: null,
symbolValue: Symbol('value'),
bigintValue: 123n
};
Objects, arrays and other complex types:
const complex = {
array: [1, 2, 3],
object: { nested: true },
date: new Date(),
regexp: /pattern/gi,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
weakMap: new WeakMap(),
promise: Promise.resolve("done")
};
Functions are full-fledged object values:
const withFunctions = {
// Regular function
regularFunction: function() {
return "regular function";
},
// Arrow function
arrowFunction: () => "arrow function",
// Method (shorthand syntax)
method() {
return "object method";
},
// Async function
async asyncMethod() {
return "async method";
},
// Generator function
*generatorMethod() {
yield "generator";
}
};
DOM elements, classes and other special objects:
const special = {
// DOM element (in browser)
element: document.createElement('div'),
// Class constructor
MyClass: class MyClass {
constructor(name) {
this.name = name;
}
},
// Error object
error: new Error("something went wrong"),
// Buffer (in Node.js)
// buffer: Buffer.from('hello')
};
Key Type | Conversion | Example | Result |
---|---|---|---|
string | No change | "key" | "key" |
number | To string | 42 | "42" |
boolean | To string | true | "true" |
undefined | To string | undefined | "undefined" |
null | To string | null | "null" |
Symbol | No change | Symbol('id') | Symbol(id) |
Object | toString() | {} | "[object Object]" |
Array | join(’,‘) | [1,2] | "1,2" |
Keys have a specific order:
const obj = {
"3": "three",
"1": "one",
"2": "two",
"b": "bee",
"a": "ay"
};
console.log(Object.keys(obj)); // ["1", "2", "3", "b", "a"]
// Numeric keys are sorted, string keys follow insertion order
Symbol keys have special behavior:
const sym = Symbol('test');
const obj = {
regular: "regular key",
[sym]: "symbol key"
};
// Not displayed in standard methods
console.log(Object.keys(obj)); // ["regular"]
console.log(Object.values(obj)); // ["regular key"]
console.log(Object.entries(obj)); // [["regular", "regular key"]]
// Special methods for Symbol
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(test)]
console.log(Reflect.ownKeys(obj)); // ["regular", Symbol(test)]
// Array-like object indices
const arrayLike = {
0: "first",
1: "second",
2: "third",
length: 3
};
// Configuration by ID
const configs = {
1: { name: "dev", debug: true },
2: { name: "prod", debug: false },
999: { name: "test", debug: true }
};
const PRIVATE_ID = Symbol('privateId');
const PRIVATE_METHOD = Symbol('privateMethod');
class User {
constructor(name) {
this.name = name;
this[PRIVATE_ID] = Math.random();
}
[PRIVATE_METHOD]() {
return "private method";
}
getId() {
return this[PRIVATE_ID];
}
}
const user = new User("John");
console.log(user.name); // "John"
console.log(user.getId()); // 0.123456789
console.log(Object.keys(user)); // ["name"] (Symbol keys are hidden)
// Creating object with dynamic keys
function createObject(keyPrefix, values) {
const obj = {};
values.forEach((value, index) => {
obj[`${keyPrefix}_${index}`] = value;
});
return obj;
}
const result = createObject("item", ["a", "b", "c"]);
console.log(result); // { item_0: "a", item_1: "b", item_2: "c" }
const prefix = "user";
const id = 123;
const obj = {
// Computed keys
[`${prefix}_${id}`]: "value",
[prefix.toUpperCase()]: "UPPERCASE",
[`is${prefix.charAt(0).toUpperCase() + prefix.slice(1)}Active`]: true
};
console.log(obj);
// {
// user_123: "value",
// USER: "UPPERCASE",
// isUserActive: true
// }
// ❌ Problem: all objects become "[object Object]"
const map = {};
const key1 = { id: 1 };
const key2 = { id: 2 };
map[key1] = "value 1";
map[key2] = "value 2"; // Will overwrite the previous!
console.log(map); // { "[object Object]": "value 2" }
// ✅ Solution: use Map
const map2 = new Map();
map2.set(key1, "value 1");
map2.set(key2, "value 2");
console.log(map2.get(key1)); // "value 1"
console.log(map2.get(key2)); // "value 2"
const sym = Symbol('test');
const obj = {
regular: "regular",
[sym]: "symbol"
};
// ❌ Symbol keys are lost in JSON
const json = JSON.stringify(obj);
console.log(json); // {"regular":"regular"}
const restored = JSON.parse(json);
console.log(restored[sym]); // undefined
// ✅ Consider this during serialization
function serializeWithSymbols(obj) {
const symbols = Object.getOwnPropertySymbols(obj);
const result = { ...obj };
symbols.forEach(sym => {
result[sym.toString()] = obj[sym];
});
return result;
}
// ❌ Unexpected behavior
const obj = {};
obj[1] = "one";
obj["1"] = "string one"; // Will overwrite the previous!
console.log(obj); // { "1": "string one" }
// ✅ Be careful with key types
const safeObj = new Map();
safeObj.set(1, "number one");
safeObj.set("1", "string one");
console.log(safeObj.get(1)); // "number one"
console.log(safeObj.get("1")); // "string one"
Feature | Object | Map |
---|---|---|
Key types | string, Symbol | Any types |
Size | manual | .size |
Iteration | Object.keys() | for...of |
Performance | Optimized for records | Optimized for frequent additions/deletions |
JSON serialization | Yes | No (requires conversion) |
Prototype | Has (Object.prototype ) | Clean (Object.create(null) ) |
// When to use Object
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};
// When to use Map
const cache = new Map();
const element1 = document.getElementById('btn1');
const element2 = document.getElementById('btn2');
cache.set(element1, { clicks: 5 });
cache.set(element2, { clicks: 2 });
cache.set("global", { lastUpdate: Date.now() });
const obj = {
1: "a",
"1": "b",
true: "c",
"true": "d"
};
console.log(Object.keys(obj).length);
const sym = Symbol('key');
const obj = {
[sym]: "symbol value",
key: "string value"
};
console.log(Object.keys(obj));
console.log(Object.getOwnPropertySymbols(obj));
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const container = {};
container[obj1] = "first";
container[obj2] = "second";
console.log(Object.keys(container));
console.log(container[obj1]);
const obj = {
"2": "two",
"10": "ten",
"1": "one",
"b": "bee",
"a": "ay"
};
console.log(Object.keys(obj));
const obj = {
method1: function() { return "method1"; },
method2() { return "method2"; },
method3: () => "method3"
};
console.log(typeof obj.method1);
console.log(obj.method2());
console.log(obj.method3.name);
const prefix = "data";
const keys = ["name", "age"];
const obj = {};
keys.forEach((key, index) => {
obj[`${prefix}_${key}`] = `value_${index}`;
});
console.log(Object.keys(obj));
console.log(obj.data_name);
// ✅ Use Object for configurations and records
const userConfig = {
theme: "dark",
language: "en",
notifications: true
};
// ✅ Use Map for dynamic collections
const userSessions = new Map();
userSessions.set(userId1, { loginTime: Date.now() });
userSessions.set(userId2, { loginTime: Date.now() - 1000 });
// ✅ Symbol for metadata and private properties
const META_INFO = Symbol('metaInfo');
class Component {
constructor(props) {
Object.assign(this, props);
this[META_INFO] = {
created: Date.now(),
version: "1.0.0"
};
}
getMetaInfo() {
return this[META_INFO];
}
}
// ✅ Key validation
function createSafeObject(entries) {
const obj = {};
entries.forEach(([key, value]) => {
// Ensure key is a string
const safeKey = String(key);
if (safeKey && safeKey !== "undefined" && safeKey !== "null") {
obj[safeKey] = value;
}
});
return obj;
}
const safe = createSafeObject([
["name", "John"],
[null, "bad key"], // Will be skipped
["age", 30]
]);
Object keys:
Object values:
Practical conclusions:
Understanding object keys and values is the foundation of effective data handling in JavaScript!
Want more interview preparation articles? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪