Book Image

Building Applications with Spring 5 and Vue.js 2

By : James J. Ye
Book Image

Building Applications with Spring 5 and Vue.js 2

By: James J. Ye

Overview of this book

Building Applications with Spring 5 and Vue.js 2, with its practical approach, helps you become a full-stack web developer. As well as knowing how to write frontend and backend code, a developer has to tackle all problems encountered in the application development life cycle – starting from the simple idea of an application, to the UI and technical designs, and all the way to implementation, testing, production deployment, and monitoring. With the help of this book, you'll get to grips with Spring 5 and Vue.js 2 as you learn how to develop a web application. From the initial structuring to full deployment, you’ll be guided at every step of developing a web application from scratch with Vue.js 2 and Spring 5. You’ll learn how to create different components of your application as you progress through each chapter, followed by exploring different tools in these frameworks to expedite your development cycle. By the end of this book, you’ll have gained a complete understanding of the key design patterns and best practices that underpin professional full-stack web development.
Table of Contents (23 chapters)
Title Page
Copyright and Credits
Dedication
About Packt
Contributors
Preface
Index

JavaScript from a Java developer's viewpoint


For readers who are new to JavaScript but who are familiar with the Java language, here are some differences between the two languages that may confuse you. And even though this section is written from a Java developer's perspective, if you're new to JavaScript, you will also find it informative.

Functions and methods

A function in JavaScript is quite different from a method in Java because it is actually an object created by theFunctionconstructor, which is a built-in object of the language. Yes, that's right.Functionitself is an object too. What is a method in JavaScript, then? When a function is a property of an object, it is a method. So, in JavaScript, a method is a function, but not all functions are methods.

Since a function is an object, it can also have properties and methods. To establish whether an object is a function or not, you can useinstanceofas follows:

var workout = function () {};
console.log(workout instanceof Function); // true

What is the difference between a function and other objects in JavaScript, apart from the fact that it is created by theFunctionconstructor? First of all, a function is callable, while other objects are not. Another difference is that a function has a prototype property while other objects don't. We will talk aboutprototypelater.

In JavaScript, you can use a function to create objects with new. In a case such as this, that function serves as a constructor. As a convention, when a function serves as a constructor, it should be capitalized. The following is a simple example of using a function as a User constructor. We will build the User constructor containing more details later:

function User () {
}
var user = new User();

Before we move on, let's see the different ways to create a function in JavaScript. Function declarations and function expressions are the most common ways to create functions. Other than that, you can usenew Function()to create a function. However, this is not recommended due to its poor performance as well as its readability. TheUserfunction in the preceding code snippet is a function declaration. Andworkout is a function expression. The way that a function is created and invoked will affect the execution context that its function body will point to. We will talk about it later.

Objects and classes

In Java, you create a class to represent a concept, for example, a User class. TheUserclass has a constructor, some fields, and methods. And you use its constructor to instantiate aUserobject. And every object in Java is an instance of the associated class that provides code sharing among its instances. You can extend theUserclass to create, for example, aTeamMemberclass.

In JavaScript, there are several ways to create an object:

  • TheObject()constructor method
  • The object literal method
  • The constructor function method
  • The Object.create()method
  • Thecreator function method
  • The ES6 class method

Let's look at each method one at a time.

TheObjectconstructor method looks like this:

// Call the Object constructor with new
var user = new Object();
user.name = 'Sunny';
user.interests = ['Traveling', 'Swimming'];
user.greeting = function () {
  console.log('Hi, I\'m ' + this.name + '.');
};
user.greeting(); // Hi, I'm Sunny.

TheObjectconstructor creates an object wrapper. This is not a recommended approach, even though it is valid in JavaScript. In practice, it is better to use an object literal instead, which makes the code compact.

The object literal method looks like this:

// Create a user with object literal
var user = {
name: 'Sunny',
interests: ['Traveling', 'Swimming'],
greeting: function () {
console.log('Hi, I\'m ' + this.name + '.');
}
}
user.greeting();// Hi, I'm Sunny.

