Data types Flashcards

1
Q

Which is heavier- objecs or primitives ?

A

Objects are “heavier” than primitives. They require additional resources to support the internal machinery.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Are functions in JS objects ?

A

Yes

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Are primitives like string, number objects ? because we can use methods like str.toUpperCase() ?

A

Primitives are not objects

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

let str = “Hello”;

alert( str.toUpperCase() ); // HELLO

As we know primitives are not objects still we are able to call toUpperCase() on str. How this code works ?

A

Simple, right? Here’s what actually happens in str.toUpperCase():

The string str is a primitive. So in the moment of accessing its property, a special object is created that knows the value of the string, and has useful methods, like toUpperCase().
That method runs and returns a new string (shown by alert).
The special object is destroyed, leaving the primitive str alone.
So primitives can provide methods, but they still remain lightweight.

The JavaScript engine highly optimizes this process. It may even skip the creation of the extra object at all. But it must still adhere to the specification and behave as if it creates one.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Consider the following code:

let str = “Hello”;

str.test = 5;

alert(str.test);
How do you think, will it work? What will be shown?

A

let str = “Hello”;

str.test = 5; // (*)

alert(str.test);
Depending on whether you have use strict or not, the result may be:

undefined (no strict mode)
An error (strict mode).
Why? Let’s replay what’s happening at line (*):

When a property of str is accessed, a “wrapper object” is created.
In strict mode, writing into it is an error.
Otherwise, the operation with the property is carried on, the object gets the test property, but after that the “wrapper object” disappears, so in the last line str has no trace of the property.
This example clearly shows that primitives are not objects.

They can’t store additional data.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Do null/undefined also have some methods that we can call ?

A

The special primitives null and undefined are exceptions. They have no corresponding “wrapper objects” and provide no methods. In a sense, they are “the most primitive”.

An attempt to access a property of such value would give the error:

alert(null.test); // error

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

What are the two types of numbers in JS ?

A

In modern JavaScript, there are two types of numbers:

Regular numbers in JavaScript are stored in 64-bit format IEEE-754, also known as “double precision floating point numbers”. These are numbers that we’re using most of the time, and we’ll talk about them in this chapter.

BigInt numbers, to represent integers of arbitrary length. They are sometimes needed, because a regular number can’t safely exceed 2^53 or be less than -2^53. As bigints are used in few special areas, we devote them a special chapter BigInt.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What are the two ways of writing 1 billion ?

A
let billion = 1000000000;
We also can use underscore _ as the separator:

let billion = 1_000_000_000;

Here the underscore _ plays the role of the “syntactic sugar”, it makes the number more readable. The JavaScript engine simply ignores _ between digits, so it’s exactly the same one billion as above.

In real life though, we try to avoid writing long sequences of zeroes. We’re too lazy for that. We’ll try to write something like “1bn” for a billion or “7.3bn” for 7 billion 300 million. The same is true for most large numbers.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

How to use ‘e’ for writing large numbers ?

A

let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes

alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
In other words, e multiplies the number by 1 with the given zeroes count.

1e3 === 1 * 1000; // e3 means *1000
1.23e6 === 1.23 * 1000000; // e6 means *1000000

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

How to write very small numbers in JS ?

A

Now let’s write something very small. Say, 1 microsecond (one millionth of a second):

let mсs = 0.000001;
Just like before, using “e” can help. If we’d like to avoid writing the zeroes explicitly, we could write the same as:

let mcs = 1e-6; // six zeroes to the left from 1
If we count the zeroes in 0.000001, there are 6 of them. So naturally it’s 1e-6.

In other words, a negative number after “e” means a division by 1 with the given number of zeroes:

// -3 divides by 1 with 3 zeroes
1e-3 === 1 / 1000; // 0.001
// -6 divides by 1 with 6 zeroes
1.23e-6 === 1.23 / 1000000; // 0.00000123
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

How to write Hex, binary and octal numbers in JS ?

A

Hexadecimal numbers are widely used in JavaScript to represent colors, encode characters, and for many other things. So naturally, there exists a shorter way to write them: 0x and then the number.

For instance:

alert( 0xff ); // 255
alert( 0xFF ); // 255 (the same, case doesn’t matter)
Binary and octal numeral systems are rarely used, but also supported using the 0b and 0o prefixes:

let a = 0b11111111; // binary form of 255
let b = 0o377; // octal form of 255

alert( a == b ); // true, the same number 255 at both sides
There are only 3 numeral systems with such support. For other numeral systems, we should use the function parseInt (which we will see later in this chapter).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

How does toString(base) works ?

A

The method num.toString(base) returns a string representation of num in the numeral system with the given base.

For example:

let num = 255;

alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
The base can vary from 2 to 36. By default it’s 10.

Common use cases for this are:

base=16 is used for hex colors, character encodings etc, digits can be 0..9 or A..F.

base=2 is mostly for debugging bitwise operations, digits can be 0 or 1.

base=36 is the maximum, digits can be 0..9 or A..Z. The whole latin alphabet is used to represent a number. A funny, but useful case for 36 is when we need to turn a long numeric identifier into something shorter, for example to make a short url. Can simply represent it in the numeral system with base 36:

alert( 123456..toString(36) ); // 2n9c

Please note that two dots in 123456..toString(36) is not a typo. If we want to call a method directly on a number, like toString in the example above, then we need to place two dots .. after it.

If we placed a single dot: 123456.toString(36), then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method.

Also could write (123456).toString(36).

Round

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

How we do rounding in JS. List four methods of doing so as - Math.floor, Math.ciel, Math.round, Math.trunc ?

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

How to round the digits to nth place after decimal ?

A

The method toFixed(n) rounds the number to n digits after the point and returns a string representation of the result.

let num = 12.34;
alert( num.toFixed(1) ); // "12.3"
This rounds up or down to the nearest value, similar to Math.round:
let num = 12.36;
alert( num.toFixed(1) ); // "12.4"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Advantages of backticks ?

A

allow us to embed any expression into the string, by wrapping it in ${…}:

function sum(a, b) {
  return a + b;
}

alert(1 + 2 = ${sum(1, 2)}.); // 1 + 2 = 3.
Another advantage of using backticks is that they allow a string to span multiple lines:

let guestList = `Guests:
 * John
 * Pete
 * Mary
`;

alert(guestList); // a list of guests, multiple lines

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Are strings immutable in JS ?

A

Yes

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

How to change the case of strings ?

A

Methods toLowerCase() and toUpperCase() change the case:

alert( ‘Interface’.toUpperCase() ); // INTERFACE
alert( ‘Interface’.toLowerCase() ); // interface
Or, if we want a single character lowercased:

alert( ‘Interface’[0].toLowerCase() ); // ‘i’

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

How the str.indexOf() works ?

A

The first method is str.indexOf(substr, pos).

It looks for the substr in str, starting from the given position pos, and returns the position where the match was found or -1 if nothing can be found.

For instance:

let str = ‘Widget with id’;

alert( str.indexOf(‘Widget’) ); // 0, because ‘Widget’ is found at the beginning
alert( str.indexOf(‘widget’) ); // -1, not found, the search is case-sensitive

alert( str.indexOf(“id”) ); // 1, “id” is found at the position 1 (..idget with id)
The optional second parameter allows us to start searching from a given position.

For instance, the first occurrence of “id” is at position 1. To look for the next occurrence, let’s start the search from position 2:

let str = ‘Widget with id’;

alert( str.indexOf(‘id’, 2) ) // 12

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

How the includes() works ?

A

The more modern method str.includes(substr, pos) returns true/false depending on whether str contains substr within.

It’s the right choice if we need to test for the match, but don’t need its position:

alert( “Widget with id”.includes(“Widget”) ); // true

alert( “Hello”.includes(“Bye”) ); // false
The optional second argument of str.includes is the position to start searching from:

alert( “Widget”.includes(“id”) ); // true
alert( “Widget”.includes(“id”, 3) ); // false, from position 3 there is no “id”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

How startsWith() and endWith() works ?

A

The methods str.startsWith and str.endsWith do exactly what they say:

alert( “Widget”.startsWith(“Wid”) ); // true, “Widget” starts with “Wid”
alert( “Widget”.endsWith(“get”) ); // true, “Widget” ends with “get”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

let str = “stringify”;
alert( str.slice(0, 5) );
alert( str.slice(0, 1) );

How it works ?

A

