JavaScript is an implementation of the ECMAScript specification. This section focuses on sorting out commonly tested knowledge points in ECMAScript, and then analyzing some easily arising questions.
Knowledge Points
Variable Types
Classification and judgment of JS data types
Value types and reference types
Prototype and prototype chain (inheritance)
Definition of prototype and prototype chain
Inheritance Syntax
Scope and Closure
Execution Context
this
What is a closure?
Asynchronous
Synchronous vs Asynchronous
synchrony and single-threaded
Front-end asynchronous scenarios
Examination of ES6/7 new standards
Arrow functions
Module
Class
Set and Map
Promise
Variable Types
JavaScript is a weakly-typed scripting language, that is, it doesnât require specifying the type when you define a variable; the type will be automatically determined during program execution.
ECMAScript defines six primitive types:
Boolean
String
Number
Null
Undefined
Symbol (newly defined in ES6)
Note: The primitive types do not include Object.
Topic: What methods are used for type judgment?
typeof
The values obtained by typeof xxx
are of the following types: undefined
, boolean
, number
, string
, object
, function
, symbol
. Itâs pretty straightforward, so no need for individual demonstrations. There are three points to note here:
The result of
typeof null
is o`bject; actually, this is a bug of typeof, null is primitive value, not a reference type.The result
of typeof [1, 2]
isobject
, there is noarray
in the result. Reference types are all objects except functions.The value obtained by using
typeof Symbol()
for thesymbol
type issymbol
, which is a new knowledge point added in ES6.
instanceof
It is used for correspondence between instances and constructors. For example, to determine if a variable is an array, typeof
cannot be used, but you can use [1, 2] instanceof Array
to judge. Because [1, 2]
is an array
, its constructor is Array
. Similarly:
|
|
Topic: Difference between value types and reference types
Value type vs Reference type
In addition to primitive types, ES also has reference types. Among the types identified by typeof
mentioned above, only object
and function
are reference types, others are value types.
Based on the way variable types are passed in JavaScript, it is divided into value
types and reference
types. Value types include Boolean
, String
, Number
, Undefined
, Null
, and reference
types include all classes of the Object
category, such as Date
, Array
, Function
, etc. In terms of parameter passing, value types are passed by value and reference types are passed by sharing.
Letâs look at the main differences between the two and what needs to be noticed in practical development through a small question below.
|
|
In the above code, both a and b are value types. They are assigned and modified separately, and there is no influence on each other. Now letâs look at an example of reference type:
|
|
In the above code, both a
and b
are reference types. After executing b = a
, when the property value of b
is modified, the value of a
also changes. This is because both a and b
are reference types, pointing to the same memory address, i.e., they both reference the same value. Therefore, when b
modifies the property, the value of a
changes accordingly.
Letâs further explain this with the help of the question below.
State the execution result of the following code and analyze the reason.
|
|
Through the execution of the code, you will find that:
The value of a does not change
But the value of b has changed
This is because the Number
type a
is passed by value, while the Object
type b
is passed by sharing.
The reason for this design in JS is: for types that are passed by value, a
copy is stored in stack memory. This type of type generally does not occupy too much memory, and passing by value ensures its access speed. The types that are passed by sharing are copying its reference, not copying its value (pointer in C language), which ensures that oversized objects and others will not cause memory waste due to constant copying of content.
Reference types are often used in code in the following way, or they are prone to unnoticed errors!
|
|
Although obj itself is a variable of reference type (object), both a
and b
inside it are value types and reference types respectively. Assigning a
value to a will not change obj.a
, but operations on b
will be reflected on the obj object.
Prototype and prototype chain
JavaScript is a language based on prototypes, understanding prototypes is quite simple, but extremely important. Letâs understand the concept of prototypes in JavaScript through questions.
Topic: How to understand the prototypes in JavaScript
To answer this question, you can remember and understand several key points:
All reference types (
arrays
,objects
,functions
) have object characteristics, that is, they can freely extend properties (exceptnull
).All reference types (
arrays
,objects
,functions
) have a__proto__
property, the value of which is a normal object.All functions have a prototype property, the value of which is also a normal object.
All reference types (arrays, objects, functions), the value of the
__proto__
property points to the prototype property value of its constructor.
Letâs explain this with code, and you can run the following code to see the results:
|
|
Prototypes
Letâs start with a simple example code.
|
|
Executing printName
is easy to understand, but what happened when executing alertName
? Remember another key point here. When trying to get a certain property of an object, if the object itself does not have this property, it will go to its __proto__
(that is, its constructorâs prototype) to find it, so f.alertName
will find Foo.prototype.alertName
.
So how do you determine whether this property is the objectâs own property? Use hasOwnProperty
, a common place is when traversing an object.
|
|
Topic: How to understand the prototype chain in JS
Prototype Chain
Continuing with the above example, what happens when f.toString() is executed?
|
|
Because f itself does not have toString(), and there is no toString in f.__proto__
(that is, Foo.prototype
). Recall the sentence just now â when trying to get a certain property of an object, if the object itself doesnât have this property, it will look for it in its __proto__
(that is, its constructorâs prototype).
If toString is not found in f.__proto__
, continue to look for it in f.__proto__.__proto__
, because f.__proto__
is just an ordinary object!
f.__proto__
is Foo.prototype
, which does not find toString, keep looking up.
f.__proto__.__proto__
is Foo.prototype.__proto__
. As Foo.prototype
is just an ordinary object, so Foo.prototype.__proto__
is Object.prototype
, where toString can be found.
Therefore, f.toString ultimately corresponds to Object.prototype.toString
.
If you keep looking up like this, youâll find itâs a chain-like structure, so itâs called a âprototype chainâ. If it cannot be found all the way to the top level, it declares failure and returns undefined. What is the top level â Object.prototype.proto === null
.
this in the Prototype Chain
All the methods obtained and executed from the prototype or higher levels of prototypes, the this
within them when executing, points to the object that triggers the event. Thus, the this in both printName
and alertName
is f
.
Scope and Closure
Scope and closure are the most likely knowledge points to be tested in front-end interviews. For example, the following question:
Topic: Now there is a piece of HTML code. The requirement is to write code. When you click on a link with a certain number, an alert pops up with its number.
|
|
If you donât know that this question involves closure, you might write the following code:
|
|
In fact, you will find that it always pops up 6 when executed. At this point, you should solve it through closures:
|
|
To understand closures, we need to start with the âexecution contextâ.
Execution Context
Letâs talk about a knowledge point about variable hoisting. In the interview, you may encounter the following question, where many candidates give wrong answers:
Topic: Tell the result of the following execution (the author here directly commented the output)
|
|
Before a piece of JS script (i.e., within a <script>
tag) is executed, it needs to be parsed first (hence, JS is an interpreted script language). During parsing, a global execution context environment is created first, taking all the variables and function declarations that are about to be executed in the code (internal function is not included because you donât know when the function will be executed). Variables are temporarily assigned to undefined
, and functions are declared to be available. After this step is done, the program is officially executed. Emphasize again, this is the work that starts before the code execution.
Letâs look at the small test question above. Why is a
undefined
, but b
throws an error. In fact, JS will âparse the entire documentâ before the code is executed. It finds var a
, knows there is a variable a
, and stores it in the execution context. But b
didnât find the var keyword, so it didnât âreserve spaceâ in the execution context in advance. So when the code is executed, the early-present a
has a record, but the value hasnât been assigned yet, which is undefined
. While b
is not found in the execution context and naturally throws an error (b
reference not found).
In addition, before a function is executed, a function execution context environment is also created, which is similar to the global context, but the function execution context adds this
, arguments, and function parameters. Itâs easy to understand parameters and arguments
, but we need to specifically explain this
.
To sum up:
Range: a
<script>
tag, a JS file, or a functionGlobal context: variable definition, function declaration
Function context: variable definition, function declaration, this, arguments
this
Firstly, understand a very important concept â the value of this
can only be confirmed during execution, not during definition! Why? â Because this
is part of the execution context, and the execution context needs to be determined before the code is executed, not when it is defined. As shown in the following example.
|
|
this
will have different execution results, mainly focused on the following scenarios:
As a constructor, within the constructor.
As an object property, as in the above code
a.fn()
.As a regular function, as in the above code
fn1()
.Used for call, apply, bind, like in the above code
a.fn.call({name: âBâ})
.
Now letâs explain what is the scope and scope chain, scope chain and scope are also frequently asked questions.
Topic: How to understand the scope and scope chain in JS
Scope
Before ES6, JS didnât have block-level scopes. For example:
|
|
From the above example, we can understand the concept of scope, which is an independent territory, so that variables will not leak out and be exposed. The name above has been exposed, so JS doesnât have block-level scopes, only global scope and function scope.
|
|
Global scope is the outermost scope. If we write many lines of JS code and the variable definitions are not enclosed within functions, then they are all in the global scope. The drawback of this is that it is very prone to collisions and conflicts.
|
|
This is why all the code in jQuery, Zepto, and other libraries are placed in (function() {âŠ})()
. Because all the variables inside will not be leaked and exposed, they wonât pollute the outside, and they wonât affect other libraries or JS scripts. This is a manifestation of function scope.
Note: ES6 started to introduce block-level scope, which can be defined by using let, as follows:
|
|
Scope Chain
First, letâs understand what is called a âfree variableâ. In the following code, console.log(a)
needs to get the variable a
, but a
is not defined in the current scope (compare with b
). A variable that is not defined in the current scope is called a âfree variableâ. How to get a
âfree variableâ? â Look for it in the parent scope.
|
|
What if the parent scope doesnât have it either? It continues to look up step by step until it reaches the global scope. If it still hasnât found it, it gives up. This step-by-step relationship is called the âscope chainâ.
|
|
Closure
Iâve explained these concepts, letâs look at an example to understand closures.
|
|
Free variables will be located from the scope chain, but they rely on the scope chain at the time of function definition, not the execution of the function, the above example is a closure. Closures mainly have two use cases:
The function is returned as a value, as in the previous example.
The function is passed as a parameter, as shown in the example below.
|
|
At this point, looking back at the beginning â âScope and Closureâ which is a part of click and pop up alert code viewing closures, it will be easy to understand.
Asynchronous
Asynchronous and Synchronous are also commonly tested topics in interviews, the author will explain the difference between synchronous and asynchronous below.
Synchronous vs Asynchronous
Consider the following demo; according to the programâs expressed intent, it should first print 100, print 200 after a second, and finally print 300. But in actual operation, thatâs not the case at all.
|
|
Comparatively, the following program first prints 100, then displays 200 (wait for user confirmation), and finally prints 300. This execution effect meets the expected requirements.
|
|
Whatâs the difference between these two? â The first exampleâs intermediate steps do not block the subsequent programâs operations at all, while the second example does block the subsequent operations. The former behavior is known as Asynchronous (the latter is called Synchronous), which does not block the subsequent program execution.
Asynchronicity and Single Threaded
The fundamental reason that JS requires asynchronicity is because JS is single-threaded, which means it can only do one thing at a time and canât lie in two boats ~ do two things at once.
An Ajax request takes 5 seconds due to slow network speed. If it is synchronous, the page will be stuck here for those 5 seconds and nothing can be done. If itâs asynchronous, itâs much better as the 5 seconds waiting wonât delay anything else. That 5-second waiting period is due to a slow network speed, not because of JS.
Speaking of single-threaded, letâs look at a real exam question:
Topic: Explain the execution process and result of the following code.
|
|
This is a very puzzling question, numerous candidates assumed that 100ms later, since a becomes false, the while loop will stop. However, this is not the case. Because JavaScript is single-threaded, once it enters the while loop, it doesnât have the âtimeâ (or thread) to run the timer. Thus, running this code results in an infinite loop!
Front-end asynchronous scenarios
Timing functions: setTimeout, setInterval
Network request, like Ajax,
<img>
loading
Ajax code example
|
|
img code example (commonly used for dot statistics)
|
|
Examination of ES6/7 new standards
Topic: Whatâs the difference between
this
in ES6 arrow functions and in regular functions?
Arrow functions
Arrow functions are a new way of defining functions in ES6. A function like function name(arg1, arg2) {âŠ}
can be defined using (arg1, arg2) => {âŠ}
. Examples are as follows:
|
|
The purpose of arrow functions is twofold. Firstly, itâs more concise to write. Secondly, it can solve the problem that this is a global variable when functions are executed before ES6. See the following code:
|
|
Topic: How to use ES6 modules?
Module
The syntax of module in ES6 is simpler, directly see examples. If you only output a unique object, use export default. The code is as followsïŒ
|
|
If you want to output multiple objects, you canât use default
, and you need to add {âŠ}
when you import
. The code is as follows:
|
|
Topic: Whatâs the difference between ES6 âclassâ and regular constructor functions?
Class
class has always been a keyword (reserved word) in JS, but it has not been formally used until ES6. The âclassâ in ES6 is to replace the previous method of initializing objects with constructor functions, which is more object-oriented in syntax. For example:
JS constructor function syntax:
|
|
ES6 class syntax:
|
|
Consider the following points, all about class syntax:
class is a new syntax form, itâs
class Name {âŠ}
, which is completely different from function syntaxComparing the two, the content of the constructor function body should be placed in the
constructor
function in the class,constructor
is the constructor, it is executed by default when initializing the instance.The way to write functions in class is
add() {âŠ}
, without the function keyword.
Using class to implement inheritance is much simpler, at least simpler than implementing inheritance with constructor functions. See the following examples:
JS constructor function implements inheritance
|
|
The way class is used in ES6 to implement inheritance
|
|
Notice the following two points:
Using
extends
can implement inheritance, which is more in line with the syntax of classical object-oriented languages, like Java.The
constructor
of the subclass must executesuper()
to call theconstructor
of the superclass.
Topic: What new data types have been added in ES6?
Set and Map
Set and Map are new data structures added in ES6. They extend the current JS array and object, the two important data structures. As they are newly added data structures, they are not widely used currently, but as a front-end programmer, it is necessary to understand them in advance. Letâs summarize the key points of both:
Set is similar to an array, but arrays allow elements to be duplicated, whereas Set does not allow duplicate elements.
Map is similar to an object, but regular objectâs keys must be strings or numbers, whereas keys of a Map can be of any data type.
Set
A Set instance does not allow duplicate elements, which can be demonstrated in the following example. A Set instance can be initialized from an array or by adding elements using add. Duplicate elements will be ignored.
|
|
Attributes and methods of a Set instance include:
size: Get the number of elements.
add(value): Adds an element and returns the Set instance itself.
delete(value): Deletes an element and returns a boolean indicating whether the deletion was successful.
has(value): Returns a boolean indicating whether the value is an element of the Set instance.
clear(): Clears all elements without a return value.
|
|
There are various methods to iterate over a Set instance:
keys(): Returns an iterator of key names.
alues(): Returns an iterator of key values. However, as Set structures do not have key names, only key values (or say key names and key values are the same), keys() and values() result in the same values.
entries(): Returns an iterator of key-value pairs.
forEach(): Iterates over each member using a callback function.
|
|
Map
The usage of Map is basically the same as an ordinary object. Letâs first look at its feature of using non-string or non-number as a key.
|
|
You need to use new Map()
to initialize an instance. In the following code, set, get, has, and delete are self-explanatory (this will also be demonstrated later). map.set(obj, âOKâ) here is using object as the key (not only can it be an object, but any data type is allowed), and map.get(obj) correctly retrieves it later.
The attributes and methods of a Map instance are as follows:
sizeïŒGets the number of members.
setïŒSets memberâs key and value.
getïŒGets the memberâs attribute value.
hasïŒChecks whether the member exists.
deleteïŒDeletes a member.
clearïŒClears all.
|
|
The methods to iterate over a Map instance are:
keys(): Returns an iterator of key names.
values(): Returns an iterator of key values.
entries(): Returns an iterator of all members.
forEach(): Iterates over all members of the Map.
|
|
Promise
Promise is a specification proposed by CommonJS. It has multiple versions and it has been included in the ES6 standard. ES6 natively supports Promise objects, and libraries like Bluebird and Q can be used for support in non-ES6 environments.
Promise can make callback calls appear as chained calls, making the process more clear and the code more elegant.
In short, promise can be summarized as three statuses, two processes, and one method. A quick way to remember is â3â2â1â:
Three statuses: pending, fulfilled, rejected
Two processes:
pending to fulfilled (resolve)
pending to rejected (reject)
One method: then
Of course, there are other concepts like catch, Promise.all/race, but we will not expand on them here.