The object literal is a compact syntax to create an object in JavaScript and it is the recommended way of creating an object overnew Object(). Starting from ES5, object literals also support getter and setter accessors, as can be seen here:

var user = {
get role() {
return 'Engineer';
}
} 
user.role;// Engineer

And if you try to assign a value torole, it will stay unchanged because there is no setter accessor defined for the role property.

The constructor function method looks like this:

// Create a constructor function
function User (name, interests) {
this.name = name;
this.interests = interests;
this.greeting = function () {
console.log('Hi, I\'m ' + this.name + '.');
}
}
// Call the constructor with new to create a user object
var user = new User('Sunny', ['Traveling', 'Swimming']);
user.greeting(); // Hi, I'm Sunny.

This syntax is very close to the one in Java. JavaScript is very tolerant, and you can omit the parenthesis when calling the constructor. However, this will not pass any arguments to the constructor, as can be seen here:

var user = new User;
console.log(user.name); // undefined

And again, even though this is valid in JavaScript, it is not recommended to omit the parenthesis.

TheObject.create()method looks like this:

// Use Object.create() method with the prototype of
// User constructor function created above
var user = Object.create(User.prototype, {
name: { value: 'Sunny' },
interests: { value: ['Traveling', 'Swimming']}
}); 
user.greeting(); // Uncaught TypeError: user.greeting() is not a //function

The reasongreeting()is not a function of the user object here is that theObject.create()method creates a new object with the constructor's prototype object. And the greetingfunction is not defined inUser.prototype, or passed in the second argument of Object.create(). To make the user be able to greet, we can either pass the greeting function in the second argument, or we can add it to theUserconstructor's prototype object. The difference is that the first approach only adds the greeting function to the current user object. If you created another user without passing in the greeting function, that user won't have greeting function. On the other hand, adding the function to the prototype object will add the greeting function to all the objects created by that constructor. Let's add it to the User prototype object:

// Add greeting to prototype object
User.prototype.greeting = function () {
console.log('Hi, I\'m ' + this.name + '.');
}
user.greeting(); // Hi, I'm Sunny.

Actually, using a prototype is how a superclass provides methods for subclasses to inherit in JavaScript. We will talk about that in detail later.

The creator function method looks like this:

// Use a creator function with an object as its return value
function createUser (name, interests) {
var user = {};
user.name = name;
user.interests = interests;
user.greeting = function () {
console.log('Hi, I\'m ' + this.name + '.');
};
return user;
}
// Call the creator function with parameters
var user = createUser('Sunny', ['Traveling', 'Swimming']);
user.greeting(); // Hi, I'm Sunny.

The creator function here is a factory method, similar to the static factory method that used to instantiate an object in Java. And it is merely a pattern because underneath it wraps the object creation details inside of the creator function.

The ES6 class method looks like this:

// Create User class
class User {
// Equivalent to User constructor function
constructor (name, interests) {
this.name = name;
this.interests = interests;
}
// Equivalent to User.prototype.greeting
greeting () {
console.log('Hi, I\'m ' + this.name + '.')
}
}
let user = new User('Sunny', ['Traveling', 'Swimming']);
user.greeting(); // Hi, I'm Sunny.

This is very close to the syntax in Java. Instead of using the class declaration, you can also use the class expression to create the class, as follows:

// Use class expression
let User = class {
constructor (name, interests) {
this.name = name;
this.interests = interests;
}
greeting () {
console.log('Hi, I\'m ' + this.name + '.')
} 
}

 

Even though it uses the same keyword, class, class in JavaScript is quite different from the class in Java. For example, there is no static class and no private class in JavaScript. We will talk more aboutclass in the ES6 section.

Objects, properties, and property attributes