let str = “stringify”;
alert( str.slice(0, 5) ); // ‘strin’, the substring from 0 to 5 (not including 5)
alert( str.slice(0, 1) ); // ‘s’, from 0 to 1, but not including 1, so only character at 0
If there is n

str.slice(start [, end])
Returns the part of the string from start to (but not including) end.

F

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q
let str = "stringify";
alert( str.slice(2) ); // Output ?
A

let str = “stringify”;
alert( str.slice(2) ); // ‘ringify’, from the 2nd position till the end
If there is no second argument, then slice goes till the end of the string:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

let str = “stringify”;

alert( str.slice(-4, -1) ); // Output ?

A

Negative values for start/end are also possible. They mean the position is counted from the string end:

let str = “stringify”;

// start at the 4th position from the right, end at the 1st from the right
alert( str.slice(-4, -1) ); // 'gif'
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

let str = “stringify”;

alert( str.substring(2, 6) );
alert( str.substring(6, 2) );

alert( str.slice(2, 6) );
alert( str.slice(6, 2) );

A

str.substring(start [, end])
Returns the part of the string between start and end.

This is almost the same as slice, but it allows start to be greater than end.

For instance:

let str = “stringify”;

// these are same for substring
alert( str.substring(2, 6) ); // “ring”
alert( str.substring(6, 2) ); // “ring”

// …but not for slice:
alert( str.slice(2, 6) ); // “ring” (the same)
alert( str.slice(6, 2) ); // “” (an empty string)
Negative arguments are (unlike slice) not supported, they are treated as 0.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q
let str = "stringify";
alert( str.substr(2, 4) ); // 'ring', from the 2nd position get 4 characters
let str = "stringify";
alert( str.substr(-4, 2) ); // 'gi', from the 4th position get 2 characters
A

str.substr(start [, length])
Returns the part of the string from start, with the given length.

In contrast with the previous methods, this one allows us to specify the length instead of the ending position:

let str = "stringify";
alert( str.substr(2, 4) ); // 'ring', from the 2nd position get 4 characters
The first argument may be negative, to count from the end:
let str = "stringify";
alert( str.substr(-4, 2) ); // 'gi', from the 4th position get 2 characters
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

Can we store objects and functions in a array ?

A

An array can store elements of any type.

For instance:

// mix of values
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];
// get the object at index 1 and then show its name
alert( arr[1].name ); // John
// get the function at index 3 and run it
arr[3](); // hello
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

What are two methods that work at the end of the array for insertion and removal of elements ?

A

Methods that work with the end of the array:

pop
Extracts the last element of the array and returns it:

let fruits = [“Apple”, “Orange”, “Pear”];

alert( fruits.pop() ); // remove “Pear” and alert it

alert( fruits ); // Apple, Orange
push
Append the element to the end of the array:

let fruits = [“Apple”, “Orange”];

fruits.push(“Pear”);

alert( fruits ); // Apple, Orange, Pear
The call fruits.push(…) is equal to fruits[fruits.length] = ….

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

How shift method works ?

A

shift
Extracts the first element of the array and returns it:

let fruits = [“Apple”, “Orange”, “Pear”];

alert( fruits.shift() ); // remove Apple and alert it

alert( fruits ); // Orange, Pear

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

How unshift method works ?

A

unshift
Add the element to the beginning of the array:

let fruits = [“Orange”, “Pear”];

fruits.unshift(‘Apple’);

alert( fruits ); // Apple, Orange, Pear

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q

Can we add multiple elements at once using push and unshift methods ?

A

Methods push and unshift can add multiple elements at once:

let fruits = [“Apple”];

fruits. push(“Orange”, “Peach”);
fruits. unshift(“Pineapple”, “Lemon”);

// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q

Is array an object ?

A

An array is a special kind of object. The square brackets used to access a property arr[0] actually come from the object syntax. That’s essentially the same as obj[key], where arr is the object, while numbers are used as keys.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q

Since, arrays are objects, Can we treat it like a object and do all the things that we do with objects ?

A

NO. We should use arrays as intended. JS engine will know if we treat an array as an object and will stop the operations.

what makes arrays really special is their internal representation. The engine tries to store its elements in the contiguous memory area, one after another, just as depicted on the illustrations in this chapter, and there are other optimizations as well, to make arrays work really fast.

But they all break if we quit working with an array as with an “ordered collection” and start working with it as if it were a regular object.

For instance, technically we can do this:

let fruits = []; // make an array

fruits[99999] = 5; // assign a property with the index far greater than its length

fruits.age = 25; // create a property with an arbitrary name
That’s possible, because arrays are objects at their base. We can add any properties to them.

But the engine will see that we’re working with the array as with a regular object. Array-specific optimizations are not suited for such cases and will be turned off, their benefits disappear.

The ways to misuse an array:

Add a non-numeric property like arr.test = 5.
Make holes, like: add arr[0] and then arr[1000] (and nothing between them).
Fill the array in the reverse order, like arr[1000], arr[999] and so on.
Please think of arrays as special structures to work with the ordered data. They provide special methods for that. Arrays are carefully tuned inside JavaScript engines to work with contiguous ordered data, please use them this way. And if you need arbitrary keys, chances are high that you actually require a regular object {}.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
33
Q

How for..of loop works on arrays ?

A

But for arrays there is another form of loop, for..of:

let fruits = [“Apple”, “Orange”, “Plum”];

// iterates over array elements
for (let fruit of fruits) {
  alert( fruit );
}
The for..of doesn’t give access to the number of the current element, just its value, but in most cases that’s enough. And it’s shorter.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
34
Q

Can we use for..in loop on arrays ? since, it is like an object.

A

Technically, because arrays are objects, it is also possible to use for..in:

let arr = [“Apple”, “Orange”, “Pear”];

for (let key in arr) {
alert( arr[key] ); // Apple, Orange, Pear
}
But that’s actually a bad idea. There are potential problems with it:

The loop for..in iterates over all properties, not only the numeric ones.

There are so-called “array-like” objects in the browser and in other environments, that look like arrays. That is, they have length and indexes properties, but they may also have other non-numeric properties and methods, which we usually don’t need. The for..in loop will list them though. So if we need to work with array-like objects, then these “extra” properties can become a problem.

The for..in loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it’s still very fast. The speedup may only matter in bottlenecks. But still we should be aware of the difference.

Generally, we shouldn’t use for..in for arrays

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
35
Q
let fruits = [];
fruits[123] = "Apple";

alert( fruits.length ); // Output ?

A

The length property automatically updates when we modify the array. To be precise, it is actually not the count of values in the array, but the greatest numeric index plus one.

For instance, a single element with a large index gives a big length:

let fruits = [];
fruits[123] = "Apple";

alert( fruits.length ); // 124

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
36
Q

length property is writable. what does that mean ?

A

Another interesting thing about the length property is that it’s writable.

If we increase it manually, nothing interesting happens. But if we decrease it, the array is truncated. The process is irreversible, here’s the example:

let arr = [1, 2, 3, 4, 5];

arr.length = 2; // truncate to 2 elements
alert( arr ); // [1, 2]

arr.length = 5; // return length back
alert( arr[3] ); // undefined: the values do not return
So, the simplest way to clear the array is: arr.length = 0;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
37
Q

let arr = new Array(2); // will it create an array of [2] ?

alert( arr[0] ); // undefined! no elements.

alert( arr.length ); // length 2

Why undefined is coming in the above output ?

A

There is one more syntax to create an array:

let arr = new Array("Apple", "Pear", "etc");
It’s rarely used, because square brackets [] are shorter. Also there’s a tricky feature with it.

If new Array is called with a single argument which is a number, then it creates an array without items, but with the given length.

Let’s see how one can shoot themself in the foot:

let arr = new Array(2); // will it create an array of [2] ?

alert( arr[0] ); // undefined! no elements.

alert( arr.length ); // length 2
To avoid such surprises, we usually use square brackets, unless we really know what we’re doing.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
38
Q

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === ‘1,2,3’ ); // Output ?

A

Arrays have their own implementation of toString method that returns a comma-separated list of elements.

For instance:

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === ‘1,2,3’ ); // true
Also, let’s try this:

alert( [] + 1 ); // “1”
alert( [1] + 1 ); // “11”
alert( [1,2] + 1 ); // “1,21”
Arrays do not have Symbol.toPrimitive, neither a viable valueOf, they implement only toString conversion, so here [] becomes an empty string, [1] becomes “1” and [1,2] becomes “1,2”.

When the binary plus “+” operator adds something to a string, it converts it to a string as well, so the next step looks like this:

alert( “” + 1 ); // “1”
alert( “1” + 1 ); // “11”
alert( “1,2” + 1 ); // “1,21”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
39
Q

Should we compare arrays with == or < or > ?

A

To compare arrays, don’t use the == operator (as well as >, < and others), as they have no special treatment for arrays. They handle them as any objects, and it’s not what we usually want.

Instead you can use for..of loop to compare arrays item-by-item.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
40
Q

let arr = [“I”, “go”, “home”];

delete arr[1]; // remove “go”

alert( arr[1] ); // Output ?

// now arr = ["I",  , "home"];
alert( arr.length ); // Output ?
A

How to delete an element from the array?

The arrays are objects, so we can try to use delete:

let arr = [“I”, “go”, “home”];

delete arr[1]; // remove “go”

alert( arr[1] ); // undefined

// now arr = [“I”, , “home”];
alert( arr.length ); // 3
The element was removed, but the array still has 3 elements, we can see that arr.length == 3.

That’s natural, because delete obj.key removes a value by the key. It’s all it does. Fine for objects. But for arrays we usually want the rest of elements to shift and occupy the freed place. We expect to have a shorter array now.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
41
Q

let arr = [“I”, “study”, “JavaScript”];

arr.splice(1, 1);

alert( arr ); // Output ?

A

let arr = [“I”, “study”, “JavaScript”];

arr.splice(1, 1); // from index 1 remove 1 element

alert( arr ); // [“I”, “JavaScript”]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
42
Q

let arr = [“I”, “study”, “JavaScript”, “right”, “now”];

arr.splice(0, 3, “Let’s”, “dance”);

alert( arr ) // Output ?

A

let arr = [“I”, “study”, “JavaScript”, “right”, “now”];

// remove 3 first elements and replace them with another
arr.splice(0, 3, "Let's", "dance");

alert( arr ) // now [“Let’s”, “dance”, “right”, “now”]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
43
Q

let arr = [“I”, “study”, “JavaScript”, “right”, “now”];

let removed = arr.splice(0, 2);

alert( removed ); // Output ?

A

let arr = [“I”, “study”, “JavaScript”, “right”, “now”];

// remove 2 first elements
let removed = arr.splice(0, 2);

alert( removed ); // “I”, “study”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
44
Q

let arr = [“I”, “study”, “JavaScript”];

arr.splice(2, 0, “complex”, “language”);

alert( arr ); // Output ?

A

let arr = [“I”, “study”, “JavaScript”];

// from index 2
// delete 0
// then insert "complex" and "language"
arr.splice(2, 0, "complex", "language");

alert( arr ); // “I”, “study”, “complex”, “language”, “JavaScript”

The splice method is also able to insert the elements without any removals. For that we need to set deleteCount to 0:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
45
Q

let arr = [1, 2, 5];

arr.splice(-1, 0, 3, 4);

alert( arr ); // Output ?

A

let arr = [1, 2, 5];

// from index -1 (one step from the end)
// delete 0 elements,
// then insert 3 and 4
arr.splice(-1, 0, 3, 4);

alert( arr ); // 1,2,3,4,5
Here and in other array methods, negative indexes are allowed. They specify the position from the end of the array, like here:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
46
Q

Write the syntax for slice method

A

The method arr.slice is much simpler than similar-looking arr.splice.

The syntax is:

arr.slice([start], [end])
It returns a new array copying to it all items from index start to end (not including end). Both start and end can be negative, in that case position from array end is assumed.

It’s similar to a string method str.slice, but instead of substrings it makes subarrays.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
47
Q

let arr = [“t”, “e”, “s”, “t”];

alert( arr.slice(1, 3) );

alert( arr.slice(-2) ); //Output ?

A

let arr = [“t”, “e”, “s”, “t”];

alert( arr.slice(1, 3) ); // e,s (copy from 1 to 3)

alert( arr.slice(-2) ); // s,t (copy from -2 till the end)
We can also call it without arguments: arr.slice() creates a copy of arr. That’s often used to obtain a copy for further transformations that should not affect the original array.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
48
Q

Write the syntax for arr.concat method

A

The method arr.concat creates a new array that includes values from other arrays and additional items.

The syntax is:

arr.concat(arg1, arg2…)
It accepts any number of arguments – either arrays or values.

The result is a new array containing items from arr, then arg1, arg2 etc.

If an argument argN is an array, then all its elements are copied. Otherwise, the argument itself is copied.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
49
Q

let arr = [1, 2];

alert( arr.concat([3, 4]) ); // 1,2,3,4

alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

A

let arr = [1, 2];

// create an array from: arr and [3,4]
alert( arr.concat([3, 4]) ); // 1,2,3,4
// create an array from: arr and [3,4] and [5,6]
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

// create an array from: arr and [3,4], then add values 5 and 6
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
Normally, it

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
50
Q

let arr = [1, 2];

let arrayLike = {
  0: "something",
  length: 1
};

alert( arr.concat(arrayLike) ); //Output ?

A

let arr = [1, 2];

let arrayLike = {
  0: "something",
  length: 1
};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]
Normally, it only copies elements from arrays. Other objects, even if they look like arrays, are added as a whole:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
51
Q

let arr = [1, 2];

let arrayLike = {
  0: "something",
  1: "else",
  [Symbol.isConcatSpreadable]: true,
  length: 2
};

alert( arr.concat(arrayLike) ); // Output ?

A

let arr = [1, 2];

let arrayLike = {
  0: "something",
  1: "else",
  [Symbol.isConcatSpreadable]: true,
  length: 2
};

alert( arr.concat(arrayLike) ); // 1,2,something,else
if an array-like object has a special Symbol.isConcatSpreadable property, then it’s treated as an array by concat: its elements are added instead:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
52
Q

[“Bilbo”, “Gandalf”, “Nazgul”].forEach(alert);

How this works ?

A

alert popup will be shown for each element in the array

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
53
Q

[“Bilbo”, “Gandalf”, “Nazgul”].forEach((item, index, array) => {
alert(${item} is at index ${index} in ${array});
});

Output ?

A

It will be like Bilbo is at index 0 in Bilbo, Gandalf, Nazgul

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
54
Q

How arr.indexOf, arr.lastIndexOf and arr.includes works ?

A

The methods arr.indexOf, arr.lastIndexOf and arr.includes have the same syntax and do essentially the same as their string counterparts, but operate on items instead of characters:

arr. indexOf(item, from) – looks for item starting from index from, and returns the index where it was found, otherwise -1.
arr. lastIndexOf(item, from) – same, but looks for from right to left.
arr. includes(item, from) – looks for item starting from index from, returns true if found.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
55
Q

let arr = [1, 0, false];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

alert( arr.includes(1) ); // Output ?

A

let arr = [1, 0, false];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

alert( arr.includes(1) ); // true

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
56
Q

arr.indexOf, arr.lastIndexOf and arr.includes uses ===. What does that mean ?

A

Note that the methods use === comparison. So, if we look for false, it finds exactly false and not the zero.

If we want to check for inclusion, and don’t want to know the exact index, then arr.includes is preferred.

Also, a very minor difference of includes is that it correctly handles NaN, unlike indexOf/lastIndexOf:

const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (should be 0, but === equality doesn't work for NaN)
alert( arr.includes(NaN) );// true (correct)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
57
Q

Imagine we have an array of objects. How do we find an object with the specific condition?

A

Here the arr.find(fn) method comes in handy.

The syntax is:

let result = arr.find(function(item, index, array) {
  // if true is returned, item is returned and iteration is stopped
  // for falsy scenario returns undefined
});
The function is called for elements of the array, one after another:

item is the element.
index is its index.
array is the array itself.
If it returns true, the search is stopped, the item is returned. If nothing found, undefined is returned.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
58
Q
let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];

let user = users.find(item => item.id == 1);

alert(user.name); // Output ?

A

Output: John
In real life arrays of objects is a common thing, so the find method is very useful.

Note that in the example we provide to find the function item => item.id == 1 with one argument. That’s typical, other arguments of this function are rarely used.

The arr.findIndex method is essentially the same, but it returns the index where the element was found instead of the element itself and -1 is returned when nothing is found.

59
Q

What is the drawback of find() method ?

A

The find method looks for a single (first) element that makes the function return true. If many elements have the same key, they it won’t return them

60
Q

How filter(fn) method works ?

A

The find method looks for a single (first) element that makes the function return true.

If there may be many, we can use arr.filter(fn).

The syntax is similar to find, but filter returns an array of all matching elements:

let results = arr.filter(function(item, index, array) {
  // if true item is pushed to results and the iteration continues
  // returns empty array if nothing found
});
61
Q
let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];
// returns array of the first two users
let someUsers = users.filter(item => item.id < 3);

alert(someUsers.length); // Output ?

A
let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];
// returns array of the first two users
let someUsers = users.filter(item => item.id < 3);

alert(someUsers.length); // 2
The length is 2 because, someUsers is referencing a new array that was returned by the filter method

62
Q
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // Output ?
A
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
The arr.map method is one of the most useful and often used.

It calls the function for each element of the array and returns the array of results.

The syntax is:

let result = arr.map(function(item, index, array) {
  // returns the new value instead of item
});
63
Q

let arr = [ 1, 2, 15 ];

// the method reorders the content of arr
arr.sort();

alert( arr ); // Output ?

A

let arr = [ 1, 2, 15 ];

// the method reorders the content of arr
arr.sort();

alert( arr ); // 1, 15, 2

The call to arr.sort() sorts the array in place, changing its element order.

It also returns the sorted array, but the returned value is usually ignored, as arr itself is modified.

Did you notice anything strange in the outcome?

The order became 1, 15, 2. Incorrect. But why?

The items are sorted as strings by default.

Literally, all elements are converted to strings for comparisons. For strings, lexicographic ordering is applied and indeed “2” > “15”.

To use our own sorting order, we need to supply a function as the argument of arr.sort().

The function should compare two arbitrary values and return:

function compare(a, b) {
  if (a > b) return 1; // if the first value is greater than the second
  if (a == b) return 0; // if values are equal
  if (a < b) return -1; // if the first value is less than the second
}
For instance, to sort as numbers:
function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

let arr = [ 1, 2, 15 ];

arr.sort(compareNumeric);

alert(arr); // 1, 2, 15
Now it works as intended.

64
Q

Write a shorthand for sort() method

A

arr.sort( (a, b) => a - b );

65
Q

let countries = [‘Österreich’, ‘Andorra’, ‘Vietnam’];

alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Output ?

alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Output ?

A

Remember strings comparison algorithm? It compares letters by their codes by default.

For many alphabets, it’s better to use str.localeCompare method to correctly sort letters, such as Ö.

For example, let’s sort a few countries in German:
let countries = ['Österreich', 'Andorra', 'Vietnam'];

alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (wrong)

alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (correct!)

66
Q

let arr = [1, 2, 3, 4, 5];
arr.reverse();

alert( arr ); // Output ?

A

The method arr.reverse reverses the order of elements in arr.

For instance:

let arr = [1, 2, 3, 4, 5];
arr.reverse();

alert( arr ); // 5,4,3,2,1
It also returns the array arr after the reversal.

67
Q

let names = ‘Bilbo, Gandalf, Nazgul’;

let arr = names.split(‘, ‘);

for (let name of arr) {
alert( A message to ${name}. ); // Output ?
}

A

Here’s the situation from real life. We are writing a messaging app, and the person enters the comma-delimited list of receivers: John, Pete, Mary. But for us an array of names would be much more comfortable than a single string. How to get it?

The str.split(delim) method does exactly that. It splits the string into an array by the given delimiter delim.

In the example below, we split by a comma followed by space:

let names = ‘Bilbo, Gandalf, Nazgul’;

let arr = names.split(‘, ‘);

for (let name of arr) {
alert( A message to ${name}. ); // A message to Bilbo (and other names)
}

68
Q

let arr = ‘Bilbo, Gandalf, Nazgul, Saruman’.split(‘, ‘, 2);

alert(arr); // Output ?

A
The split method has an optional second numeric argument – a limit on the array length. If it is provided, then the extra elements are ignored. In practice it is rarely used though:
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

alert(arr); // Bilbo, Gandalf

69
Q

let str = “test”;

alert( str.split(‘’) ); //Output ?

A
The call to split(s) with an empty s would split the string into an array of letters:
let str = "test";

alert( str.split(‘’) ); // t,e,s,t

70
Q

let arr = [‘Bilbo’, ‘Gandalf’, ‘Nazgul’];

let str = arr.join(‘;’); // glue the array into a string using ;

alert( str ); // Output ?

A

The call arr.join(glue) does the reverse to split. It creates a string of arr items joined by glue between them.

For instance:
let arr = [‘Bilbo’, ‘Gandalf’, ‘Nazgul’];

let str = arr.join(‘;’); // glue the array into a string using ;

alert( str ); // Bilbo;Gandalf;Nazgul

71
Q

How reduce() method works ?

A

When we need to iterate over an array – we can use forEach, for or for..of.

When we need to iterate and return the data for each element – we can use map.

The methods arr.reduce and arr.reduceRight also belong to that breed, but are a little bit more intricate. They are used to calculate a single value based on the array.

The syntax is:

let value = arr.reduce(function(accumulator, item, index, array) {
  // ...
}, [initial]);
The function is applied to all array elements one after another and “carries on” its result to the next call.

Arguments:

accumulator – is the result of the previous function call, equals initial the first time (if initial is provided).
item – is the current array item.
index – is its position.
array – is the array.
As function is applied, the result of the previous function call is passed to the next one as the first argument.

So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end it becomes the result of reduce.

72
Q

let arr = [1, 2, 3, 4, 5];

// removed initial value from reduce (no 0)
let result = arr.reduce((sum, current) => sum + current);

alert( result ); // Output ?

A

We also can omit the initial value:

let arr = [1, 2, 3, 4, 5];

// removed initial value from reduce (no 0)
let result = arr.reduce((sum, current) => sum + current);

alert( result ); // 15
The result is the same. That’s because if there’s no initial, then reduce takes the first element of the array as the initial value and starts the iteration from the 2nd element.

The calculation table is the same as above, minus the first row.

But such use requires an extreme care. If the array is empty, then reduce call without initial value gives an error.

Here’s an example:

let arr = [];

// Error: Reduce of empty array with no initial value
// if the initial value existed, reduce would return it for the empty arr.
arr.reduce((sum, current) => sum + current);
So it’s advised to always specify the initial value.

73
Q

How reduceRight() method works ?

A

it works the same as reduce() but start from right to left

74
Q

alert(typeof {}); // Output ?

alert(typeof []); // Output ?

A

alert(typeof {}); // object

alert(typeof []); // object

75
Q

alert(Array.isArray({})); // Output ?

alert(Array.isArray([])); // Output ?

A

alert(Array.isArray({})); // false

alert(Array.isArray([])); // true

arrays are used so often that there’s a special method for that: Array.isArray(value). It returns true if the value is an array, and false otherwise.

76
Q

How symbol.iterator works ?

A

Iterable objects are a generalization of arrays. That’s a concept that allows us to make any object useable in a for..of loop.

Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, strings are also iterable.

If an object isn’t technically an array, but represents a collection (list, set) of something, then for..of is a great syntax to loop over it, so let’s see how to make it work.

We can easily grasp the concept of iterables by making one of our own.

For instance, we have an object that is not an array, but looks suitable for for..of.

Like a range object that represents an interval of numbers:

let range = {
from: 1,
to: 5
};

// We want the for..of to work:
// for(let num of range) ... num=1,2,3,4,5
To make the range object iterable (and thus let for..of work) we need to add a method to the object named Symbol.iterator (a special built-in symbol just for that).

When for..of starts, it calls that method once (or errors if not found). The method must return an iterator – an object with the method next.
Onward, for..of works only with that returned object.
When for..of wants the next value, it calls next() on that object.
The result of next() must have the form {done: Boolean, value: any}, where done=true means that the loop is finished, otherwise value is the next value.

