Objects Flashcards
Are object keys sorted? If so how?
Integer keys are sorted by value. String keys are sorted by order of creation
Are objects passed by value or reference? What about primitives?
Primitives are passed by value. Objects are passed by reference
How to deeply copy an object?
If the object is a data object, i.e. no methods, then the built-in structuredClone() method will do the job. Otherwise, we would need to use some library like lodash
How does garbage collection work?
The key idea is ‘reachability’. First javascript has some roots, which are a base set of inherently reachable values. E.g. Execution context and global variables. Any other value is considered reachable only if it is reachable from some root.
For example:
1. let user = { … }; user = null
2. let user1 = { … }; let user2 = user1; user1 = null;
For (1) the object literal will be garbage collected since it is unreachable. For (2) since the object literal is reachable by user2, we cannot garbage collect it.
NOTE: Only incoming edges matter when determining if an object is reachable.
Then the basic algorithm is to mark and sweep nodes.
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
let family = marry({
name: “John”
}, {
name: “Ann”
});
delete family.father;
delete family.mother.husband;
Is there any garbage collection that will take place?
https://www.notion.so/wesleylim/Javascript-02c81e242b5e4da29c10541c66677a32?pvs=4#e6f19c05a3c145188405ef34942e9c2b
Yes. As seen in the diagram above, we have a ‘family’ variable that points to an object. The object is connected to two objects via ‘father’ and ‘mother’. The objects also point to each other via ‘man’ and ‘wife’. Thus (1) deletes the link between the root object and the ‘father’ object, while (2) deletes the link between the ‘mother’ object and the ‘father’ object, so now the ‘father’ object is unreachable.
Is the following valid code? If so, why?
function sayHi() {
alert( this.name );
}
Yes. The value of ‘this’ is evaluated during the run-time, depending on the context.
What is the value of ‘this’ in the following code?
function sayHi() {
alert( this.name );
}
sayHi();
If strict mode it will be undefined.
In non-strict mode it will the value of the global object, which is ‘window’ in a browser.
What is the value of ‘this’ in the following code?
let user = {
firstName: “Ilya”,
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi();
It is the ‘user’ object. Arrow functions do not have their own ‘this’ value, and instead take it from the surrounding lexical scope which it is defined.
What is the output of the following code, if any?
let group = {
title: “Our Group”,
students: [“John”, “Pete”, “Alice”],
showList() {
this.students.forEach(function(student) {
alert(this.title + ‘: ‘ + student);
});
}
};
group.showList();
There will be an error because the ‘this’ value in the callback that is passed to the .forEach() method, is undefined.
What are constructor (functions) and what happens when you call them with ‘new’?
Constructor functions are just regular functions that should only be called with ‘new’ and are named Uppercase by convention.
When functions are executed with new:
1. An empty object is created and assign to ‘this’
2. The function body executes, usually assigning properties to ‘this’
3. The value of ‘this’ is returned.
What are symbols, and what are they used for?
By specification, only two primitive types may serve as object property keys: strings or symbols.
A “symbol” represents a unique identifier.
let id1 = Symbol(“id”);
let id2 = Symbol(“id”);
alert(id1 == id2); // false
Symbols allow us to create “hidden” properties of an object, that no other part of code can accidentally access or overwrite. For example, when working with data objects from third party libraries, we might used a symbol instead of string to prevent potentially overriding properties defined by the library. They are skipped in a for-in loop.
If we need to define global symbols we can instead create them like so:
let globalSymbol = Symbol.for(‘id’); // created if not already created
let anotherSymbol = Symbol.for(‘id) // references created symol.
console.asset(globalSymbol=== anotherSymbol) // true
We can also get back the key used to create a symbol using the Symbol.keyFor(symbol) method.
How are objects casted to primitives(whether implicitly or explicitly)?
Different scenarios expect different types of values, called hints = ‘string’ | ‘number’ | ‘default’(rare case when operator is not sure what to expect)
To do the conversion, JavaScript tries to find and call three object methods:
1. Call objSymbol.toPrimitive – the method with the symbolic key Symbol.toPrimitive (system symbol), if such method exists,
2. Otherwise if hint is “string”, try calling obj.toString() or obj.valueOf(), whatever exists.
3. Otherwise if hint is “number” or “default”, try calling obj.valueOf() or obj.toString(), whatever exists.
let user = { name: "John", money: 1000, [Symbol.toPrimitive](hint) { alert(`hint: ${hint}`); return hint == "string" ? `{name: "${this.name}"}` : this.money; } }; // conversions demo: alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500
What are Property Flags
Objects have properties. Properties have flags that are used to configure them:
0. value (default, not configurable)
1. writable - if true -> value can be modified
2. enumerable - if true -> listed in loops
3. configurable - if true, the property can be deleted and flags can be modified.
When a property is created, all three flags are true.
How do you access property flags? How do you modify them?
Access:
Use the Object.getOwnPropertyDescriptor(obj, propName) method that returns the four flags for a property as an object.
If we need to get many properties’ descriptors in one function call we can use Object.getOwnPropertyDescriptors(obj)
Modify
We can use the Object.defineProperty(obj, propName, Partial<PropertyDescriptor>) method which defines the property on the object with the descriptor passed in. Importantly, flags not defined in the descriptor object passed in are default false.
If we need to define many properties' descriptors in one function call we can use Object.defineProperties(obj, Record<propName, descriptor>)</PropertyDescriptor>
Given that the schema of an object User looks like:
{
_id: 01ascnhyawdm7126b$21
name: ‘wesley’,
email: ‘wesley_lim@hive.gov.sg’,
}
Can you explain why it might be that Object.keys(user) only returns name and email?
This is likely because the property has its ‘enumerable’ flag set to false, so it does not appear in iterative functions like keys() and for loops.