In Java, once an object is created, there is (almost) no way to modify its methods during runtime. Java is not a dynamic language. In JavaScript, things are quite different. You can create an object and modify it easily during runtime, such as adding new properties and replacing a method. That's what a dynamic language can do. Actually, that is not the special part. The special part is thatObjectis a language type in JavaScript, like other language types that JavaScript has, which includesUndefined, Null, Boolean, String, Symbol, and Number. Any value in JavaScript is a value of those types.

Note

The undefined type has a single value,undefined. The null type has a single value,null. A Boolean has two values:trueandfalse.

In Java, an object has fields and methods. In JavaScript, an object is logically a collection of properties. A property has a name of the String type and a list of attributes. Attributes, in JavaScript, are used to define and explain the state of a property. There are two types of properties—data properties and access properties.

A data property has four attributes:

  • value, which can be of any JavaScript language type
  • writable, which defines whether a data property can be changed or not
  • enumerable, which defines whether a property can be enumerated by using a for-in statement
  • configurable, which defines whether a property can be deleted, changed to be an access property, changed to be not writable, or whether itsenumerableattribute can be modified

An access property also has four attributes:

  • get accessor, which can be a Function object or undefined
  • set accessor, which can be a Function object or undefined
  • enumerable, which defines whether a property can be enumerated by using a for-in statement
  • configurable, which defines whether a property can be deleted, be changed to be a data property, or whether its other attributes can be modified.

To access a property of an object, you can use dot notation or bracket notation. The dot notation acts the same as how it does in Java. The bracket notation, on the other hand, is quite interesting. In JavaScript, property names must be strings. If you try to use a non-string object as a property name with bracket notation, the object will be casted into a string via itstoString()method, as we can see here:

var obj = {};
obj['100'] = 'one hundred';
// Number 100 will be casted to '100'
console.log(obj[100]);// 'one hundred'
// Both foo and bar will be casted to string '[object Object]'
var foo = {prop: 'f'}, bar = {prop: 'b'};
obj[foo] = 'Foo'
console.log(obj[bar])// 'Foo'

In a nutshell, here is how an object appears, logically:

Figure 1.1: Object, properties, and property attributes

In JavaScript,you can useObject.definePropertyorObject.definePropertiesto modify the properties of an object. Here is how it works:

1.  function User (name, department) {
2.    var _department = department;
3.    var _name = name;
4.    Object.defineProperty(this, 'name', {
5.      value: _name,
6.      writable: true,
7.      enumerable: true,
8.      configurable: false
9.    }); 
10.   Object.defineProperty(this, 'department', {
11.     get: function () {
12.       console.log('Retrieving department');
13.       return _department;
14.     },
15.     set: function (newValue) {
16.       console.log('Updating department value to "' + newValue + '"');
17.       _department = newValue;
18.     },
19.     enumerable: true,
20.     configurable: true
21.   });
24.   Object.defineProperty(this, 'greeting', {
25.     value: function () {
26.       console.log('Hi, I\'m ' + _name + '.');
27.     },
28.     enumerable: false,
29.     configurable: false
30.   }); 
31. } 

As you can see from lines 4 to 9, we use Object.defineProperty to define name as a data property, and its actual data is stored in the internal property _name. In lines 10 to 21, we define department as an access property that has a get accessor and set accessor, and the actual value is kept in _department. In lines 24 to 30, we define greeting property as a data property and its value is a Function object:

32. var user = new User('Sunny', 'Engineering');
33. console.log(user.department);
34. user.department = 'Marketing'; 
35. user.greeting(); 
36. Object.defineProperty(user, 'name', { 
37.   enumerable: false
38. });
39. delete user.name;
40. delete user.department;
41. for (var prop in user) {
42.   console.log(prop);
43. }