let range = {

from: 1,
to: 5,

Symbol.iterator {
this.current = this.from;
return this;
},

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
Now rangeSymbol.iterator returns the range object itself: it has the necessary next() method and remembers the current iteration progress in this.current. Shorter? Yes. And sometimes that’s fine too.

The downside is that now it’s impossible to have two for..of loops running over the object simultaneously: they’ll share the iteration state, because there’s only one iterator – the object itself. But two parallel for-ofs is a rare thing, even in async scenarios.

77
Q

What are iterables ?

A

Iterables are objects that implement the Symbol.iterator method

78
Q

What are array-likes ?

A

Array-likes are objects that have indexes and length, so they look like arrays.

79
Q

Are strings iterable and array-like both ?

A

strings are both iterable (for..of works on them) and array-like (they have numeric indexes and length).

80
Q

An iterable is always array-like. True/False ?

A

an iterable may be not array-like. And vice versa an array-like may be not iterable.

81
Q

How Array.from works ?

A

There’s a universal method Array.from that takes an iterable or array-like value and makes a “real” Array from it. Then we can call array methods on it.

For instance:

let arrayLike = {
  0: "Hello",
  1: "World",
  length: 2
};

let arr = Array.from(arrayLike); // ()
alert(arr.pop()); // World (method works)
Array.from at the line (
) takes the object, examines it for being an iterable or array-like, then makes a new array and copies all items to it.

82
Q

What is the syntax for Array.from ?

A

The full syntax for Array.from also allows us to provide an optional “mapping” function:

Array.from(obj[, mapFn, thisArg])
The optional second argument mapFn can be a function that will be applied to each element before adding it to the array, and thisArg allows us to set this for it.

For instance:

// assuming that range is taken from the example above

// square each number
let arr = Array.from(range, num => num * num);

alert(arr); // 1,4,9,16,25

83
Q

What is Map ? Also tell about set, get, has, delete, clear, size methods

A

Map is a collection of keyed data items, just like an Object. But the main difference is that Map allows keys of any type.

Methods and properties are:

new Map() – creates the map.

map. set(key, value) – stores the value by the key.
map. get(key) – returns the value by the key, undefined if key doesn’t exist in map.
map. has(key) – returns true if the key exists, false otherwise.
map. delete(key) – removes the value by the key.
map. clear() – removes everything from the map.
map. size – returns the current element count.

84
Q

let map = new Map();

map.set(‘1’, ‘str1’);
map.set(1, ‘num1’);
map.set(true, ‘bool1’);
alert( map.get(1) );
alert( map.get(‘1’) );

alert( map.size );
Output ?

A

let map = new Map();

map. set(‘1’, ‘str1’); // a string key
map. set(1, ‘num1’); // a numeric key
map. set(true, ‘bool1’); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

85
Q

map[key] isn’t the right way to use a Map. It works but some other method of accessing keys is more preffered. what is that method ?

A

Although map[key] also works, e.g. we can set map[key] = 2, this is treating map as a plain JavaScript object, so it implies all corresponding limitations (only string/symbol keys and so on).

So we should use map methods: set, get and so on.

86
Q

Can we use objects as keys in map ?

A

Map can also use objects as keys.

For instance:

let john = { name: “John” };

// for every user, let's store their visits count
let visitsCountMap = new Map();
// john is the key for the map
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123
Using objects as keys is one of the most notable and important Map features. The same does not count for Object. String as a key in Object is fine, but we can’t use another Object as a key in Object.

87
Q

Can we use another object as a key in a object ?

A

No

88
Q

Is chaining possible to do in maps ?

A

Every map.set call returns the map itself, so we can “chain” the calls:

map.set(‘1’, ‘str1’)
.set(1, ‘num1’)
.set(true, ‘bool1’);

89
Q

Write three methods used for iteration on maps

A

For looping over a map, there are 3 methods:

map. keys() – returns an iterable for keys,
map. values() – returns an iterable for values,
map. entries() – returns an iterable for entries [key, value], it’s used by default in for..of.

90
Q
let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

for (let vegetable of recipeMap.keys()) {
alert(vegetable);
}

for (let amount of recipeMap.values()) {
alert(amount);
}

for (let entry of recipeMap) {
alert(entry);
}

A
let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);
// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}
// iterate over values (amounts)
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}
// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
  alert(entry); // cucumber,500 (and so on)
}
91
Q

Is order preserved in maps ?

A

The iteration goes in the same order as the values were inserted. Map preserves this order, unlike a regular Object.

92
Q

recipeMap.forEach( (value, key, map) => {
alert(${key}: ${value});
});

A

recipeMap.forEach( (value, key, map) => {
alert(${key}: ${value}); // cucumber: 500 etc
});
Map has a built-in forEach method, similar to Array:

93
Q
// array of [key, value] pairs
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

alert( map.get(‘1’) ); // str1
Can we initialize a map like this ?

A

When a Map is created, we can pass an array (or another iterable) with key/value pairs for initialization, like this:

94
Q

How to create a map from an object ?

A

If we have a plain object, and we’d like to create a Map from it, then we can use built-in method Object.entries(obj) that returns an array of key/value pairs for an object exactly in that format.

So we can create a map from an object like this:

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get(‘name’) ); // John
Here, Object.entries returns the array of key/value pairs: [ [“name”,”John”], [“age”, 30] ]. That’s what Map needs.

95
Q

How to make a object from map ?

A

We’ve just seen how to create Map from a plain object with Object.entries(obj).

There’s Object.fromEntries method that does the reverse: given an array of [key, value] pairs, it creates an object from them:

let prices = Object.fromEntries([
  ['banana', 1],
  ['orange', 2],
  ['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2
We can use Object.fromEntries to get a plain object from Map.

96
Q

let map = new Map();

map. set(‘banana’, 1);
map. set(‘orange’, 2);
map. set(‘meat’, 4);

let obj = Object.fromEntries(map.entries()); // (*)

alert(obj.orange); // Output ?

A

We can use Object.fromEntries to get a plain object from Map.

E.g. we store the data in a Map, but we need to pass it to a 3rd-party code that expects a plain object.

Here we go:

let map = new Map();

map. set(‘banana’, 1);
map. set(‘orange’, 2);
map. set(‘meat’, 4);

let obj = Object.fromEntries(map.entries()); // make a plain object (*)

// done!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2
A call to map.entries() returns an iterable of key/value pairs, exactly in the right format for Object.fromEntries.

We could also make line (*) shorter:

let obj = Object.fromEntries(map); // omit .entries()
That’s the same, because Object.fromEntries expects an iterable object as the argument. Not necessarily an array. And the standard iteration for map returns same key/value pairs as map.entries(). So we get a plain object with same key/values as the map.
97
Q

What is set ? how add, delete, has, clear, size works ?

A

set. add(value) – adds a value, returns the set itself.
set. delete(value) – removes the value, returns true if value existed at the moment of the call, otherwise false.
set. has(value) – returns true if the value exists in the set, otherwise false.
set. clear() – removes everything from the set.
set. size – is the elements count.

98
Q

How to create a set ?

A

new Set(iterable) – creates the set, and if an iterable object is provided (usually an array), copies values from it into the set.

99
Q

How to iterate over sets ?

A

We can loop over a set either with for..of or using forEach:

let set = new Set([“oranges”, “apples”, “bananas”]);

for (let value of set) alert(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  alert(value);
});
Note the funny thing. The callback function passed in forEach has 3 arguments: a value, then the same value valueAgain, and then the target object. Indeed, the same value appears in the arguments twice.

That’s for compatibility with Map where the callback passed forEach has three arguments. Looks a bit strange, for sure. But may help to replace Map with Set in certain cases with ease, and vice versa.

100
Q

How set.keys(), set.values(), set.entries() work ?

A

set. keys() – returns an iterable object for values,
set. values() – same as set.keys(), for compatibility with Map,
set. entries() – returns an iterable object for entries [value, value], exists for compatibility with Map.

101
Q

Let arr be an array.

Create a function unique(arr) that should return an array with unique items of arr.

For instance:

function unique(arr) {
  /* your code */
}

let values = [“Hare”, “Krishna”, “Hare”, “Krishna”,
“Krishna”, “Krishna”, “Hare”, “Hare”, “:-O”
];

alert( unique(values) ); // Hare, Krishna, :-O
P.S. Here strings are used, but can be values of any type.

A
function unique(arr) {
  return Array.from(new Set(arr));
}
102
Q

We’d like to get an array of map.keys() in a variable and then apply array-specific methods to it, e.g. .push.

But that doesn’t work:

let map = new Map();

map.set(“name”, “John”);

let keys = map.keys();

// Error: keys.push is not a function
keys.push(“more”);
Why? How can we fix the code to make keys.push work?

A

That’s because map.keys() returns an iterable, but not an array.

We can convert it into an array using Array.from:

let map = new Map();

map.set(“name”, “John”);

let keys = Array.from(map.keys());

keys.push(“more”);

alert(keys); // name, more

103
Q

Explain weakmaps with an example

A

As we know from the chapter Garbage collection, JavaScript engine keeps a value in memory while it is “reachable” and can potentially be used.

For instance:

let john = { name: “John” };

// the object can be accessed, john is the reference to it

// overwrite the reference
john = null;
// the object will be removed from memory
Usually, properties of an object or elements of an array or another data structure are considered reachable and kept in memory while that data structure is in memory.

For instance, if we put an object into an array, then while the array is alive, the object will be alive as well, even if there are no other references to it.

Like this:

let john = { name: “John” };

let array = [ john ];

john = null; // overwrite the reference

// the object previously referenced by john is stored inside the array
// therefore it won't be garbage-collected
// we can get it as array[0]
Similar to that, if we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected.

For instance:

let john = { name: “John” };

let map = new Map();
map.set(john, "...");

john = null; // overwrite the reference

// john is stored inside the map,
// we can get it by using map.keys()
WeakMap is fundamentally different in this aspect. It doesn’t prevent garbage-collection of key objects.

Let’s see what it means on examples.

104
Q

Difference between maps and weakmaps with an example

A

The first difference between Map and WeakMap is that keys must be objects, not primitive values:

let weakMap = new WeakMap();

let obj = {};

weakMap.set(obj, “ok”); // works fine (object key)

// can't use a string as the key
weakMap.set("test", "Whoops"); // Error, because "test" is not an object
Now, if we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.

let john = { name: “John” };

let weakMap = new WeakMap();
weakMap.set(john, "...");

john = null; // overwrite the reference

// john is removed from memory!

Compare it with the regular Map example above. Now if john only exists as the key of WeakMap – it will be automatically deleted from the map (and memory).

105
Q

Do weakMaps supports iteration and methods like keys(), entries() etc. ?

A

WeakMap does not support iteration and methods keys(), values(), entries(), so there’s no way to get all keys or values from it.

WeakMap has only the following methods:

weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
Why such a limitation? That’s for technical reasons. If an object has lost all other references (like john in the code above), then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens.

The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically, the current element count of a WeakMap is not known. The engine may have cleaned it up or not, or did it partially. For that reason, methods that access all keys/values are not supported.

106
Q

Explain in detail: How we can use weakMaps for storing additional data for some purpose ?

A

The main area of application for WeakMap is an additional data storage.

If we’re working with an object that “belongs” to another code, maybe even a third-party library, and would like to store some data associated with it, that should only exist while the object is alive – then WeakMap is exactly what’s needed.

We put the data to a WeakMap, using the object as the key, and when the object is garbage collected, that data will automatically disappear as well.

weakMap.set(john, "secret documents");
// if john dies, secret documents will be destroyed automatically
Let’s look at an example.

For instance, we have code that keeps a visit count for users. The information is stored in a map: a user object is the key and the visit count is the value. When a user leaves (its object gets garbage collected), we don’t want to store their visit count anymore.

Here’s an example of a counting function with Map:

// 📁 visitsCount.js
let visitsCountMap = new Map(); // map: user => visits count

// increase the visits count
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
And here’s another part of the code, maybe another file using it:

// 📁 main.js
let john = { name: "John" };

countUser(john); // count his visits

// later john leaves us
john = null;
Now, john object should be garbage collected, but remains in memory, as it’s a key in visitsCountMap.

We need to clean visitsCountMap when we remove users, otherwise it will grow in memory indefinitely. Such cleaning can become a tedious task in complex architectures.

We can avoid it by switching to WeakMap instead:

// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // weakmap: user => visits count
// increase the visits count
function countUser(user) {
  let count = visitsCountMap.get(user) || 0;
  visitsCountMap.set(user, count + 1);
}
Now we don’t have to clean visitsCountMap. After john object becomes unreachable, by all means except as a key of WeakMap, it gets removed from memory, along with the information by that key from WeakMap.
107
Q

Explain in detail: How we can use weakmaps for caching ?

A

Another common example is caching. We can store (“cache”) results from a function, so that future calls on the same object can reuse it.

To achieve that, we can use Map (not optimal scenario):

// 📁 cache.js
let cache = new Map();
// calculate and remember the result
function process(obj) {
  if (!cache.has(obj)) {
    let result = /* calculations of the result for */ obj;
cache.set(obj, result);   }

return cache.get(obj);
}

// Now we use process() in another file:

// 📁 main.js
let obj = {/* let's say we have an object */};

let result1 = process(obj); // calculated

// ...later, from another place of the code...
let result2 = process(obj); // remembered result taken from cache
// ...later, when the object is not needed any more:
obj = null;

alert(cache.size); // 1 (Ouch! The object is still in cache, taking memory!)
For multiple calls of process(obj) with the same object, it only calculates the result the first time, and then just takes it from cache. The downside is that we need to clean cache when the object is not needed any more.

If we replace Map with WeakMap, then this problem disappears. The cached result will be removed from memory automatically after the object gets garbage collected.

// 📁 cache.js
let cache = new WeakMap();
// calculate and remember the result
function process(obj) {
  if (!cache.has(obj)) {
    let result = /* calculate the result for */ obj;
cache.set(obj, result);   }

return cache.get(obj);
}

// 📁 main.js
let obj = {/* some object */};
let result1 = process(obj);
let result2 = process(obj);
// ...later, when the object is not needed any more:
obj = null;
// Can't get cache.size, as it's a WeakMap,
// but it's 0 or soon be 0
// When obj gets garbage collected, cached data will be removed as well
108
Q

What are weakSets ?

A

WeakSet behaves similarly:

It is analogous to Set, but we may only add objects to WeakSet (not primitives).
An object exists in the set while it is reachable from somewhere else.
Like Set, it supports add, has and delete, but not size, keys() and no iterations.
Being “weak”, it also serves as additional storage. But not for arbitrary data, rather for “yes/no” facts. A membership in WeakSet may mean something about the object.

For instance, we can add users to WeakSet to keep track of those who visited our site:

let visitedSet = new WeakSet();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

visitedSet.add(john); // John visited us
visitedSet.add(pete); // Then Pete
visitedSet.add(john); // John again

// visitedSet has 2 users now

// check if John visited?
alert(visitedSet.has(john)); // true
// check if Mary visited?
alert(visitedSet.has(mary)); // false

john = null;

// visitedSet will be cleaned automatically
The most notable limitation of WeakMap and WeakSet is the absence of iterations, and the inability to get all current content. That may appear inconvenient, but does not prevent WeakMap/WeakSet from doing their main job – be an “additional” storage of data for objects which are stored/managed at another place.
109
Q

There’s an array of messages:

let messages = [
  {text: "Hello", from: "John"},
  {text: "How goes?", from: "John"},
  {text: "See you soon", from: "Alice"}
];
Your code can access it, but the messages are managed by someone else’s code. New messages are added, old ones are removed regularly by that code, and you don’t know the exact moments when it happens.

Now, which data structure could you use to store information about whether the message “has been read”? The structure must be well-suited to give the answer “was it read?” for the given message object.

P.S. When a message is removed from messages, it should disappear from your structure as well.

P.P.S. We shouldn’t modify message objects, add our properties to them. As they are managed by someone else’s code, that may lead to bad consequences.

A

Let’s store read messages in WeakSet:

let messages = [
  {text: "Hello", from: "John"},
  {text: "How goes?", from: "John"},
  {text: "See you soon", from: "Alice"}
];

let readMessages = new WeakSet();

// two messages have been read
readMessages.add(messages[0]);
readMessages.add(messages[1]);
// readMessages has 2 elements

// ...let's read the first message again!
readMessages.add(messages[0]);
// readMessages still has 2 unique elements
// answer: was the message[0] read?
alert("Read message 0: " + readMessages.has(messages[0])); // true

messages.shift();
// now readMessages has 1 element (technically memory may be cleaned later)
The WeakSet allows to store a set of messages and easily check for the existence of a message in it.

It cleans up itself automatically. The tradeoff is that we can’t iterate over it, can’t get “all read messages” from it directly. But we can do it by iterating over all messages and filtering those that are in the set.

Another, different solution could be to add a property like message.isRead=true to a message after it’s read. As messages objects are managed by another code, that’s generally discouraged, but we can use a symbolic property to avoid conflicts.

Like this:

// the symbolic property is only known to our code
let isRead = Symbol("isRead");
messages[0][isRead] = true;
Now third-party code probably won’t see our extra property.

Although symbols allow to lower the probability of problems, using WeakSet is better from the architectural point of view.

110
Q
let user = {
  name: "John",
  age: 30
};

How Object.keys(), Object.values(), Object.entries() will work ?

A
Object.keys(user) = ["name", "age"]
Object.values(user) = ["John", 30]
Object.entries(user) = [ ["name","John"], ["age",30] ]
111
Q

How to apply array methods on objects ? Explain with examples

A

Objects lack many methods that exist for arrays, e.g. map, filter and others.

If we’d like to apply them, then we can use Object.entries followed by Object.fromEntries:

Use Object.entries(obj) to get an array of key/value pairs from obj.
Use array methods on that array, e.g. map, to transform these key/value pairs.
Use Object.fromEntries(array) on the resulting array to turn it back into an object.
For example, we have an object with prices, and would like to double them:

let prices = {
  banana: 1,
  orange: 2,
  meat: 4,
};
let doublePrices = Object.fromEntries(
  // convert prices to array, map each key/value pair into another pair
  // and then fromEntries gives back the object
  Object.entries(prices).map(entry => [entry[0], entry[1] * 2])
);

alert(doublePrices.meat); // 8

112
Q

There is a salaries object with arbitrary number of salaries.

Write the function sumSalaries(salaries) that returns the sum of all salaries using Object.values and the for..of loop.

If salaries is empty, then the result must be 0.

For instance:

let salaries = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};

alert( sumSalaries(salaries) ); // 650

A

function sumSalaries(salaries) {

  let sum = 0;
  for (let salary of Object.values(salaries)) {
    sum += salary;
  }

return sum; // 650
}

let salaries = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};

