Be insensitive, and keep a lifelong growth mindset.

0%

Advanced JavaScript: Objects and Functions

1. Everything is and Object: Inheritance and the Prototype Chain

Everything is and Object

1.1. Object-Oriented Programming

  • Objects interacting with one another through methods and properties;
  • Used to store data, structure applications into modules and keeping code clean.
    Constructors and Instances in JavaScript

1.2. Inheritance in General

Inheritance in General

1.3. Inheritance in JavaScript: Prototypes and Prototype Chains

The inheritance in javaScript depends on “Prototype” property.
Inheritance in JavaScript: Prototypes and Prototype Chains

1.4. Summary

  • Every JavaScript object has a prototype property, which makes inheritance possible in JavaScript
  • The prototype property of an object is where we put methods and properties that we want other objects to inherit.
  • The Constructor’s prototype property is NOT the prototype of the Constructor itself, it’s the prototype of ALL instances that are created through it.
  • When a certain method (or property) is called, the search starts in the object itself, and if it cannot be found, the search moves on to the object’s prototype. This continues until the method is found: prototype chain.

2. Creating Objects: Function Constructors

The first way is to define all properties and functions in constructor:

1
2
3
4
5
6
7
8
9
10
11
12
//Approach 1: only use constructor
var Person = function(name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
this.calculateAge = function() {
console.log(2016 - this.yearOfBirth);
}
}

var john = new Person('John', 1990, 'teacher');
john.calculateAge();

The alternative way is to use prototype property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Approach 2: use prototype property
var Person = function(name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
this.calculateAge = function() {
console.log(2016 - this.yearOfBirth);
}
}

Person.prototype.calculateAge =
function() {
console.log(2016 - this.yearOfBirth);
};

Person.prototype.lastName = 'Smith';

var john = new Person('John', 1990, 'teacher');
var jane = new Person('Jane', 1969, 'designer');
var mark = new Person('Mark', 1948, 'retired');

john.calculateAge();
jane.calculateAge();
mark.calculateAge();

console.log(john.lastName);
console.log(jane.lastName);
console.log(mark.lastName);

3. The Prototype Chain in the Console

In console, if we type in ‘john’, we can see the john object gets printed.

Person Object

From the above screenshot, we can see Person’s prototype is an Object, and also

1
john._proto_ === Person.prototype //Return true

Screenshot 2

From the second screenshot, we can see that because of the inheritance, the john object can call hasOwnProperty method. Obviously, job is john’s own property, but lastName is not.

Another method to know is instanceof, we can know john is an instance of Person object.

Here’s an additional example:

Array Object

By typing console.info(x), we can see this array object. If you click on the proto, you can see all methods available in array.

Array Object 2

4. Creating Objects: Object.create

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Alternative way to create object: Object.create
var personProto = {
calculateAge: function() {
console.log(2016 - this.yearOfBirth);
}
}

var john = Object.create(personProto);
john.name = 'John';
john.yearOfBirth = 1990;
john.job = 'teacher';

var jane = Object.create(personProto,
{
name: { value: 'Jane'},
yearOfBirth: { value: 1969 },
job: { value: 'designer'}
});

Object.create builds an object that inherits directly from the one that we passed into the firs argument.

While, on the other hand, the function constructor, the newly created object inherits from constructor’s prototype property.

Actually, one of the biggest benefits of object.create is that it allows us to implement a really complex inheritance structures in an easier way, because it allows us to directly specify which object should be a prototype.

Read more: Understanding the difference between Object.create() and new SomeFunction()

5. Primitives vs Objects

Variables containing primitives actually hold that data inside of the variable itself.

However, variables associated with objects do not actually contain the object, but instead, they contain a reference to the place in memory where the object is stored.

So again, a variable declared as an object does not have a real copy of the object, it just points to that object.

See the example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 23;
var b = a;
a = 46;
console.log(a);
console.log(b);

var obj1 = {
name: 'John',
age: 26
};

var obj2 = obj1;
obj1.age = 30;

console.log(obj1.age);
console.log(obj2.age);

In console, we can see the print out as follows

1
2
3
4
46
23
30
30

It shows that actually obj1 and obj2 are pointing to the same object, once obj1.age gets changed, it reflects to obj2.age as well.

Another example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var age = 27;
var obj = {
name: 'Jonas',
city: 'Lisbon'
}

function change (a, b) {
a = 30;
b.city = 'San Francisco';
}

change(age, obj);

console.log(age);
console.log(obj.city);

When we pass a primitive into a function, a simple copy is created. You can change a as much as you want, but it will never affect the variable on the outside.

For the object, instead of passing the object to the function, we just pass in a reference.