In line 32, we create a user object using the User constructor function. In line 33, we access the department property. Since it is a get accessor, the getter function will be invoked and the message Retrieving department will show up in the console before the actual department value. In line 34, we assign a new value to the department property. Since we have defined the set accessor, the setter function will be invoked. In line 35, since the greeting property of user object is defined as a function, we will need to invoke it. In lines 36 to 38, we try to redefine the name property. However, since it is not configurable, JavaScript will throw an error with this. The same is true with regard to line 39, where we try to delete this property. The deletion of department property in line 40 will work since it is configurable. In lines 41 to 43, the only property that will show up in the console is the name property, because the department has been deleted and the greeting property is not enumerable.

Prototype and inheritance

As has been briefly mentioned previously, inheritance in JavaScript is archived by using prototypes of constructor functions. In JavaScript, a prototype is an object that provides shared properties for other objects. And only a function object has a prototype because only a function object is callable and can create other objects. In ES6, arrow functions don't have prototypes. We will discuss that later.

You can think of a function as a factory and its prototype is the specification of the products that the factory manufactures. Every time you call a function with thenewkeyword, you place an order for its product. And the factory will produce it according to how it is specified in the prototype.

Now, let's see how inheritance works in code. We will create another constructor function, calledTeamMemberand it will inherit properties fromUserand also override thegreeting()method and provide a new method calledwork(). Later, we will addeat()method toUserandmove()toObject.

Here is how it is implemented in ES5:

1.function User (name, interests) {
2.this.name = name;
3.this.interests = interests;
4.}
5.User.prototype.greeting = function () {
6.console.log('Hi, I\'m ' + this.name + '.');
7. }

In lines 1 to 4, we create a User constructor function. And what it really does is create a function object using the Function constructor. In JavaScript, you can check who created an object by using its constructor property, which references back to its creator, as follows:

console.log(User.constructor === Function);// true

And once the User constructor function is created, it has a prototype object. And a User prototype object itself is created by the User constructor function, as you can see in the following:

console.log(User.prototype.constructor === User); // true

And in JavaScript, after you create a user object using the User constructor function, that object will have a __proto__ property that references the User prototype object. You can see the link like this:

var user = new User();
console.log(user.__proto__ === User.prototype); // true 

This __proto__ reference serves as a link in the prototype chain. You will see what that means visually later.

Now back to the code. In lines 5 to 7, we create a greeting property on the User prototype. This will create a method that can be inherited by User subclasses. And, as we mentioned earlier, if you define the greeting method inside the User constructor function, subclasses won't see this greeting method. We will see the reason for this shortly:

8.  function TeamMember (name, interests, tasks) {
9.   User.call(this, name, interests);
10.  this.tasks = tasks;
11. }
12. TeamMember.prototype = Object.create(User.prototype);
13. TeamMember.prototype.greeting = function () {
14. console.log('I\'m ' + this.name + '. Welcome to the team!');
15. };
16. TeamMember.prototype.work = function () {
17. console.log('I\'m working on ' + this.tasks.length + ' tasks');
18. };

In lines 8 to 13, we create a TeamMember constructor function, and inside it, we invoke the User constructor function's call() method, which is inherited from the Function object to chain constructors, which is similar to invoking super() in a constructor of a Java class. One difference is that the call() method's first argument must be an object, which serves as the execution context. In our example, we use this as the execution context. Inside the call() method, thename and interests properties are initialized. And then, we add an additional property, tasks, to TeamMember.

In line 12, we use Object.create() to create a TeamMember prototype object using the User prototype object. In this way, objects created by the TeamMember constructor function will have the properties of the User prototype object and each team member object will have a __proto__ property that links to this TeamMember prototype.

In lines 13 to 15, we override the original greeting() method of the User prototype so that objects created by the TeamMember constructor function will have different behavior. This will not affect the User prototype object since they are essentially two different objects, even though these two prototype objects have the same constructor, as you can see in the following:

console.log(User.prototype === TeamMember.prototype);// false
console.log(User.prototype.constructor === TeamMember.prototype.constructor); // true

In lines 16 to 18, we add a new method, work()to the TeamMember prototype object. In this way, objects created by the TeamMember constructor function will have this additional behavior:

19. var member = new TeamMember('Sunny', ['Traveling'],
20. ['Buy three tickets','Book a hotel']);
21. member.greeting();// I'm Sunny. Welcome to the team!
22. member.work();// I'm working on 2 tasks
23
24. console.log(member instanceof TeamMember); // true
25. console.log(member instanceof User); // true
26. console.log(member instanceof Object); // true
27
28. User.prototype.eat = function () {
29. console.log('What will I have for lunch?');
30. };
31. member.eat(); // What will I have for lunch? 
32
33. // Add a method to the top
34. Object.prototype.move = function () {
35. console.log('Every object can move now');
36. };
37. member.move();// Every object can move now
38. var alien = {};
39. alien.move(); // Every object can move now
40. User.move();// Even the constructor function

In line 19, we create a member object using theTeamMemberconstructor function. Line 21shows that the member object can greet in a different way to objects created by theUserconstructor function. And line 22shows that the member object can work.

Lines 24 to 26 show that the member object is an instance of all its superclasses.

In lines 28 to 30, we add theeat()method to theUserprototype, and even though the member object is created before this, as you can see from line 31, it also inherits that method.

In line 34, we add themove()method to theObjectprototype, which might turn out to be a really bad idea since, as you can see in lines 37 to 40, every object can move now, even those constructor function objects.

We just create an inheritance chain starting from Object | User | TeamMember. The prototype link is the key to this chain. Here is how it appears:

Figure 1.2: Prototype-based inheritance

On the left-hand side are the constructor functions, and on the right-hand side are their corresponding prototypes. The bottom is themember object. As you can see, the member object's __proto__ property references the prototype object of TeamMember. And the __proto__ property of the TeamMember prototype object itself references the prototype object of User. And the __proto__ property of the User prototype object references the top level, which is the prototype object of ObjectTo verify the link, you can do something like this:

console.log(member.__proto__ === TeamMember.prototype); // true
console.log(TeamMember.prototype.__proto__ === User.prototype); // true
console.log(User.prototype.__proto__ === Object.prototype); // true

So, be really careful with the __proto__ property. If you, let's say, accidentally change this property to something else, the inheritance will break:

User.prototype.__proto__ = null;
member.move(); // Uncaught TypeError: member.move is not a function 
console.log(member instanceof Object); // false (Oops!)

It is recommended to useObject.prototype.isPrototypeof()to check the prototype chain:

TeamMember.prototype.isPrototypeOf(member); // true

With the inheritance relationship map showing in the preceding diagram, you can easily see how JavaScript resolves a property through the prototype chain. For example, when you access a member object's name property, JavaScript finds that it is on the object itself and will not go up the chain. And when you access themove()method, JavaScript will go up the chain and check whether the TeamMemberprototype has it and, since it doesn't, JavaScript will keep going up until it finds the method in the Objectprototype. You can use an object'shasOwnProperty()method to check whether that object has a property as its own instead of inherited through the prototype chain:

member.hasOwnProperty('name'); // true
member.hasOwnProperty('move'); // false

Scope and closure

Scope is about the accessibility of variables. In Java, basically, a set of curly brackets {} defines a scope, including class-level scope, method-level scope, and block-level scope. 

Let's take a look at the following example in Java:

1.public class User { 
2.  private String name; 
3.private List<String> interests; 
4.
5.public User (String name, List<String> interests) {
6.this.name = name;
7.this.interests = interests;
8.}
9. 
10. // Check if a user is interested in something
11. public boolean isInterestedIn(String something) {
12. boolean interested = false;
13. for (int i = 0; i < interests.size(); i++) {
14. if (interests.get(i).equals(something)) {
15. interested = true;
16. break;
17. } 
18. } 
19. return interested;
20. }
21. }

The name and interests properties are in the class-level scope and they are accessible anywhere within the class. Theinterested variable, defined in line 12, is in method-level scope, and it is only accessible within that method. Thei variable, in line 13, is defined within the for loop and it is block-level scope only accessible within the for loop block. In Java, the scope of the variables is static and can be determined by the compiler.