alert( sumSalaries(salaries) ); // 650
Or, optionally, we could also get the sum using Object.values and reduce:

// reduce loops over array of salaries,
// adding them up
// and returns the result
function sumSalaries(salaries) {
  return Object.values(salaries).reduce((a, b) => a + b, 0) // 650
}
113
Q

Write a function count(obj) that returns the number of properties in the object:

let user = {
  name: 'John',
  age: 30
};

alert( count(user) ); // 2
Try to make the code as short as possible.

P.S. Ignore symbolic properties, count only “regular” ones.

A
function count(obj) {
  return Object.keys(obj).length;
}
114
Q

What is destructuring assignment ?

A

The two most used data structures in JavaScript are Object and Array.

Objects allow us to create a single entity that stores data items by key.
Arrays allow us to gather data items into an ordered list.
Although, when we pass those to a function, it may need not be an object/array as a whole. It may need individual pieces.

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.

Destructuring also works great with complex functions that have a lot of parameters, default values, and so on. Soon we’ll see that.

115
Q
// we have an array with the name and surname
let arr = ["John", "Smith"]

let [firstName, surname] = arr;

alert(firstName); // Output ?
alert(surname); // Output ?

A
// we have an array with the name and surname
let arr = ["John", "Smith"]
// destructuring assignment
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;

alert(firstName); // John
alert(surname); // Smith

116
Q

let [firstName, surname] = “John Smith”.split(‘ ‘);
alert(firstName); //
alert(surname); // Output ?

A

let [firstName, surname] = “John Smith”.split(‘ ‘);
alert(firstName); // John
alert(surname); // Smith

117
Q

let [firstName, , title] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];

alert( title ); // Output ?

A

Unwanted elements of the array can also be thrown away via an extra comma:

// second element is not needed
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert( title ); // Consul
In the code above, the second element of the array is skipped, the third one is assigned to title, and the rest of the array items is also skipped (as there are no variables for them).

118
Q

Does destructoring works with any iterable ?

A

Yes,
Actually, we can use it with any iterable, not only arrays:

let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
That works, because internally a destructuring assignment works by iterating over the right value. It’s a kind of syntax sugar for calling for..of over the value to the right of = and assigning the values.
119
Q

Can destructuring be done on any object ?

A

We can use any “assignables” on the left side.

For instance, an object property:

let user = {};
[user.name, user.surname] = "John Smith".split(' ');

alert(user.name); // John
alert(user.surname); // Smith

120
Q
let user = {
  name: "John",
  age: 30
};
// loop over keys-and-values
for (let [key, value] of Object.entries(user)) {
  alert(`${key}:${value}`); // Output ? 
}
A
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
}
121
Q

let user = new Map();

user. set(“name”, “John”);
user. set(“age”, “30”);

// Map iterates as [key, value] pairs, very convenient for destructuring
for (let [key, value] of user) {
  alert(`${key}:${value}`); // Output ? 
}
A

let user = new Map();

user. set(“name”, “John”);
user. set(“age”, “30”);

// Map iterates as [key, value] pairs, very convenient for destructuring
for (let [key, value] of user) {
  alert(`${key}:${value}`); // name:John, then age:30
}
122
Q

How to swap values using destructuring operator ?

A

There’s a well-known trick for swapping values of two variables using a destructuring assignment:

let guest = "Jane";
let admin = "Pete";
// Let's swap the values: make guest=Pete, admin=Jane
[guest, admin] = [admin, guest];

alert(${guest} ${admin}); // Pete Jane (successfully swapped!)
Here we create a temporary array of two variables and immediately destructure it in swapped order.

We can swap more than two variables this way.

123
Q

let [name1, name2, …rest] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];

// rest is array of items, starting from the 3rd one
alert(rest[0]); // Output ?
alert(rest[1]); // Output ?
alert(rest.length); // Output ?

A

If we’d like also to gather all that follows – we can add one more parameter that gets “the rest” using three dots “…”:

let [name1, name2, …rest] = [“Julius”, “Caesar”, “Consul”, “of the Roman Republic”];

// 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
The value of rest is the array of the remaining array elements.

We can use any other variable name in place of rest, just make sure it has three dots before it and goes last in the destructuring assignment.

let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// now titles = ["Consul", "of the Roman Republic"]
124
Q

let [firstName, surname] = [];

alert(firstName); // Output ?
alert(surname); // Output ?

A

If the array is shorter than the list of variables at the left, there’ll be no errors. Absent values are considered undefined:

let [firstName, surname] = [];

alert(firstName); // undefined
alert(surname); // undefined

125
Q

Can we set default values to the destructuring operator ?

A

Yes,

If we want a “default” value to replace the missing one, we can provide it using =:

// default values
let [name = "Guest", surname = "Anonymous"] = ["Julius"];

alert(name); // Julius (from array)
alert(surname); // Anonymous (default used)
Default values can be more complex expressions or even function calls. They are evaluated only if the value is not provided.

For instance, here we use the prompt function for two defaults:

// runs only prompt for surname
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];

alert(name); // Julius (from array)
alert(surname); // whatever prompt gets
Please note: the prompt will run only for the missing value (surname).

126
Q

What is the basic syntax for object destructuring ?

A

The destructuring assignment also works with objects.

The basic syntax is:

let {var1, var2} = {var1:…, var2:…}
We should have an existing object on the right side, that we want to split into variables. The left side contains an object-like “pattern” for corresponding properties. In the simplest case, that’s a list of variable names in {…}.

127
Q
let options = {
  title: "Menu",
  width: 100,
  height: 200
};

let {title, width, height} = options;

alert(title); // Output ?
alert(width); // Output ?
alert(height); // Output ?

A
let options = {
  title: "Menu",
  width: 100,
  height: 200
};

let {title, width, height} = options;

alert(title); // Menu
alert(width); // 100
alert(height); // 200
Properties options.title, options.width and options.height are assigned to the corresponding variables.

The order does not matter. This works too:

// changed the order in let {...}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
128
Q
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); // Output ?
alert(w); // Output ?
alert(h); // Output ?

A

The pattern on the left side may be more complex and specify the mapping between properties and variables.

If we want to assign a property to a variable with another name, for instance, make options.width go into the variable named w, then we can set the variable name using a colon:

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
The colon shows “what : goes where”. In the example above the property width goes to w, property height goes to h, and title is assigned to the same name.

129
Q
let options = {
  title: "Menu"
};

let {width = 100, height = 200, title} = options;

alert(title); // Output ?
alert(width); // Output ?
alert(height); //Output ?

A

For potentially missing properties we can set default values using “=”, like this:

let options = {
  title: "Menu"
};

let {width = 100, height = 200, title} = options;

alert(title); // Menu
alert(width); // 100
alert(height); // 200

130
Q
let options = {
  title: "Menu"
};

let {width = prompt(“width?”), title = prompt(“title?”)} = options;

alert(title); // Output ?
alert(width); // Output ?

A

Just like with arrays or function parameters, default values can be any expressions or even function calls. They will be evaluated if the value is not provided.

In the code below prompt asks for width, but not for title:

let options = {
  title: "Menu"
};

let {width = prompt(“width?”), title = prompt(“title?”)} = options;

alert(title); // Menu
alert(width); // (whatever the result of prompt is)