6. First Class Functions: Passing Functions as Arguments

6.1. Functions are Also Objects in JavaScript

  • A function is an instance of the Object type;
  • A function behaves like any other object;
  • We can store functions in a variable;
  • We can pass a function as an argument to another function;
  • We can return a function from a function

Because of all above, we say that in JavaScript, we have First-Class Functions.

6.2. Code Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var years = [1990, 1965, 1937, 2005, 1998];

function arrayCalc(arr, fn) {
var arrRes = [];
for (var i = 0; i < arr.length; i++) {
arrRes.push(fn(arr[i]));
}
return arrRes;
}

function caculateAge(el) {
return 2016 - el;
}

function isFullAge(el) {
return el >= 18;
}

function maxHeartRate(el) {
if (el >= 18 && el <= 81) {
return Math.round(206.9 - (0.67 * el));
} else {
return -1;
}
}

var ages = arrayCalc(years, calculateAge);
var fullAges = arrayCalc(years, isFullAge);
var rates = arrayCalc(ages, maxHeartRate);

console.log(ages);
console.log(fullAges);
console.log(rates);

7. First Class Functions: Functions Returning Functions

Functions can also be returned from functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function interviewQuestion(job) {
if (job ==== 'designer') {
return function(name) {
console.log(name + ', can you please explain what UX design is?');
}
} else if (job === 'teacher') {
return function(name) {
console.log('What subject do you teach, ' + name + '?');
}
} else {
return function(name) {
console.log('Hello ' + name + ', what do you do?');
}
}
}

var teacherQuestion = interviewQuestion('teacher');
var designerQuestion = interviewQuestion('designer');

teacherQuestion('John');
designerQuestion('John');

//An alternative way to call
interviewQuestion('teacher')('Mark');

8. Immediately Invoked Function Expressions (IIFE)

Here we use a silly game to demonstrate how IIFE works,

1
2
3
4
5
6
7
8
9
(function () {
var score = Math.random() * 10;
console.log(score >= 5);
})();

(function (goodLuck) {
var score = Math.random() * 10;
console.log(score >= 5 - goodLuck)
})(5);

IIFE can only be called once and has the advantage of data privacy, because unlike defining a function and assigning it to a variable, IIFE can not be reused elsewhere.

9. Closures

9.1. Code Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function retirement(retirementAge) {
var a = ' years left until retirement.';
return function(yearOfBirth) {
var age = 2016 - yearOfBirth;
console.log((retirementAge - age) + a);
}
}

var retirementUS = retirement(66);
var retirementGermany = retirement(65);
var retirementIceland = retirement(67);

retirementUS(1990);
retirementGermany(1990);
retirementIceland(1990);

9.2. Closures Summary

An inner function has always access to the variables and parameters of its outer function, even after the outer function has returned.

9.3. How Closures Work

Closures Summary

After the retirement function returns, the execution context of retirement function is gone from the Execution Stack. However, in the scope chain, the retirement function is still there and keeps working. Therefore, we can access the variables that we were created in the retirement function long after the function has completed execution, and after its execution context is gone.

9.4. Rewrite Interview Question Problem

1
2
3
4
5
6
7
8
9
10
11
12
13
function interviewQuestion(job) {
return function(name) {
if (job ==== 'designer') {
console.log(name + ', can you please explain what UX design is?');
} else if (job === 'teacher') {
console.log('What subject do you teach, ' + name + '?');
} else {
console.log('Hello ' + name + ', what do you do?');
}
}
}

interviewQuestion('teacher')('John');

10. Bind, Call and Apply

10.1. Examples on Bind, Call and Apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var john = {
name: 'John',
age: 26,
job: 'teacher',
presentation: function(style, timeOfDay) {
if (style === 'formal') {
console.log('Good ' + timeOfDay + ', Ladies and gentlemen! I\'m ' + this.name + ', I\'m a ' + this.job + ' and I\'m ' + this.age + ' years old.');
} else if (style === 'friendly') {
console.log('Hey! What\'s up? I\'m ' + this.name + ', I\'m a ' + this.job + ' and I\'m ' + this.age + ' years old. Have a nice ' + timeOfDay + '.');
}
}
}

var emily = {
name: 'Emily',
age: 35,
job: 'designer'
}

john.presentation('formal', 'morning');

john.presentation.call(emily, 'friendly', 'afternoon'); // Call

john.presentation.apply(emily, ['friendly', 'afternoon']); //Apply

var johnFriendly = john.presentation.bind(john, 'friendly'); //Bind

johnFriendly('morning');
johnFriendly('night');

var emilyFormal = john.presentation.bind(emily, 'formal'); //Bind
emilyFormal('afternoon');

