Object Knowledge 2 Flashcards
What is logged to console?
let [apple, orange] = [1, 2];
console.log(apple);
console.log(orange);
1
2
What is alerted?
let [firstName, surname] = “John Smith”.split(‘ ‘);
alert(firstName);
alert(surname);
John
Smith
What is Destructuring assignment?
Does it mutate?
Destructuring assignment is a special syntax that allows us to “unpack” arrays or objects into a bunch of variables, as sometimes that’s more convenient.
No, it does not destruct. Should probably be called “unpacking syntax”.
What is alerted?
let [firstName, , title] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
alert( title );
// Consul
Unpack this array into separate variables
[1, 2]
let [apple, orange] = [1, 2];
console. log(apple); // 1
console. log(orange); // 2
Unpack only the first and third array items into variables:
[“Julius”, “Caesar”, “Consul”, “of the Roman Republic”]
let [firstName, , title] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
alert( title );
Destructuring assignment works with any iterable
t or f
let [one, two, three] = new Set([1, 2, 3]);
True
What is alerted?
let user = {}; [user.name, user.surname] = "John Smith".split(' ');
alert(user.name);
alert(user.surname);
// John // Smith
Do a for… of loop over an object (3 ways)
- Operate the for of loop on Object.entries array
- Operator the for of loop on Object.entries array and destruct the entries
- Add an iterator
1. With Object.entries let anObject = { one: 1, two: 2, three: 3 }
for (entries of Object.entries(anObject)) { console.log(entries[0]); console.log(entries[1]); } one 1 two 2 three 3
2. With Destructuring assignment and Object.entries let user = { name: "John", age: 30 };
// loop over keys-and-values for (let [key, value] of Object.entries(user)) { alert(`${key}:${value}`); // name:John, then age:30 }
3. let anObject = { 'one': 1, 'two': 4, 'three': 6435634, [Symbol.iterator]() { return { counter: 0, arrayObj: Object.entries(anObject), next() { if (this.counter > this.arrayObj.length - 1) { return {done: true}; } return {done: false, value: this.arrayObj[this.counter++][1]} } } } }
for (value of anObject) {
console.log(value);
}
Swap the variable values:
[guest, admin] let guest = "Jane"; let admin = "Pete"; (make guest = "Pete", admin = "Jane"
[guest, admin] = [admin, guest];
Can do more than 2 at a time.
What is alerted?
let [name1, name2] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
alert(name1);
alert(name2);
vs
let [name1, ,name2] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
alert(name1);
alert(name2);
// Julius // Caesar
// Julius // Consul
What happens with this destructuring assignment?
let [name1, name2, …rest] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
Adds the remaining items into an array alled rest
// rest is array of items, starting from the 3rd one
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
Deconstruct an array.
Assign the first 2 items to variables. Add the remaining items into an array
let [name1, name2, …rest] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];
What is alerted?
let [firstName, surname] = [];
alert(firstName);
alert(surname);
// undefined // undefined
setup default values for a destructuring assignment on an array
Setup a default prompt for restructuring assignment on an array
let [name = “Guest”, surname = “Anonymous”] = [“Julius”];
alert(name); // Julius (from array)
alert(surname); // Anonymous (default used)
Can be more complicated" // runs only prompt for surname let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (from array)
alert(surname); // whatever prompt gets
Destructure an object (make new variables from object properties in one step);
Does the order matter?
let {var1, var2} = {var1:…, var2:…}
The order doesn’t matter
But the variable names have to be the same because objects are not iterable.
Destructure an object and change the variable names
let options = { title: "Menu", width: 100, height: 200 };
// { sourceProperty: targetVariable } let {width: w, height: h, title} = options;
// width -> w // height -> h // title -> title
alert(title); // Menu
alert(w); // 100
alert(h); // 200
Have default values when destructuring an object
Have default values when destructuring an array
let options = { title: "Menu" };
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
let anArray = [1, 2];
let [ appl, pea, purp = 3] = anArray;
console.log(purp);
What happens with: let options = { title: "Menu" };
let {width = prompt(“width?”), title = prompt(“title?”)} = options;
alert(title);
alert(width);
Only the prompt for width comes though
// Menu // (whatever the result of prompt is)
Have a default value and change the variable name when destructuring an object
let options = { title: "Menu" };
let {width: w = 100, height: h = 200, title} = options;
alert(title); // Menu
alert(w); // 100
alert(h); // 200
Destructure remaining object values into an array
let options = { title: "Menu", height: 200, width: 100 };
// title = property named title // rest = object with the rest of properties let {title, ...rest} = options;
// now title=”Menu”, rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
Destructure an object or array into existing variables? (no ‘let’ expression)
let title, width, height;
// okay now ({title, width, height} = {title: "Menu", width: 200, height: 100});
alert( title ); // Menu
If you don’t put it in (), it assumes it’s a code block and breaks.
Destructure a nested array or object
let options = { size: { width: 100, height: 200 }, items: ["Cake", "Donut"], extra: true };
// destructuring assignment split in multiple lines for clarity
let {
size: { // put size here
width,
height
},
items: [item1, item2], // assign items here
title = “Menu” // not present in the object (default value is used)
} = options;
alert(title); // Menu alert(width); // 100 alert(height); // 200 alert(item1); // Cake alert(item2); // Donut
What is a great benefit of using destructured objects as function parameters? (2 things)
Don’t have to remember the order of inputs into a function
Don’t have to input ‘undefined’ when you want the default value
let options = {
title: “My menu”,
items: [“Item1”, “Item2”]
};
// ...and it immediately expands it to variables function showMenu({title = "Untitled", width = 200, height = 100, items = []}) { // title, items – taken from options, // width, height – defaults used alert( `${title} ${width} ${height}` ); // My Menu 200 100 alert( items ); // Item1, Item2 }
showMenu(options);
Create a function that destructures an object for it’s arguments
let options = {
title: “My menu”,
items: [“Item1”, “Item2”]
};
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) { alert( `${title} ${width} ${height}` ); // My Menu 200 100 alert( items ); // Item1, Item2 }
showMenu(options);
- Destruct a nested object
2. Create a function that destructures a nested object for it’s arguments
1. let myObj = { one: { two: 22, }, }
let {one: {two: apple}} = myObj;
console.log(apple);
2. let options = { title: "My menu", items: ["Item1", "Item2"] };
function showMenu({ title = "Untitled", width: w = 100, // width goes to w height: h = 200, // height goes to h items: [item1, item2] // items first element goes to item1, second to item2 }) { alert( `${title} ${w} ${h}` ); // My Menu 100 200 alert( item1 ); // Item1 alert( item2 ); // Item2 }
showMenu(options);
function({ incomingProperty: varName = defaultValue ... })
When a function expects to destructure an object for its arguments, how can you quickly set all default variables?
showMenu( {} );
Usually this would give an error to a function expecting to destruct an object for it’s arguments
showMenu({}); // ok, all values are default
showMenu(); // this would give an error
How can you get around this?
Make { } the default argument when nothign is given
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) { alert( `${title} ${width} ${height}` ); }
showMenu(); // Menu 100 200
There is a salaries object:
let salaries = { "John": 100, "Pete": 300, "Mary": 250 }; Create the function topSalary(salaries) that returns the name of the top-paid person.
If salaries is empty, it should return null.
If there are multiple top-paid persons, return any of them.
Hint Use Object.entries and destructuring to iterate over key/value pairs. . . . . . . . . . . . . . . . . . . . . . function topSalary(salaries) {
let maxSalary = 0; let maxName = null;
for(const [name, salary] of Object.entries(salaries)) { if (maxSalary < salary) { maxSalary = salary; maxName = name; } }
return maxName;
}
What is logged to console?
let anObject = { apple: 1, one: { two: 2, three: 3 } }
let { apple, one: { three, }, ...rest } = anObject;
console.log(rest);
undefined
the … operator only collects the variables within it’s level of nesting.
Have to do this: let { apple, one: { three, ...rest },
} = anObject;
Create a linked list (a list of a chain of object)
let list = { value: 1, next: { value: 2, next: { value: 3, next: { value: 4, next: null } } } };
OR
let list = { value: 1 };
list. next = { value: 2 };
list. next.next = { value: 3 };
list. next.next.next = { value: 4 };
list. next.next.next.next = null;
Split a list and join it back together
let list = { value: 1 };
list. next = { value: 2 };
list. next.next = { value: 3 };
list. next.next.next = { value: 4 };
list. next.next.next.next = null;
Split: let secondList = list.next.next; list.next.next = null;
Join:
list.next.next = secondList;
Prepend a new value to a list (prepend means add to beginning)
Append a new value to a list
let list = { value: 1 };
list. next = { value: 2 };
list. next.next = { value: 3 };
list. next.next.next = { value: 4 };
list. next.next.next.next = null;
list = { value: “new item”, next: list };
list.next.next.next.next= {value: 5, next: null}
Remove an item from the middle of a list
list.next = list.next.next;
What is prototypal inheritance?
When we read a property from an object, and it’s missing, JavaScript automatically takes it from the prototype.
Set an object as the prototype of another (old way)
let animal = { eats: true }; let rabbit = { jumps: true };
rabbit.\_\_proto\_\_ = animal; (double underscore) // sets rabbit.[[Prototype]] = animal. If a property isn't found on rabbit, it looks on animal
What happens with this code?
let animal = { eats: true, walk() { alert("Animal walk"); } };
let rabbit = { jumps: true, \_\_proto\_\_: animal };
let longEar = {
earLength: 10,
__proto__: rabbit
};
longEar.eats;
returns true
What can __proto__ be?
Can it go in circles?
Can there be multiple prototypes?
null or objects
No
No but can have a chain
What happens with this code?
let animal = { eats: true, walk() { alert(' Walk Walk '); } };
let rabbit = { \_\_proto\_\_: animal };
rabbit.walk = function() {
alert(“Rabbit! Bounce-bounce!”);
};
rabbit.walk();
// Rabbit! Bounce-bounce!
prototype is for reading, not writing.
What happens with this code?
let animal = { eats: true };
let rabbit = { jumps: true, \_\_proto\_\_: animal };
alert(Object.keys(rabbit));
for(let prop in rabbit) alert(prop);
// Object.keys only returns own keys // jumps
// for..in loops over both own and inherited keys // jumps, then eats
We have rabbit inheriting from animal.
If we call rabbit.eat(), which object receives the full property: animal or rabbit?
let animal = { eat() { this.full = true; } };
let rabbit = { \_\_proto\_\_: animal };
rabbit.eat();
The answer: rabbit.
That’s because this is an object before the dot, so rabbit.eat() modifies rabbit.
Property lookup and execution are two different things.
The method rabbit.eat is first found in the prototype, then executed with this=rabbit.
We have two hamsters: speedy and lazy inheriting from the general hamster object.
When we feed one of them, the other one is also full. Why? How can we fix it?
let hamster = { stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = { \_\_proto\_\_: hamster };
let lazy = { \_\_proto\_\_: hamster };
// This one found the food
speedy.eat(“apple”);
alert( speedy.stomach ); // apple
// This one also has it, why? fix please. alert( lazy.stomach ); // apple
It’s pushing to an object. It’s not reassigning “stomach”. So the all share the same stomach object.
Best way to fix: // assign to this.stomach instead of this.stomach.push
let hamster = { stomach: [],
eat(food) {
this.stomach = [food];
}
};
Make a constructor function create objects with a specific prototype.
let animal = { eats: true };
function Rabbit(name) { this.name = name; }
Rabbit.prototype = animal;
let rabbit = new Rabbit(“White Rabbit”); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
prototype on Rabbit is a regular property.
__proto__ is old and depracated, how do you set prototypes for the modern age (2022)
- Create a new object with a set prototype
- Set a prototype of an existing object
- return the object’s prototype (as null or an object)
Object.create(proto, [descriptors])
Object.setPrototypeOf(obj, proto)
Object.getPrototypeOf(obj)
Why put functions on the prototype rather than the constructor?
So functions aren’t duplicated on each object.
What happens with this code?
let il = { obj: {two: 2, three:3, four:4} };
let {obj: {two, …apple}} = il;
console.log(obj);
error obj not defined How do you fix it . .. . . . . . . . . . . . To fix it, do it in 2 steps: let {obj} = il; let {obj: {two, ...apple}} = il;