In JavaScript, the scope of the variables is much more flexible. There is global scope and function scope, and block scope with theletandconst keywords, which were introduced in ES6, which we will talk about later.

Let's look at the following JavaScript example:

1.function bookHotel (city) {
2.var availableHotel = 'None';
3.for (var i=0; i<hotels.length; i++) {
4.var hotel = hotels[i];
5.if (hotel.city === city && hotel.hasRoom) {
6.availableHotel = hotel.name;
7.break;
8.}
9.}
10. // i and hotel still accessible here
11. console.log('Checked ' + (i+1) + ' hotels');// Checked 2 hotels
12. console.log('Last checked ' + hotel.name);// Last checked Hotel B
13. {
14. function placeOrder() {
15. var totalAmount = 200;
16. console.log('Order placed to ' + availableHotel);
17. }
18. } 
19. placeOrder();
20. // Not accessible
21. // console.log(totalAmount);
22. return availableHotel;
23. }
24. var hotels = [{name: 'Hotel A', hasRoom: false, city: 'Sanya'},
{name: 'Hotel B', hasRoom: true, city: 'Sanya'}];
25. console.log(bookHotel('Sanya')); // Hotel B
26. // Not accessible
27. // console.log(availableHotel);

Thehotels variabledeclared in line 24,is in global scope and is accessible anywhere, such as inside thebookHotel()function, even though the variable is defined after the function. 

TheavailableHotelvariable declared in line 2 is in the scope of the bookHotel()function. It is a local variable and is not accessible outside of the function, as you can see from line 27. Inside its enclosing function, the availableHotelvariable is accessible anywhere, even the nestedplaceOrder() function, as you can see in line 16. This is called closure. A closure is formed when a function is nested inside another function. And no matter how deeply you have nested a function, it will still have access to its parent function's scope, and all the way to the top scope, which is global scope. ThetotalAmount variable, defined in line 15, is a local variable of theplaceOrder()function.

And in lines 3 and 4, we defined theiandhotel variables with thevar keyword. Even though it is in a for loop block, it is still accessible outside the block, as shown in lines 11 and 12. In ES6, you can use the let keyword to define i and hotel, which will put these two variables in for loop block scope. We will talk more about this later.

The this keyword

In Java,thisalways refers to the current object. It is solid. In JavaScript, this behaves differently. In short, this refers to the current execution context, which is an object. And the way that JavaScript runtime determines the current execution context is much more complex than in Java.

In JavaScript, there is an execution context stack, logically formed from active execution contexts. When control is transferred from one executable code to another, control enters the new executable code's execution context, which becomes the current execution context, or what is referred to as the running execution context. At the bottom of the stack is the global context, where everything begins, just like the main method in Java. The current execution context is always at the top of the stack.