10.2. Rewrite the Age Calculation Problem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var years = [1990, 1965, 1937, 2005, 1998];

function arrayCalc(arr, fn) {
var arrRes = [];
for (var i = 0; i < arr.length; i++) {
arrRes.push(fn(arr[i]));
}
return arrRes;
}

function caculateAge(el) {
return 2016 - el;
}

function isFullAge(limit, el) {
return el >= limit;
}

var ages = arrayCalc(years, calculateAge);
var fullJapan = arrayCalc(ages, isFullAge.bind(this, 20));

console.log(ages);
console.log(fullJapan);

11. Code Challenge: Build a Fun Quiz Game in the Console

11.1. Basic Level

  1. Build a function constructor called Question to describe a question. A question should include:
    a) question itself
    b) the answers from which the player can choose the correct one (choose an adequate data structure here, array, object, etc.)
    c) correct answer (I would use a number for this)

  2. Create a couple of questions using the constructor

  3. Store them all inside an array

  4. Select one random question and log it on the console, together with the possible answers (each question should have a number) (Hint: write a method for the Question objects for this task).

  5. Use the ‘prompt’ function to ask the user for the correct answer. The user should input the number of the correct answer such as you displayed it on Task 4.

  6. Check if the answer is correct and print to the console whether the answer is correct ot nor (Hint: write another method for this).

  7. Suppose this code would be a plugin for other programmers to use in their code. So make sure that all your code is private and doesn’t interfere with the other programmers code (Hint: we learned a special technique to do exactly that).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
(function() {
function Question(question, answers, correct) {
this.question = question;
this.answers = answers;
this.correct = correct;
}

Question.prototype.displayQuestion = function() {
console.log(this.question);

for (var i = 0; i < this.answers.length; i++) {
console.log(i + ': ' + this.answers[i]);
}
}

Question.prototype.checkAnswer = function(ans) {
if (ans === this.correct) {
console.log('Correct answer!');

} else {
console.log('Wrong answer. Try again :)')
}
}

var q1 = new Question('Is JavaScript the coolest programming language in the world?',
['Yes', 'No'],
0);

var q2 = new Question('What is the name of this course\'s teacher?',
['John', 'Micheal', 'Jonas'],
2);

var q3 = new Question('What does best describe coding?',
['Boring', 'Hard', 'Fun', 'Tediuos'],
2);

var questions = [q1, q2, q3];

var n = Math.floor(Math.random() * questions.length);

questions[n].displayQuestion();

var answer = parseInt(prompt('Please select the correct answer.'));

questions[n].checkAnswer(answer);
})();

11.2. Expert Level

  1. After you display the result, display the next random question, so that the game never ends (Hint: write a function for this and call it right after displaying the result)

  2. Be careful: after Task 8, the game literally never ends. So include the option to quit the game if the user writes ‘exit’ instead of the answer. In this case, DON’T call the function from task 8.

  3. Track the user’s score to make the game more fun! So each time an answer is correct, add 1 point to the score (Hint: I’m going to use the power of closures for this, but you don’t have to, just do this with the tools you feel more comfortable at this point).

  4. Display the score in the console. Use yet another method for this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
(function() {
function Question(question, answers, correct) {
this.question = question;
this.answers = answers;
this.correct = correct;
}

Question.prototype.displayQuestion = function() {
console.log(this.question);

for (var i = 0; i < this.answers.length; i++) {
console.log(i + ': ' + this.answers[i]);
}
}

Question.prototype.checkAnswer = function(ans, callback) {
var sc;

if (ans === this.correct) {
console.log('Correct answer!');
sc = callback(true);
} else {
console.log('Wrong answer. Try again :)');
sc = callback(false);
}

this.displayScore(sc);
}

Question.prototype.displayScore = function(score) {
console.log('Your current score is: ' + score);
console.log('------------------------------');
}


var q1 = new Question('Is JavaScript the coolest programming language in the world?',
['Yes', 'No'],
0);

var q2 = new Question('What is the name of this course\'s teacher?',
['John', 'Micheal', 'Jonas'],
2);

var q3 = new Question('What does best describe coding?',
['Boring', 'Hard', 'Fun', 'Tediuos'],
2);

var questions = [q1, q2, q3];

function score() {
var sc = 0;
return function(correct) {
if (correct) {
sc++;
}
return sc;
}
}
var keepScore = score();


function nextQuestion() {

var n = Math.floor(Math.random() * questions.length);
questions[n].displayQuestion();

var answer = prompt('Please select the correct answer.');

if(answer !== 'exit') {
questions[n].checkAnswer(parseInt(answer), keepScore);

nextQuestion();
}
}

nextQuestion();

})();