131
Q
let options = {
  title: "Menu"
};

let {width: w = 100, height: h = 200, title} = options;

alert(title); //
alert(w); //
alert(h); // Output ?

A
let options = {
  title: "Menu"
};

let {width: w = 100, height: h = 200, title} = options;

alert(title); // Menu
alert(w); // 100
alert(h); // 200

132
Q
let options = {
  title: "Menu",
  width: 100,
  height: 200
};

let { title } = options;

alert(title); // Output ?

A
If we have a complex object with many properties, we can extract only what we need:
let options = {
  title: "Menu",
  width: 100,
  height: 200
};
// only extract title as a variable
let { title } = options;

alert(title); // Menu

133
Q
let options = {
  title: "Menu",
  height: 200,
  width: 100
};

let {title, …rest} = options;

alert(rest.height); //
alert(rest.width); // Output ?

A

What if the object has more properties than we have variables? Can we take some and then assign the “rest” somewhere?

We can use the rest pattern, just like we did with arrays. It’s not supported by some older browsers (IE, use Babel to polyfill it), but works in modern ones.

It looks like this:

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

134
Q

let title, width, height;

{title, width, height} = {title: “Menu”, width: 200, height: 100}; // Output ?

A

Gotcha if there’s no let
In the examples above variables were declared right in the assignment: let {…} = {…}. Of course, we could use existing variables too, without let. But there’s a catch.

This won’t work:

let title, width, height;

// error in this line
{title, width, height} = {title: "Menu", width: 200, height: 100};
The problem is that JavaScript treats {...} in the main code flow (not inside another expression) as a code block. Such code blocks can be used to group statements, like this:
{
  // a code block
  let message = "Hello";
  // ...
  alert( message );
}
So here JavaScript assumes that we have a code block, that’s why there’s an error. We want destructuring instead.

To show JavaScript that it’s not a code block, we can wrap the expression in parentheses (…):

let title, width, height;

// okay now
({title, width, height} = {title: "Menu", width: 200, height: 100});

alert( title ); // Menu

135
Q
let options = {
  size: {
    width: 100,
    height: 200
  },
  items: ["Cake", "Donut"],
  extra: true
};

How to do nested destructuring ?

A

If an object or an array contain other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions.

In the code below options has another object in the property size and an array in the property items. The pattern on the left side of the assignment has the same structure to extract values from them:

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
136
Q

How to use destructuring while passing an object as a parameter ? Explain with example.

A

There are times when a function has many parameters, most of which are optional. That’s especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.

Here’s a bad way to write such function:

function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
  // ...
}
In real-life, the problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still… Another problem is how to call a function when most parameters are ok by default.

Like this?

// undefined where default values are fine
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
That’s ugly. And becomes unreadable when we deal with more parameters.

Destructuring comes to the rescue!

We can pass parameters as an object, and the function immediately destructurizes them into variables:

// we pass object to function
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);
We can also use more complex destructuring with nested objects and colon mappings:

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);
The full syntax is the same as for a destructuring assignment:

function({
  incomingProperty: varName = defaultValue
  ...
})
Then, for an object of parameters, there will be a variable varName for property incomingProperty, with defaultValue by default.

Please note that such destructuring assumes that showMenu() does have an argument. If we want all values by default, then we should specify an empty object:

showMenu({}); // ok, all values are default

showMenu(); // this would give an error
We can fix this by making {} the default value for the whole object of parameters:

function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
  alert( `${title} ${width} ${height}` );
}

showMenu(); // Menu 100 200
In the code above, the whole arguments object is {} by default, so there’s always something to destructurize.

137
Q

Write code for creating a new Date object ?

A

To create a new Date object call new Date() with one of the following arguments:

new Date()
Without arguments – create a Date object for the current date and time:
let now = new Date();
alert( now ); // shows current date/time
138
Q
let date = new Date(2011, 0, 1, 2, 3, 4, 567);
alert( date ); // Output ?
A
new Date(year, month, date, hours, minutes, seconds, ms)
Create the date with the given components in the local time zone. Only the first two arguments are obligatory.

The year must have 4 digits: 2013 is okay, 98 is not.
The month count starts with 0 (Jan), up to 11 (Dec).
The date parameter is actually the day of month, if absent then 1 is assumed.
If hours/minutes/seconds/ms is absent, they are assumed to be equal 0.
For instance:

new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00
new Date(2011, 0, 1); // the same, hours etc are 0 by default
The maximal precision is 1 ms (1/1000 sec):
let date = new Date(2011, 0, 1, 2, 3, 4, 567);
alert( date ); // 1.01.2011, 02:03:04.567
139
Q

List some methods for accessing the date properties in the date object. `

A

There are methods to access the year, month and so on from the Date object:

getFullYear()
Get the year (4 digits)
getMonth()
Get the month, from 0 to 11.
getDate()
Get the day of month, from 1 to 31, the name of the method does look a little bit strange.
getHours(), getMinutes(), getSeconds(), getMilliseconds()
Get the corresponding time components.

Additionally, we can get a day of week:

getDay()
Get the day of week, from 0 (Sunday) to 6 (Saturday). The first day is always Sunday, in some countries that’s not so, but can’t be changed.
All the methods above return the components relative to the local time zone.

There are also their UTC-counterparts, that return day, month, year and so on for the time zone UTC+0: getUTCFullYear(), getUTCMonth(), getUTCDay(). Just insert the “UTC” right after “get”.

140
Q

How would you send a object over a network without using the JSON ?

A

Let’s say we have a complex object, and we’d like to convert it into a string, to send it over a network, or just to output it for logging purposes.

Naturally, such a string should include all important properties.

We could implement the conversion like this:

let user = {

name: “John”,
age: 30,

toString() {
return {name: "${this.name}", age: ${this.age}};
}
};

alert(user); // {name: “John”, age: 30}
…But in the process of development, new properties are added, old properties are renamed and removed. Updating such toString every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We’d need to implement their conversion as well.

Luckily, there’s no need to write the code to handle all this. The task has been solved already.

141
Q

List the methods that JS provides for handling JSON ?

A

The JSON (JavaScript Object Notation) is a general format to represent values and objects. It is described as in RFC 4627 standard. Initially it was made for JavaScript, but many other languages have libraries to handle it as well. So it’s easy to use JSON for data exchange when the client uses JavaScript and the server is written on Ruby/PHP/Java/Whatever.

JavaScript provides methods:

JSON.stringify to convert objects into JSON.
JSON.parse to convert JSON back into an object.
For instance, here we JSON.stringify a student:

let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  wife: null
};

let json = JSON.stringify(student);

alert(typeof json); // we’ve got a string!

alert(json);
/* JSON-encoded object:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "wife": null
}
*/
The method JSON.stringify(student) takes the object and converts it into a string.

The resulting json string is called a JSON-encoded or serialized or stringified or marshalled object. We are ready to send it over the wire or put into a plain data store.

142
Q

Which data types do JSON supports ?

A

JSON supports following data types:

Objects { ... }
Arrays [ ... ]
Primitives:
strings,
numbers,
boolean values true/false,
null.
For instance:
// a number in JSON is just a number
alert( JSON.stringify(1) ) // 1
// a string in JSON is still a string, but double-quoted
alert( JSON.stringify('test') ) // "test"

alert( JSON.stringify(true) ); // true

alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]

143
Q

Which object properties are skipped by JSON ?

A

JSON is data-only language-independent specification, so some JavaScript-specific object properties are skipped by JSON.stringify.

Namely:

Function properties (methods).
Symbolic keys and values.
Properties that store undefined.
let user = {
  sayHi() { // ignored
    alert("Hello");
  },
  [Symbol("id")]: 123, // ignored
  something: undefined // ignored
};

alert( JSON.stringify(user) ); // {} (empty object)

144
Q

Are nested objects supported by JSON ?

A

The great thing is that nested objects are supported and converted automatically.

For instance:

let meetup = {
  title: "Conference",
  room: {
    number: 23,
    participants: ["john", "ann"]
  }
};
alert( JSON.stringify(meetup) );
/* The whole structure is stringified:
{
  "title":"Conference",
  "room":{"number":23,"participants":["john","ann"]},
}
*/
The important limitation: there must be no circular references.

For instance:

let room = {
  number: 23
};
let meetup = {
  title: "Conference",
  participants: ["john", "ann"]
};

meetup. place = room; // meetup references room
room. occupiedBy = meetup; // room references meetup

JSON.stringify(meetup); // Error: Converting circular structure to JSON