What is the executable code? There are three types in JavaScript:

  • Global code, which is the code that runs from the place where a JavaScript program starts. In a browser, it is wherewindowlives. And when you open a browser console and type invar user = new User(), you are writing global code.
  • Eval code, which is a string value passed in as the argument of the built-ineval()function (do not use theeval()function unless you really know what you're doing).
  • Function code, which is the code parsed as the body of a function. However, it doesn't mean all the code written inside a function is function code.

Now, to understand this better, let's look at the following example:

1.function User (name) {
2.console.log('I\'m in "' + this.constructor.name + '" context.');
3.this.name = name;
4.this.speak = function () {
5.console.log(this.name + ' is speaking from "' +
6.this.constructor.name + '" context.');
7.var drink = function () {
8.console.log('Drinking in "' + this.constructor.name + '"');
9.} 
10. drink(); 
11. }; 
12. function ask() {
13. console.log('Asking from "' + 
14. this.constructor.name + '" context.');
15. console.log('Who am I? "'+ this.name + '"');
16. }
17. ask();
18. }
19. var name = 'Unknown';
20. var user = new User('Ted');
21. user.speak();

Note

Since an execution context is, in fact, an object, here we use its .constructor.name to see what the context is. And if you run the preceding code in the node command line, it will be Object instead of Window.

If you run the code from Chrome console, the output will be the following:

// I'm in "User" context.
// Asking from "Window" context.
// Who am I? "Unknown"
// Ted is speaking from "User" context.
// Drinking in "Window"

First, let's see which part is global code and which part is function code.TheUserfunction declaration, and lines 19 to 21, are global code.Lines2 to 17 are the function code of theUserfunction. Well, not exactly. Lines 5 to 10, except line 8, are the function code of thespeak()method. Line 8 is the function code of thedrink()function. Lines 13 and 14 are the function code of theask()function.

Before we review the output, let's revisit the two commonly used ways of creating a function—function declarations and function expressions. When the JavaScript engine sees a function declaration, it will create afunctionobject that is visible in the scope in which the function is declared. For example, line 1 declares theUserfunction, which is visible in global scope. Line 12 declares theask()function, which is visible inside the scope of theUserfunction. And line 4 is a function expression that creates thespeak()method. On the other hand, in line 7, we use a function expression to create adrinkvariable. It is different from the speak() method created in line 4. Even though it is also a function expression, thedrinkvariable is not a property of an object. It is simply visible inside thespeak()method.

In JavaScript, scope and execution context are two different concepts. Scope is about accessibility, while the execution context is about the ownership of running an executable code. Thespeak()method and theask()function are in the same scope, but they have different execution contexts. When theask()function is executed, as you can see from the output, it has global context and thenameproperty resolves to the valueUnknown, which is declared in global scope. And when thespeak()method is executed, it has theusercontext. As you can see from the output, its access to thenameproperty resolves toTed. This can be quite confusing to Java developers. So what happened behind the scenes?

Let's review the preceding example from the JavaScript engine's view.When the JavaScript engine executes line 20, it creates auserobject by calling theUserconstructor function. And it will go into the function body to instantiate the object. When the control flows from the global code to the function code, the execution context is changed to theuserobject. And that's why you seeI'm in "User" context.in the output. And during the instantiation, JavaScript engine will not execute the code inside thespeak()method because there is no invoking yet. It executes theask()function when it reaches line 17. At that time, the control flows from the function code of theUserconstructor function to theask()function. Because theask()function isn't a property of an object, nor it is invoked by theFunction.call()method, which we will talk about later, the global context becomes the execution context. And that's why you seeAsking from "Window" context.andWhere am I? "Unknown"in the output. After the instantiation of the user object, the JavaScript engine goes back to execute line 21 and invokes thespeak()method on theuserobject. Now, the control flows into thespeak()method and theuserobject becomes the execution context. And that's why you seeTed is speaking from "User" context.in the output. When the engine executes the drink()function, it resolves back to the global context as the execution context. And that is why you seeDrinking in "Window" context.in the output.

As mentioned earlier, the execution context is affected by the way a function is created as well as by how it is invoked. What does that mean?Let's change line 16 fromask()toask.call(this). And if you run the preceding example again from Chrome's console, you can see the following output:

...
Asking from "User" context.
Who am I? "Ted"
...

And if you type inuser.speak.apply({name: 'Jack'}) into the console, you will see something interesting, like this:

Jack is speaking from "Object" context.
Drinking in "Window" context.

Or, if you change line 17 toask.bind(this)(), you can see the answer to the question"Who am I?"is also"Ted"now.

So, what are thesecall(),apply(), andbind()methods? It seems that there are no definitions of them in the preceding example. As you might remember, every function is an object created by theFunctionobject. After typing in the following code into the console, you can see that thespeak()function inherits the properties fromFunction prototype, including thecall(),apply(), andbind()methods:

console.log(Function.prototype.isPrototypeOf(user.speak)); // true
user.speak.hasOwnProperty('apply');// false
user.speak.__proto__.hasOwnProperty('apply');// true

Thecall()method and theapply()method are similar. The difference between these two methods is that thecall()method accepts a list of arguments, while theapply()method accepts an array of arguments. Both methods take the first argument as the execution context of the function code. For example, in user.speak.apply({name: 'Jack'}), the {name: 'Jack'} object will be the execution context of the speak() method of user. You can think of thecall()andapply()methods as a way of switching execution context.

And thebind()method acts differently from the other two. What thebind()method does is create a new function that will be bound to the first argument that is passed in as the new function’s execution context. The new function will never change its execution context even if you usecall()orapply()to switch execution context. So, whatask.bind(this)()does is create a function and then execute it immediately. Besides executing it immediately, you can assign the new function to a variable or as a method of an object.

To wrap up, there are four ways to invoke a function:

  • Constructor function invoking:new User()
  • Direct function invoking:ask()
  • Method invoking:user.speak()
  • Switching context invoking:ask.call(this)orask.apply(this)

When we are talking about constructor function invoking, the presence of this inside the function body, except those instances that are wrapped by functions of the other three types of invoking, refers to the object that the constructor creates.

When we are talking about direct function invoking, the presence of this inside the function body, except those instances that are wrapped by functions of the other three types of invoking, refers to the global context.

When we are talking about method invoking, the presence of this inside the function body, except those instances that are wrapped by functions of the other three types of invoking, refers to the object that the method belongs to.

When we are talking about switching context invoking, the presence of this inside the function body, except those instances that are wrapped by functions of the other three types of invoking, refers to the object that passed in as the first argument of thecall()method.

Hoisting

This is another thing that Java developers usually easily get confused. Hoisting is a metaphor for the way that JavaScript interpreters will lift function declarations and variable declarations to the top of their containing scope. So, In JavaScript, you can see something that is obviously wrong and will definitely break the compilation if you write that in Java, but it is totally valid in JavaScript.

Let’s see an example:

1. travel = 'No plan';
2. var travel;
3. console.log(travel); // Is the output: undefined?
4.
5. function travel() {
6. console.log('Traveling');
7. }
8. travel(); // Is the output: Traveling?

What will the output be when the JavaScript engine executes line 3 and 8? It is notundefined, and notTraveling. Line 3 is "No plan" and line 8 is"Uncaught TypeError".

Here is what the JavaScript interpreter sees when it processes the preceding code:

1.// Function declaration moved to the top of the scope
2.function travel() {
3.console.log('Traveling');
4.}
5.// Variable declaration moved under function declaration
6.var travel;
7.travel = 'No plan';
8.
9.console.log(travel);// No plan
10. travel();// Uncaught TypeError: travel is not a function

JavaScript interpreter moves the function declarations up to the top, followed by variables declarations. Function expressions, for example,var travel = function(){}, are not lifted to the top as function declarations because they are also variable declarations.

Let's see another example:

1.function workout() { 
2.goToGym();// What will the output be?
3.var goToGym = function () {
4.console.log('Workout in Gym A');
5.}
6.return; 
7.function goToGym() {
8.console.log('Workout in Gym B');
9.}
10. }
11. workout();

What will the output be when line 2 is executed? It is"Workout in Gym B.". And here is what the interpreter sees when it processes the code:

1.function workout() {
2.function goToGym() {
3.console.log('Workout in Gym B');
4.}
5.var goToGym;
6.goToGym();
7.goToGym = function () {
8.console.log('Workout in Gym A');
9.}
10. return;
11. }
12. workout();

The interpreter moves the function declaration to the top of the scope and then the variable declaration, but not the assignment. So whengoToGym()is executed, the assignment expression to the new function hasn't happened yet.

To wrap up, before executing, JavaScript interpreters will move the function declarations, and then variable declarations, without assignment expressions, up to the top of the containing scope. And it is valid to put function declarations after the return statement.