Objects: the basics Flashcards

1
Q

What is an object datatype in JS ?

A

As we know from the chapter Data types, there are eight data types in JavaScript. Seven of them are called “primitive”, because their values contain only a single thing (be it a string or a number or whatever).

In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else.

An object can be created with figure brackets {…} with an optional list of properties. A property is a “key: value” pair, where key is a string (also called a “property name”), and value can be anything.

We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It’s easy to find a file by its name or add/remove a file.

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

How to declare an empty object ? Write two methods.

A
let user = new Object(); // "object constructor" syntax
let user = {};  // "object literal" syntax
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is a property in an object ?

A

let user = { // an object
name: “John”, // by key “name” store value “John”
age: 30 // by key “age” store value 30
};
A property has a key (also known as “name” or “identifier”) before the colon “:” and a value to the right of it.

In the user object, there are two properties:

The first property has the name “name” and the value “John”.
The second one has the name “age” and the value 30.

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

How to access a property of an object ?

A

Property values are accessible using the dot notation:

// get property values of the object:
alert( user.name ); // John
alert( user.age ); // 30
The value can be of any type. Let’s add a boolean one:

user.isAdmin = true;

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

How to delete a property of an object ?

A

To remove a property, we can use delete operator:

delete user.age;

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

Can we use multi word property name for an object ?

A

We can also use multiword property names, but then they must be quoted:

let user = {
  name: "John",
  age: 30,
  "likes birds": true  // multiword property name must be quoted
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Why the last property ends with a comma ?

A

The last property in the list may end with a comma:

let user = {
  name: "John",
  age: 30,
}
That is called a “trailing” or “hanging” comma. Makes it easier to add/remove/move around properties, because all lines become alike.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

How to access multi word properties of an object ?

A

For multiword properties, the dot access doesn’t work:

// this would give a syntax error
user.likes birds = true
JavaScript doesn’t understand that. It thinks that we address user.likes, and then gives a syntax error when comes across unexpected birds.

The dot requires the key to be a valid variable identifier. That implies: contains no spaces, doesn’t start with a digit and doesn’t include special characters ($ and _ are allowed).

There’s an alternative “square bracket notation” that works with any string:

let user = {};

// set
user["likes birds"] = true;
// get
alert(user["likes birds"]); // true
// delete
delete user["likes birds"];
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Can we access a property from another variable ?

A

yes
Square brackets also provide a way to obtain the property name as the result of any expression – as opposed to a literal string – like from a variable as follows:

let key = “likes birds”;

// same as user["likes birds"] = true;
user[key] = true;
Here, the variable key may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q
let user = {
  name: "John",
  age: 30
};
let key = "name";
alert( user.key ) // Will this work ?
A
let user = {
  name: "John",
  age: 30
};
let key = "name";
alert( user.key ) // undefined
Because dot operator doesn't work like that
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

What are computed properties ?

A

We can use square brackets in an object literal, when creating an object. That’s called computed properties.

For instance:

let fruit = prompt(“Which fruit to buy?”, “apple”);

let bag = {
  [fruit]: 5, // the name of the property is taken from the variable fruit
};

alert( bag.apple ); // 5 if fruit=”apple”
The meaning of a computed property is simple: [fruit] means that the property name should be taken from fruit.

So, if a visitor enters “apple”, bag will become {apple: 5}.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q
let fruit = 'apple';
let bag = {
  [fruit + 'Computers']: 5 // What does this line mean if we write it in dot operator form ? 
};
A
let fruit = 'apple';
let bag = {
  [fruit + 'Computers']: 5 // bag.appleComputers = 5
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Are thier any name limitations for property names ?

A

As we already know, a variable cannot have a name equal to one of language-reserved words like “for”, “let”, “return” etc.

But for an object property, there’s no such restriction:

// these properties are all right
let obj = {
  for: 1,
  let: 2,
  return: 3
};

alert( obj.for + obj.let + obj.return ); // 6
In short, there are no limitations on property names. They can be any strings or symbols (a special type for identifiers, to be covered later).

Other types are automatically converted to strings.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q
let obj = {
  0: "test" 
};
alert( obj["0"] ); 
alert( obj[0] ); 

Will this work ?

A
let obj = {
  0: "test" // same as "0": "test"
};

// both alerts access the same property (the number 0 is converted to string “0”)
alert( obj[“0”] ); // test
alert( obj[0] ); // test (same property)

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

Is it possible to access any non existing property of an object. Will there be any error ?

A

No.
A notable feature of objects in JavaScript, compared to many other languages, is that it’s possible to access any property. There will be no error if the property doesn’t exist!

Reading a non-existing property just returns undefined. So we can easily test whether the property exists:

let user = {};

alert( user.noSuchProperty === undefined ); // true means “no such property”

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

What is ‘in’ operator ?

A

The syntax is:

“key” in object
For instance:

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

alert( “age” in user ); // true, user.age exists
alert( “blabla” in user ); // false, user.blabla doesn’t exist

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

Do we have to use the ‘in’ operator for checking any property existence in a object. Can’t we directly use undefined comparison ?

A

Why does the in operator exist? Isn’t it enough to compare against undefined?

Well, most of the time the comparison with undefined works fine. But there’s a special case when it fails, but “in” works correctly.

It’s when an object property exists, but stores undefined:

let obj = {
  test: undefined
};

alert( obj.test ); // it’s undefined, so - no such property?

alert( “test” in obj ); // true, the property does exist!
In the code above, the property obj.test technically exists. So the in operator works right.

Situations like this happen very rarely, because undefined should not be explicitly assigned. We mostly use null for “unknown” or “empty” values. So the in operator is an exotic guest in the code.

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

How for in loop works ?

A

To walk over all keys of an object, there exists a special form of the loop: for..in. This is a completely different thing from the for(;;) construct that we studied before.

The syntax:

for (key in object) {
  // executes the body for each key among object properties
}
For instance, let’s output all properties of user:
let user = {
  name: "John",
  age: 30,
  isAdmin: true
};
for (let key in user) {
  // keys
  alert( key );  // name, age, isAdmin
  // values for the keys
  alert( user[key] ); // John, 30, true
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Are properties ordered inside the object ?

A

The short answer is: “ordered in a special fashion”: integer properties are sorted, others appear in creation order. The details follow.

As an example, let’s consider an object with the phone codes:

let codes = {
  "49": "Germany",
  "41": "Switzerland",
  "44": "Great Britain",
  // ..,
  "1": "USA"
};

for (let code in codes) {
alert(code); // 1, 41, 44, 49
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q
let user = {
  name: "John",
  surname: "Smith"
};
user.age = 25; // add one more

for (let prop in user) {
alert( prop );
}

What would be the output ?

A
let user = {
  name: "John",
  surname: "Smith"
};
user.age = 25; // add one more
// non-integer properties are listed in the creation order
for (let prop in user) {
  alert( prop ); // name, surname, age
}

if the keys are non-integer, then they are listed in the creation order,

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q
let codes = {
  "+49": "Germany",
  "+41": "Switzerland",
  "+44": "Great Britain",
  // ..,
  "+1": "USA"
};

for (let code in codes) {
alert( +code );
}
Output ?

A
let codes = {
  "+49": "Germany",
  "+41": "Switzerland",
  "+44": "Great Britain",
  // ..,
  "+1": "USA"
};

for (let code in codes) {
alert( +code ); // 49, 41, 44, 1
}

to fix the issue with the phone codes, we can “cheat” by making the codes non-integer. Adding a plus “+” sign before each code is enough.

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

A variable assigned to an object stores not the object itself, but its “address in memory” – in other words “a reference” to it.
Is it true ?

A

Yes

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

When an object variable is copied, the reference is copied, but the object itself is not duplicated. Is it true ?

A

Yes

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q
let a = {}; // empty object 
let b = a; 

alert( a == b );
alert( a === b );
Output ?

A
let a = {}; // empty object 
let b = a; // copy the reference

alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q
let a = {};
let b = {}; // two independent objects

alert( a == b );

A
let a = {};
let b = {}; // two independent objects

alert( a == b ); // false

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

How to duplicate an object?or How to Create an independent copy, a clone? since we can not do that by simply assigning an object to another variable because it will copy the reference to the object not the object itself.

A

That’s also doable, but a little bit more difficult, because there’s no built-in method for that in JavaScript. But there is rarely a need – copying by reference is good most of the time.

But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.

Like this:

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

let clone = {}; // the new empty object

// let's copy all user properties into it
for (let key in user) {
  clone[key] = user[key];
}
// now clone is a fully independent object with the same content
clone.name = "Pete"; // changed the data in it

alert( user.name ); // still John in the original object
Also we can use the method Object.assign for that.

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

How to use object.assign to clone a object ?

A

The syntax is:

Object.assign(dest, [src1, src2, src3…])
The first argument dest is a target object.
Further arguments src1, …, srcN (can be as many as needed) are source objects.
It copies the properties of all source objects src1, …, srcN into the target dest. In other words, properties of all arguments starting from the second are copied into the first object.
The call returns dest.

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

What is deep cloning or deep copy ?

A

When an object have a nested object and we try to copy it using object assign then The nested object will be saved by reference. To prove that, if we make changes in original’s nested object, then the value would also get changed in the copy object.

for example: 
let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

To fix that, we should use a cloning loop that examines each value of user[key] and, if it’s an object, then replicate its structure as well. That is called a “deep cloning”.

We can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance _.cloneDeep(obj) from the JavaScript library lodash.

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

What is shallow copy ?

A

When we copy an object, when nested objects are copied by reference then, it is called a shallow copy. because it is not a complete clone. If we make changes in the original’s nested object, then the values will also get changed in the clone’s nested object.

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

Can const objects be modified ?

A

An important side effect of storing objects as references is that an object declared as const can be modified.

For instance:

const user = {
  name: "John"
};

user.name = “Pete”; // (*)

alert(user.name); // Pete
It might seem that the line (*) would cause an error, but it does not. The value of user is constant, it must always reference the same object, but properties of that object are free to change.

In other words, the const user gives an error only if we try to set user=… as a whole.

That said, if we really need to make constant object properties, it’s also possible, but using totally different methods. We’ll mention that in the chapter Property flags and descriptors.

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

Does automatic garbage collection happens in JS ?

A

Yes

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

What is the concept of reachibility in JS ?

A

The main concept of memory management in JavaScript is reachability.

Simply put, “reachable” values are those that are accessible or usable somehow. They are guaranteed to be stored in memory.

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

What are some base set of inherently reachable values that can not be deleted ?

A

There’s a base set of inherently reachable values, that cannot be deleted for obvious reasons.

For instance:

The currently executing function, its local variables and parameters.
Other functions on the current chain of nested calls, their local variables and parameters.
Global variables.
(there are some other, internal ones as well)
These values are called roots.

34
Q

What values are considered reachable ?

A

Any value is considered reachable if it’s reachable from a root by a reference or by a chain of references.

For instance, if there’s an object in a global variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.

There’s a background process in the JavaScript engine that is called garbage collector. It monitors all objects and removes those that have become unreachable.

35
Q

Explain reachablilty with a very simple example

A

Here’s the simplest example:

// user has a reference to the object
let user = {
  name: "John"
};

Here the arrow depicts an object reference. The global variable “user” references the object {name: “John”} (we’ll call it John for brevity). The “name” property of John stores a primitive, so it’s painted inside the object.

If the value of user is overwritten, the reference is lost:

user = null;

Now John becomes unreachable. There’s no way to access it, no references to it. Garbage collector will junk the data and free the memory.

36
Q

Explain the concept of reachability by having an example of two references

A

Now let’s imagine we copied the reference from user to admin:

// user has a reference to the object
let user = {
  name: "John"
};

let admin = user;

Now if we do the same:

user = null;
…Then the object is still reachable via admin global variable, so it’s in memory. If we overwrite admin too, then it can be removed.

37
Q

Explain the concept of reachability using interlinked objects

A

Now a more complex example. The family:

function marry(man, woman) {

woman. husband = man;
man. wife = woman;

  return {
    father: man,
    mother: woman
  }
}
let family = marry({
  name: "John"
}, {
  name: "Ann"
});
Function marry “marries” two objects by giving them references to each other and returns a new object that contains them both.

The resulting memory structure: fig 1

As of now, all objects are reachable.

Now let’s remove two references:

delete family.father;
delete family.mother.husband;

fig 2

It’s not enough to delete only one of these two references, because all objects would still be reachable.

But if we delete both, then we can see that John has no incoming reference any more:

fig 3

Outgoing references do not matter. Only incoming ones can make an object reachable. So, John is now unreachable and will be removed from the memory with all its data that also became unaccessible.

After garbage collection:

fig 4

38
Q

What is the problem of unreachable island ?

A

It is possible that the whole island of interlinked objects becomes unreachable and is removed from the memory.

The source object is the same as above. Then:

family = null;

This example demonstrates how important the concept of reachability is.

It’s obvious that John and Ann are still linked, both have incoming references. But that’s not enough.

The former “family” object has been unlinked from the root, there’s no reference to it any more, so the whole island becomes unreachable and will be removed.

39
Q

How the garbage collection algorithm works in JS ?

A

The basic garbage collection algorithm is called “mark-and-sweep”.

The following “garbage collection” steps are regularly performed:

The garbage collector takes roots and “marks” (remembers) them.
Then it visits and “marks” all references from them.
Then it visits marked objects and marks their references. All visited objects are remembered, so as not to visit the same object twice in the future.
…And so on until every reachable (from the roots) references are visited.
All objects except marked ones are removed.

For instance, let our object structure look like this: fig 1
We can clearly see an “unreachable island” to the right side. Now let’s see how “mark-and-sweep” garbage collector deals with it.

The first step marks the roots: fig 2

Then their references are marked: fig 3

…And their references, while possible: fig 4
Now the objects that could not be visited in the process are considered unreachable and will be removed: fig 5

We can also imagine the process as spilling a huge bucket of paint from the roots, that flows through all references and marks all reachable objects. The unmarked ones are then removed.

That’s the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not affect the execution.

40
Q

What optimizations are made by the JS engine for garbage collection ?

A

Some of the optimizations:

Generational collection – objects are split into two sets: “new ones” and “old ones”. Many objects appear, do their job and die fast, they can be cleaned up aggressively. Those that survive for long enough, become “old” and are examined less often.

Incremental collection – if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between them to track changes, but we have many tiny delays instead of a big one.

Idle-time collection – the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution.

There exist other optimizations and flavours of garbage collection algorithms. As much as I’d like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And, what’s even more important, things change as engines develop, so studying deeper “in advance”, without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.

41
Q

What are object methods ?

A

For a start, let’s teach the user to say hello:

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

user.sayHi = function() {
alert(“Hello!”);
};

user.sayHi(); // Hello!
Here we’ve just used a Function Expression to create a function and assign it to the property user.sayHi of the object.

Then we can call it as user.sayHi(). The user can now speak!

A function that is a property of an object is called its method.

So, here we’ve got a method sayHi of the object user.

Of course, we could use a pre-declared function as a method, like this:

let user = {
  // ...
};
// first, declare
function sayHi() {
  alert("Hello!");
};
// then add as a method
user.sayHi = sayHi;

user.sayHi(); // Hello!

42
Q

What is the shorthand for writing object methods ?

A

There exists a shorter syntax for methods in an object literal:

// these objects do the same

user = {
  sayHi: function() {
    alert("Hello");
  }
};
// method shorthand looks better, right?
user = {
  sayHi() { // same as "sayHi: function(){...}"
    alert("Hello");
  }
};
As demonstrated, we can omit "function" and just write sayHi().

To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred.

43
Q

What is ‘this’ in object methods ?

A

It’s common that an object method needs to access the information stored in the object to do its job.

For instance, the code inside user.sayHi() may need the name of the user.

To access the object, a method can use the this keyword.

The value of this is the object “before dot”, the one used to call the method.

For instance:

let user = {

name: “John”,
age: 30,

  sayHi() {
    // "this" is the "current object"
    alert(this.name);
  }

};

user.sayHi(); // John
Here during the execution of user.sayHi(), the value of this will be user.

Technically, it’s also possible to access the object without this, by referencing it via the outer variable:

let user = {

name: “John”,
age: 30,

sayHi() {
alert(user.name); // “user” instead of “this”
}

};
…But such code is unreliable. If we decide to copy user to another variable, e.g. admin = user and overwrite user with something else, then it will access the wrong object.

That’s demonstrated below:

let user = {

name: “John”,
age: 30,

sayHi() {
alert( user.name ); // leads to an error
}

};

let admin = user;
user = null; // overwrite to make things obvious

admin.sayHi(); // TypeError: Cannot read property ‘name’ of null
If we used this.name instead of user.name inside the alert, then the code would work.

44
Q

Do ‘this’ only works in the context of object methods ?

A

No. it works in different scenarios also

In JavaScript, keyword this behaves unlike most other programming languages. It can be used in any function, even if it’s not a method of an object.

There’s no syntax error in the following example:

function sayHi() {
alert( this.name );
}
The value of this is evaluated during the run-time, depending on the context.

For instance, here the same function is assigned to two different objects and has different “this” in the calls:

let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
  alert( this.name );
}

// use the same function in two objects

user. f = sayHi;
admin. f = sayHi;

// these calls have different this
// “this” inside the function is the object “before the dot”
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)

admin‘f’; // Admin (dot or square brackets access the method – doesn’t matter)
The rule is simple: if obj.f() is called, then this is obj during the call of f. So it’s either user or admin in the example above.

45
Q
function sayHi() {
  alert(this);
}

sayHi();

Output ?

A

We can even call the function without an object at all:

function sayHi() {
  alert(this);
}

sayHi(); // undefined
In this case this is undefined in strict mode. If we try to access this.name, there will be an error.

In non-strict mode the value of this in such case will be the global object (window in a browser, we’ll get to it later in the chapter Global object). This is a historical behavior that “use strict” fixes.

Usually such call is a programming error. If there’s this inside a function, it expects to be called in an object context.

46
Q

What is the difference between ‘this’ in JS and in other languages ?

A

If you come from another programming language, then you are probably used to the idea of a “bound this”, where methods defined in an object always have this referencing that object.

In JavaScript this is “free”, its value is evaluated at call-time and does not depend on where the method was declared, but rather on what object is “before the dot”.

The concept of run-time evaluated this has both pluses and minuses. On the one hand, a function can be reused for different objects. On the other hand, the greater flexibility creates more possibilities for mistakes.

Here our position is not to judge whether this language design decision is good or bad. We’ll understand how to work with it, how to get benefits and avoid problems.

47
Q

Does ‘this’ works in arrow functions also ?

A

Arrow functions are special: they don’t have their “own” this. If we reference this from such a function, it’s taken from the outer “normal” function.

For instance, here arrow() uses this from the outer user.sayHi() method:

let user = {
  firstName: "Ilya",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // Ilya
That’s a special feature of arrow functions, it’s useful when we actually do not want to have a separate this, but rather to take it from the outer context. Later in the chapter Arrow functions revisited we’ll go more deeply into arrow functions.

48
Q

Does the value of ‘this’ is defined at runtime. if yes then how ?

A

The value of this is defined at run-time.

When a function is declared, it may use this, but that this has no value until the function is called.
A function can be copied between objects.
When a function is called in the “method” syntax: object.method(), the value of this during the call is object.

49
Q

What is the need of constructor functions ?

A

The regular {…} syntax allows us to create one object. But often we need to create many similar objects, like multiple users or menu items and so on.

That can be done using constructor functions and the “new” operator.

50
Q

How constructor functions work ? Explain with an example

A

Constructor functions technically are regular functions. There are two conventions though:

They are named with capital letter first.
They should be executed only with “new” operator.
For instance:

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User(“Jack”);

alert(user.name); // Jack
alert(user.isAdmin); // false
When a function is executed with new, it does the following steps:

A new empty object is created and assigned to this.
The function body executes. Usually it modifies this, adds new properties to it.
The value of this is returned.
In other words, new User(...) does something like:
function User(name) {
  // this = {};  (implicitly)

// add properties to this

this. name = name;
this. isAdmin = false;

  // return this;  (implicitly)
}
So let user = new User("Jack") gives the same result as:
let user = {
  name: "Jack",
  isAdmin: false
};
Now if we want to create other users, we can call new User("Ann"), new User("Alice") and so on. Much shorter than using literals every time, and also easy to read.

That’s the main purpose of constructors – to implement reusable object creation code.

Let’s note once again – technically, any function (except arrow functions, as they don’t have this) can be used as a constructor. It can be run with new, and it will execute the algorithm above. The “capital letter first” is a common agreement, to make it clear that a function is to be run with new.

51
Q

What are the two conventions for constructor functions ?

A

Constructor functions technically are regular functions. There are two conventions though:

They are named with capital letter first.
They should be executed only with “new” operator.

52
Q

List the steps when a function get executed with new keyword ?

A

When a function is executed with new, it does the following steps:

A new empty object is created and assigned to this.
The function body executes. Usually it modifies this, adds new properties to it.
The value of this is returned.
In other words, new User(...) does something like:
function User(name) {
  // this = {};  (implicitly)

// add properties to this

this. name = name;
this. isAdmin = false;

  // return this;  (implicitly)
}
So let user = new User("Jack") gives the same result as:
let user = {
  name: "Jack",
  isAdmin: false
};
53
Q

How ‘new function() {..} ‘ works ?

A

If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this:

// create a function and immediately call it with new
let user = new function() {
  this.name = "John";
  this.isAdmin = false;
  // ...other code for user creation
  // maybe complex logic and statements
  // local variables etc
};
This constructor can’t be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse.
54
Q

Do constructors have return statements ?

A

Usually, constructors do not have a return statement. Their task is to write all necessary stuff into this, and it automatically becomes the result.

55
Q

If constructors have a return statement, then what rules we use ?

A

Usually, constructors do not have a return statement. Their task is to write all necessary stuff into this, and it automatically becomes the result.

But if there is a return statement, then the rule is simple:

If return is called with an object, then the object is returned instead of this.
If return is called with a primitive, it’s ignored.
In other words, return with an object returns that object, in all other cases this is returned.

For instance, here return overrides this by returning an object:

function BigUser() {

this.name = “John”;

return { name: “Godzilla” }; //

56
Q

Can we omit parenthesis while using the new operator ?

A

By the way, we can omit parentheses after new, if it has no arguments:

let user = new User; //

57
Q

How to create methods in objects using constructors ?

A

Using constructor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, and what to put in it.

Of course, we can add to this not only properties, but methods as well.

For instance, new User(name) below creates an object with the given name and the method sayHi:

function User(name) {
  this.name = name;

this.sayHi = function() {
alert( “My name is: “ + this.name );
};
}

let john = new User(“John”);

john.sayHi(); // My name is: John

/*
john = {
   name: "John",
   sayHi: function() { ... }
}
*/
58
Q

Why should constructors only be called using new keyword ?

A

Constructor functions should only be called using new. Such a call implies a creation of empty this at the start and returning the populated one at the end.

59
Q

What is the use of optional chaining ?.

A

The optional chaining ?. is a safe way to access nested object properties, even if an intermediate property doesn’t exist.

60
Q

What is the non-existing property problem that optional chaining solves ?

A

If you’ve just started to read the tutorial and learn JavaScript, maybe the problem hasn’t touched you yet, but it’s quite common.

As an example, let’s say we have user objects that hold the information about our users.

Most of our users have addresses in user.address property, with the street user.address.street, but some did not provide them.

In such case, when we attempt to get user.address.street, and the user happens to be without an address, we get an error:

let user = {}; // a user without “address” property

alert(user.address.street); // Error!
That’s the expected result. JavaScript works like this. As user.address is undefined, an attempt to get user.address.street fails with an error.

In many practical cases we’d prefer to get undefined instead of an error here (meaning “no street”).

…and another example. In Web development, we can get an object that corresponds to a web page element using a special method call, such as document.querySelector(‘.elem’), and it returns null when there’s no such element.

// document.querySelector('.elem') is null if there's no element
let html = document.querySelector('.elem').innerHTML; // error if it's null
Once again, if the element doesn’t exist, we’ll get an error accessing .innerHTML property of null. And in some cases, when the absence of the element is normal, we’d like to avoid the error and just accept html = null as the result.
61
Q

How optional chaining works ?

A

The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and returns undefined.

Further in this article, for brevity, we’ll be saying that something “exists” if it’s not null and not undefined.

In other words, value?.prop:

works as value.prop, if value exists,
otherwise (when value is undefined/null) it returns undefined.
Here’s the safe way to access user.address.street using ?.:

let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)

The code is short and clean, there’s no duplication at all.

Here’s an example with document.querySelector:

let html = document.querySelector('.elem')?.innerHTML; // will be null, if there's no element
Reading the address with user?.address works even if user object doesn’t exist:

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
Please note: the ?. syntax makes optional the value before it, but not any further.

E.g. in user?.address.street.name the ?. allows user to safely be null/undefined (and returns undefined in that case), but that’s only for user. Further properties are accessed in a regular way. If we want some of them to be optional, then we’ll need to replace more . with ?..

62
Q

Should variables be declared in order for optional chaining to work ?

A

If there’s no variable user at all, then user?.anything triggers an error:

// ReferenceError: user is not defined
user?.address;
The variable must be declared (e.g. let/const/var user or as a function parameter). The optional chaining works only for declared variables.
63
Q
let user = null;
let x = 0;

user?.sayHi(x++);

alert(x);

Output ?

A

As it was said before, the ?. immediately stops (“short-circuits”) the evaluation if the left part doesn’t exist.

So, if there are any further function calls or operations to the right of ?., they won’t be made.

For instance:
let user = null;
let x = 0;

user?.sayHi(x++); // no “user”, so the execution doesn’t reach sayHi call and x++

alert(x); // 0, value not incremented
Other

64
Q

What are the three forms of writing the optional chaining syntax ?

A

The optional chaining ?. syntax has three forms:

obj?.prop – returns obj.prop if obj exists, otherwise undefined.
obj?.[prop] – returns obj[prop] if obj exists, otherwise undefined.
obj.method?.() – calls obj.method() if obj.method exists, otherwise returns undefined.

65
Q

What is symbol type in JS ?

A

Symbol is a primitive type for unique identifiers.

Symbols are created with Symbol() call with an optional description (name).

// id is a symbol with the description "id"
let id = Symbol("id");
66
Q

Are symbol type unique ?

A

Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn’t affect anything.

For instance, here are two symbols with the same description – they are not equal:

let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

67
Q

Do symbols auto convert to a string ?

A

Most values in JavaScript support implicit conversion to a string. For instance, we can alert almost any value, and it will work. Symbols are special. They don’t auto-convert.

For instance, this alert will show an error:

let id = Symbol("id");
alert(id); // TypeError: Cannot convert a Symbol value to a string
That’s a “language guard” against messing up, because strings and symbols are fundamentally different and should not accidentally convert one into another.

If we really want to show a symbol, we need to explicitly call .toString() on it, like here:

let id = Symbol(“id”);
alert(id.toString()); // Symbol(id), now it works
Or get symbol.description property to show the description only:

let id = Symbol("id");
alert(id.description); // id
68
Q

What are the two use cases for symbol ?

A

1- Hidden object properties

2- To alter some built-in functionalities in JS

69
Q

How to create hidden properties in an object ?

A

Symbols allow us to create “hidden” properties of an object, that no other part of code can accidentally access or overwrite.

For instance, if we’re working with user objects, that belong to a third-party code. We’d like to add identifiers to them.

Let’s use a symbol key for it:

let user = { // belongs to another code
  name: "John"
};

let id = Symbol(“id”);

user[id] = 1;

alert( user[id] ); // we can access the data using the symbol as the key
What’s the benefit of using Symbol(“id”) over a string “id”?

As user objects belongs to another code, and that code also works with them, we shouldn’t just add any fields to it. That’s unsafe. But a symbol cannot be accessed accidentally, the third-party code probably won’t even see it, so it’s probably all right to do.

Also, imagine that another script wants to have its own identifier inside user, for its own purposes. That may be another JavaScript library, so that the scripts are completely unaware of each other.

Then that script can create its own Symbol(“id”), like this:

// ...
let id = Symbol("id");

user[id] = “Their id value”;
There will be no conflict between our and their identifiers, because symbols are always different, even if they have the same name.

…But if we used a string “id” instead of a symbol for the same purpose, then there would be a conflict:

let user = { name: “John” };

// Our script uses "id" property
user.id = "Our id value";

// …Another script also wants “id” for its purposes…

user.id = "Their id value"
// Boom! overwritten by another script!
70
Q

How to use a symbol in a object literal ?

A

If we want to use a symbol in an object literal {…}, we need square brackets around it.

Like this:

let id = Symbol(“id”);

let user = {
  name: "John",
  [id]: 123 // not "id": 123
};
That’s because we need the value from the variable id as the key, not the string “id”.
71
Q

Can we access symbolic properties using for…in loop ?

A

Symbolic properties do not participate in for..in loop.

For instance:

let id = Symbol("id");
let user = {
  name: "John",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name, age (no symbols)

// the direct access by the symbol works
alert( “Direct: “ + user[id] );
Object.keys(user) also ignores them. That’s a part of the general “hiding symbolic properties” principle. If another script or a library loops over our object, it won’t unexpectedly access a symbolic property.

In contrast, Object.assign copies both string and symbol properties:

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123
There’s no paradox here. That’s by design. The idea is that when we clone an object or merge objects, we usually want all properties to be copied (including symbols like id).

72
Q

What happens when objects are added obj1 + obj2, subtracted obj1 - obj2 or printed using alert(obj)?

A

JavaScript doesn’t exactly allow to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can’t implement a special object method to handle an addition (or other operators).

In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value.

That’s an important limitation, as the result of obj1 + obj2 can’t be another object!

E.g. we can’t make objects representing vectors or matrices (or achievements or whatever), add them and expect a “summed” object as the result. Such architectural feats are automatically “off the board”.

73
Q

In boolean context, all objects are what ?

True or false ?

A

All objects are true

74
Q

Which types of conversions can be done on objects ?

A

There are only numeric and string conversions.

75
Q

When numeric conversion happens on objects ?

A

The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, Date objects (to be covered in the chapter Date and time) can be subtracted, and the result of date1 - date2 is the time difference between two dates.

76
Q

When string conversion happens on objects ?

A

As for the string conversion – it usually happens when we output an object like alert(obj) and in similar contexts.

77
Q

What are hints ?

A

There are three variants of type conversion, that happen in various situations.

They’re called “hints”, as described in the specification:

78
Q

Example for object to string conversion ?

A
// output
alert(obj);
// using object as a property key
anotherObj[obj] = 123;
79
Q

Example for object to number conversion

A

For an object-to-number conversion, like when we’re doing maths:

// explicit conversion
let num = Number(obj);
// maths (except binary plus)
let n = +obj; // unary plus
let delta = date1 - date2;
// less/greater comparison
let greater = user1 > user2;
80
Q

What is default hint ?

A

Occurs in rare cases when the operator is “not sure” what type to expect.

For instance, binary plus + can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if a binary plus gets an object as an argument, it uses the “default” hint to convert it.

Also, if an object is compared using == with a string, number or a symbol, it’s also unclear which conversion should be done, so the “default” hint is used.

// binary plus uses the "default" hint
let total = obj1 + obj2;
// obj == number uses the "default" hint
if (user == 1) { ... };
The greater and less comparison operators, such as < >, can work with both strings and numbers too. Still, they use the "number" hint, not "default". That’s for historical reasons.

In practice though, we don’t need to remember these peculiar details, because all built-in objects except for one case (Date object, we’ll learn it later) implement “default” conversion the same way as “number”. And we can do the same.

81
Q

Is boolean hint also exists ?

A

Please note – there are only three hints. It’s that simple.

There is no “boolean” hint (all objects are true in boolean context) or anything else. And if we treat “default” and “number” the same, like most built-ins do, then there are only two conversions.