The introduction
Javascript is the focus of front-end interview, this article focuses on sorting out the common knowledge points in Javascript, and then some easy to appear topics for analysis. Due to the limited space of this article, it is impossible to explain everything. This article only lists some key points and difficulties. If you want to know more, please click on my blog.
Variable types
1. Data type classification of JS
According to the way variable types are passed in JavaScript, there are basic data types and reference data types. The basic data types include Undefined, Null, Boolean, Number, String and Symbol (newly added in ES6, indicating unique values), while the reference data types are collectively called Object objects, mainly including objects, arrays and functions.
In the way of parameter transmission, there are differences:.
-
If the parameter of a function is of a simple type, a numeric copy of the value type is passed inside the function. The inside of the function does not affect the parameter variable passed outside the function
-
If a parameter is a reference type, the address value of the reference type is copied to the parameter passed to the function, and internal changes to the function affect the passing
Title: The difference between primitive and reference types
Primitive types and reference types are stored in different locations in memory. Primitive types are stored directly in the stack, while reference objects are stored in the heap. At the same time, Pointers are stored in the stack, and this pointer points to the starting position of the entity in the heap. Let’s take a look at the main differences between the two through a short topic:
// Basic type var a = 10 var b = a b = 20 console.log(a) // 10 console.log(b) // 20Copy the code
In the above code, both a and B are value types, and they are assigned separately without any effect on each other. Look at an example of a reference type:
Var a = {x: 10, y: 20} var b = a b.x = 100 b.y = 200 console.log(a) // {x: 100, y: 200} console.log(b) // {x: 100, y: 200} console.log(b) // {x: 10, y: 20} 100, y: 200}Copy the code
In the above code, a and B are reference types. After b = a, change the value of b’s attribute and change that of A. Because both a and B refer to the same memory address, that is, they refer to the same value. Therefore, when B modifies the attribute, the value of A changes accordingly
2. Judgment of data type
1) typeof
Typeof returns a string representing the data type, including number, Boolean, string, symbol, object, undefined, function, etc., but null, array, etc
typeof Symbol(); // symbol valid typeof' '; // string Valid typeof 1; // number valid typeoftrue; // Boolean valid typeof undefined; //undefined valid typeof new Function(); //functionEffective typeof null; //object Invalid typeof []; //object Invalid typeof new Date(); //object Invalid typeof new RegExp(); / / object is invalidCopy the code
2) instanceof
Instanceof is an instanceof B used to determine whether A is an instanceof b. the expression A instanceof B returns true if A is an instanceof B, false otherwise. The instanceof operator tests whether an object has a constructor’s Prototype property in its prototype chain, but it cannot detect null and undefined
[] instanceof Array; //true{} instanceof Object; //truenew Date() instanceof Date; //true
new RegExp() instanceof RegExp//trueNull instanceof null instanceof undefined// ErrorCopy the code
3) constructor
Constructor is very similar to Instanceof. Unlike Instanceof, however, constructor can also handle detection of primitive data types. However, the constructor of the function is unstable, which is mainly reflected in the rewriting of the class prototype. In the process of rewriting, it is possible to overwrite the previous constructor, which results in inaccurate detection.
4) the Object. The prototype. ToString. Call ()
Object. The prototype. ToString. Call () is the most commonly used the most accurate way.
Object.prototype.toString.call(' '); // [object String] Object.prototype.toString.call(1) ; // [object Number] Object.prototype.toString.call(true); // [object Boolean] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call(new Date()) ; // [object Date] Object.prototype.toString.call([]) ; // [object Array] Object.prototype.toString.call(new RegExp()) ; // [object RegExp] Object.prototype.toString.call(new Error()) ; // [object Error]Copy the code
3. Shallow copy and deep copy
Shallow copy copies only Pointers to an object, not the object itself, and the old and new objects still share the same memory.
Shallow copy (for details, see shallow copy and deep copy) :
- Object.assign() : Note that if the target Object has only one layer, it is a deep copy
- Array.prototype.concat()
- Array.prototype.slice()
A deep copy is a copy of all reference structures of the data. To put it simply, if two data structures are identical and independent in memory, the reference type is copied instead of just the reference relationship.
Deep copy implementation:
- The popular lodash library also offers _. CloneDeep for deep copy
- Jquery provides a $.extend that can be used for deep copies
- JSON.parse(JSON.stringify())
- Handwritten recursive method
Recursively deep copy works: to copy a piece of data, we must iterate over its properties. If the properties of the object are still objects, we continue to use this method, and so on.
// Define a function that detects data typesfunction checkedType(target) {
returnObject. The prototype. ToString. Call (target). Slice (8, 1)} / / realize the depth of cloning - Object/arrayfunction clone(target) {// Determine the type of data to be copied // Initialize the variable result to become the final cloned datalet result,
targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
returnTarget} // Iterate over the target datafor (let i inTarget) {// Get the value of each item traversed through the data structure.letValue = target[I] // Check whether each value in the target structure has an object/arrayif (checkedType(value) === 'Object' || checkedType(value) === 'Array'Result [I] = result[I] = result[I] = result[I] =clone(value)
} else{// Get value is a basic data type or function. result[i] = value } }return result
}
Copy the code
Scope and closure
1. Execution context and execution stack
An execution context is an abstraction of the context in which the current JavaScript code is being parsed and executed. Any code that runs in JavaScript runs in an execution context. The execution context life cycle consists of three phases: the creation phase → the execution phase → the reclamation phase. We focus on the creation phase.
The create phase (when a function is called, but before any of its internal code is executed) does three things:
- Create a variable object: first initialize the function arguments and promote the function declaration and variable declaration.
- Create scope chains: this is described below
- Make sure this refers to: described below
function test(arg){// 1. Parameter arg is"hi"// 2. Function declarations take precedence over variable declarationsfunction
console.log(arg);
var arg = 'hello'; // 3. Var arg variable declaration ignored, arg ='hello'Be performedfunction arg(){
console.log('hello world')
}
console.log(arg);
}
test('hi'); / * output:function arg() {
console.log('hello world');
}
hello
*/
Copy the code
This is because when a function executes, it first forms a new private scope and then follows the following steps:
- If the parameter is tangible, the parameter is assigned first
- For private scope preinterpretation, function declarations take precedence over variable declarations, which are eventually overwritten by the former, but can be reassigned
- Code in private scopes executes from top to bottom
Each time a function is called, a new execution context is created. How do you manage the creation of so many execution contexts?
The JavaScript engine creates an execution stack to manage the execution context. You can think of the execution stack as a stack structure for storing function calls, following the principle of first in, last out.
- JavaScript executes on a single thread and all code is queued.
- When the browser executes global code, it first creates the global execution context, pushing it to the top of the execution stack.
- Each time a function is executed, the execution context of the function is created and pushed to the top of the stack. After the execution of the current function completes, the execution context of the current function goes off the stack and waits for garbage collection.
- The browser’s JS execution engine always accesses the execution context at the top of the stack.
- There is only one global context, which is pushed out when the browser closes.
2. Scope and scope chain
JavaScript has global scope, function scope, and block-level scope (new in ES6). We can think of it this way: the scope is an independent site, so that variables do not leak out, exposed. In other words, the greatest use of a scope is to isolate variables. Variables of the same name in different scopes do not conflict. Before introducing scope chains, let’s look at free variables. In the following code, console.log(a) returns variable A, which is not defined in the current scope (see b). Variables that are not currently defined in scope become free variables.
var a = 100
function fn() {var b = 200 console.log(a)} fn() {var b = 200 console.log(a)} fn() {var b = 200 console.log(a)} fn()Copy the code
How to get the value of the free variable – look for the parent scope (the parent scope that created the function). What if the parent doesn’t have either? Search up and down until you find the global scope. If you don’t find it, you give up. This layer by layer relationship is the scope chain.
function F1() {
var a = 100
return function () {
console.log(a)
}
}
function F2(f1) {
var a = 200
console.log(f1())
}
var f1 = F1()
F2(f1) // 100
Copy the code
In the code above, the value of the free variable A is looked up from the function F1 instead of F2, because when the free variable is looked up from the scope chain, it is based on the scope chain when the function is defined, not when the function is executed.
3. What are closures
A closure is an abstract concept in JavaScript. As I understand it, a closure is a function within a function (not in any other language). A function inside a function has access to variables of an outside function that are part of the inside function.
What closures do:
- Closures are used to access variables in functions.
- Variables can be kept in memory for a long time and have a long life cycle.
Closures should not be abused because they can leak memory and affect web page performance. Immediately after the closure is used, the resource is freed and the reference variable is pointed to NULL.
Closures have two main applications:
- Function passed as argument (see scope section example)
- Function as return value (as in the following example)
function outer() {var num = 0return function add() {// passreturnReturn add, which can be accessed outside outer. Console. log(num)}} var func1 = outer() // func1() Func2 = outer() func2() func2() func2() func2() func2() func2(Copy the code
4
It is important to understand that the value of this is checked at execution time, not at 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. Here’s an example:
/ / 1function foo() {console.log(this.a) //1} var a = 1 foo() // case 2function fn(){ console.log(this); } var obj={fn:fn}; obj.fn(); //this->objfunctionCreateJsPerson(name,age){//this is an instance of the current class. //=>p1.name=name this.age=age; //=>p1.age=age } var p1=new CreateJsPerson("Wah Chi Yin",48);
// 情况4
function add(c, d){
returnthis.a + this.b + c + d; } var o = {a:1, b:3}; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34"btn1"> Arrow function this</button> <scripttype="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
btn1.onclick = () => {
console.log(this);//obj
};
}
};
obj.getName();
</script>
Copy the code
Let’s explain each of these cases one by one
- For calling foo directly, no matter where foo is placed, this must be window, right
- For obj.foo(), we just need to remember that whoever called the function is this, so this in the foo function is the obj object in this scenario
- In the constructor pattern, the occurrence of this. XXX = XXX in the class (in the function body) is an instance of the current class
- Call, apply, and bind: this is the first argument
- The arrow function this points to: the arrow function does not have its own this. If it does, this of the outer function is the this of the inner arrow function. If not, this is the window.
Three, asynchronous
1. Synchronous vs. asynchronous
Synchronization, as I understand it, is a linear mode of execution that cannot be crossed. For example, after talking, I eat, after eating, I look at my mobile phone, and I have to wait for the last thing to be finished before I carry out the following things.
Asynchrony is a method of parallel processing where other tasks can be performed without waiting for a program to complete. For example, a person eating, looking at the phone, while talking, is the way of asynchronous processing. A result processed asynchronously in a program usually uses a callback function to process the result.
// synchronize console.log(100) alert(200); console.log(300) //100 200 300Copy the code
/ / asynchronous console. The log (100).setTimeout(function(){
console.log(200)
})
console.log(300) //100 300 200
Copy the code
2. Asynchronous and single-threaded
The fundamental reason why JS needs to be asynchronous is that JS runs single-threaded, that is, only one thing can be done at a time, not “two things at once”. In order to make use of the computing power of multi-core CPU, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and cannot operate DOM. So, this new standard doesn’t change the single-threaded nature of JavaScript.
An Ajax request takes 5 seconds because the network is slow. If it’s syncing, the page gets stuck in there for 5 seconds and can’t do anything. Asynchronous words, it is much better, 5 seconds wait for waiting, other things do not delay, as for the 5 seconds wait is the network speed is too slow, not because of JS reasons.
3. Front-end asynchronous scenario
The front-end uses asynchronous scenarios
- Scheduled task: setTimeout, setInterval
- Web request: Ajax request, dynamically loaded
- event
4.Event Loop
A complete Event Loop process can be summarized as the following stages:
-
When the execution stack is empty, we can think of the execution stack as a stack structure that stores function calls, following the principle of first in, last out. The micro queue is empty, and the Macro queue has one and only one script.
-
The global context (script tag) is pushed onto the execution stack to synchronize code execution. During execution, the task is determined to be synchronous or asynchronous, and new macro-tasks and micro-tasks can be generated by calling some interfaces, which are pushed into their respective task queues. The script will be removed from the macro queue after the synchronized code is executed. This process is essentially the execution and dequeuing of the macro task in the queue.
-
In the previous step, we assigned a macro-task. In this step, we dealt with a micro-task. But it’s important to note that when Macro-Task is out, tasks are executed one by one; Micro-tasks, on the other hand, are executed in teams. Therefore, we process the micro queue by executing the tasks in the queue one by one and de-queuing them until the queue is empty.
-
Perform render operations to update the interface
-
Check whether Web worker tasks exist and process them if so
-
The process repeats until both queues are empty
Let’s look at an example to illustrate the above process:
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')}, 0)})setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')})}, 0)Copy the code
The final output is Promise1, setTimeout1, Promise2, setTimeout2
- The macro task setTimeout2 is generated as the macro task setTimeout2 is generated as the macro task setTimeout2 is generated as the macro task setTimeout2 is generated as the macro task setTimeout2 is generated as the macro task setTimeout2
- Then go to the macro task queue, macro task setTimeout1 before setTimeout2, macro task setTimeout1, output setTimeout1
- When macro task setTimeout1 is executed, it generates the Promise2 microtask and puts it into the microtask queue. Then it emptying all tasks in the microtask queue and outputs the Promise2 microtask
- After clearing all tasks in the microtask queue, it goes to the macro task queue again, this time using setTimeout2
Prototype chain and inheritance
1. Prototypes and prototype chains
Prototype: In JavaScript, a prototype is a Prototype object that represents relationships between types.
Prototype chain: JavaScript everything is an object, and objects have relationships with each other, not in isolation. In JavaScript, the inheritance relationship between objects is that the prototype Object points to the parent Object until it points to the Object Object. In this way, a prototype pointing chain is formed. The technical term is called prototype chain.
var Person = function() {
this.age = 18
this.name = 'anonymous'
}
var Student = functionStudent. Prototype = new Person() var s1 = new Student() console.log(s1)Copy the code
Prototype diagram:
When trying to get an attribute of an object, if the object doesn’t have the attribute itself, it looks in its __proto__ (the prototype of its constructor). If the topmost layer is not found, it fails and returns undefined. Object.prototype.__proto__ === null
2. The inheritance
Here are some common inheritance methods (for more information, please click on six common JavaScript inheritance methods) :
- Combinatorial inheritance of prototype chain + borrowing constructor
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Copy the code
Call (this) inherits the attributes of the Parent class in the constructor of the child class, and then changes the prototype of the child class to new Parent() to inherit the functions of the Parent class.
The advantage of this inheritance method is that the constructor can pass parameters and will not be shared with the reference attributes of the parent class, and the function of the parent class can be reused. However, there is also a disadvantage that the parent class constructor is called when the parent class function is inherited, resulting in more unnecessary parent class attributes on the prototype of the subclass, resulting in a waste of memory.
- Parasitic combinatorial inheritance: This is an optimization of the previous combinatorial inheritance
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Copy the code
The core of the inheritance implementation above is to assign the prototype of the parent class to the child class, and set the constructor to the child class, which not only solves the problem of useless parent class attributes, but also can correctly find the constructor of the child class.
- ES6 class inheritance
The class keyword is introduced in ES6. The class extends extends extends, and static methods of a class are defined using the static keyword. This is much cleaner and more convenient than ES5’s inheritance by modifying the stereotype chain. Note that the class keyword is just syntactic sugar for the stereotype, and JavaScript inheritance is still implemented based on the stereotype.
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Copy the code
The core of class implementation inheritance is the use of extends to indicate which Parent class it inherits from, and you must call super in the subclass constructor because this code can be viewed as parent.call (this, value).
DOM operation and BOM operation
1. The DOM manipulation
When a web page is loaded, the browser creates a document Object model (DOM) of the page, which we can think of as an HTML structure recognized by JS, a plain JS object or array. Let’s look at common DOM operations:
- New nodes and mobile nodes are added
var div1 = document.getElementById('div1'Var p1 = document.createElement(var p1 = document.createElement('p')
p1.innerHTML = 'this is p1'Div1.appendchild (p1) // Add the newly created element // move the existing node. Var p2 = document.getelementById (var p2 = document.getelementById ('p2')
div1.appendChild(p2)
Copy the code
- Get the parent element
var div1 = document.getElementById('div1')
var parent = div1.parentElement
Copy the code
- Get child elements
var div1 = document.getElementById('div1')
var child = div1.childNodes
Copy the code
- Remove nodes
var div1 = document.getElementById('div1')
var child = div1.childNodes
div1.removeChild(child[0])
Copy the code
2.DOM event model and event flow
The DOM event model is divided into capture and bubbling. Propagation between child elements and parent elements after an event occurs. This spread is divided into three stages.
(1) Capture stage: the stage where the event is propagated from the Window object top-down to the target node;
(2) Target stage: the stage when the real target node is processing events;
(3) Bubbling stage: the stage in which the event propagates from the target node to the Window object from bottom to top.
The specific flow of DOM event capture
Capturing works from top to bottom. The event goes from the Window object, then to the Document (object), then to the HTML tag (get the HTML tag from the Document.documentElement), then to the body tag (get the body tag from the document.body), Then follow the normal HTML structure of the layer down, and finally reach the target element.
Here’s an example of an event bubbling up:
// event bubble <div id="outer">
<div id="inner"></div>
</div>
......
window.onclick = function() {
console.log('window');
};
document.onclick = function() {
console.log('document');
};
document.documentElement.onclick = function() {
console.log('html');
};
document.body.onclick = function() {
console.log('body');
}
outer.onclick = function(ev) {
console.log('outer');
};
inner.onclick = function(ev) {
console.log('inner');
};
Copy the code
How do I stop bubbling?
Prevent events from bubbling to the parent by using the event.stopPropagation() method, preventing any parent event handlers from being executed. We can add event.stopPropagation() to the click event of the inner element to prevent the parent event from executing and print only ‘inner’.
inner.onclick = function(ev) {
console.log('inner')
ev.stopPropagation()
}
Copy the code
3. Event Agent (Event delegate)
Since events propagate up to the parent node during the bubbling phase, you can define the listener function of the child node on the parent node and let the listener function of the parent node process events of multiple child elements uniformly. This approach is called event proxy.
Let’s set up a scenario where a
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a> </div> <button> click to add a label </button>Copy the code
var div1 = document.getElementById('div1')
div1.addEventListener('click'.function(e) {// e.target can listen to which element triggers the click event var target = e.targetif (e.nodeName === 'A'<a> element alert(target.innerhtml)}}Copy the code
Finally, the advantages of using proxies are as follows:
- Keep code simple
- Reduce your browser’s memory footprint
4. The BOM
BOM (Browser object Model) is the setting and retrieval of information about the browser itself, such as the width and height of the browser, and which address to redirect the browser to.
- Window.screen object: Contains information about the user’s screen
- Window. location object: Used to get the address (URL) of the current page and redirect the browser to the new page
- Window. history object: browsing history forward and backward, etc
- Window. navigator object: Often used to get browser information, mobile access, etc
Gets the screen width and height
console.log(screen.width)
console.log(screen.height)
Copy the code
Get url, protocol, path, parameter, hash, and so on
/ / such as the current site is https://juejin.im/timeline/frontend? a=10&b=10#someconsole.log(location.href) // https://juejin.im/timeline/frontend? a=10&b=10#someconsole.log(location.protocol) // https: console.log(location.pathname) // /timeline/frontend console.log(location.search) // ? a=10&b=10 console.log(location.hash) //#some
Copy the code
In addition, there are calls to the browser forward, backward functions, etc
history.back()
history.forward()
Copy the code
Get browser features (commonly known as UA) and identify the client, such as Chrome
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
Copy the code
5. With cross-domain Ajax
Ajax is a technique for asynchronously requesting data that can help improve user experience and application performance. Simply put, Ajax loads background data through asynchronous requests and renders it on a web page without the need to refresh the page. Common application scenarios include form verification whether login is successful, Baidu search drop-down box prompt and express tracking number query, etc. Ajax is designed to improve the user experience and reduce the amount of data transferred over the network.
How to write an XMLHttpRequest by hand without using any libraries
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {// The function is executed asynchronouslyif (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText)
}
}
}
xhr.open("GET"."/api".false)
xhr.send(null)
Copy the code
Because browsers have the same origin policy for security reasons. That is, if a protocol, domain name, or port difference is cross-domain, the Ajax request will fail.
So what are the security reasons for introducing this mechanism? In fact, it is mainly used to prevent CSRF attacks. To put it simply, a CSRF attack uses a user’s login status to initiate a malicious request.
Then let’s consider the question, the request is cross-domain, so is the request sent at all? The request must have been sent, but the browser intercepted the response.
Several common cross-domain solutions (see nine Cross-domain implementation Principles (full version) for details) :
- JSONP: Use the same origin policy pair
<script>
Tags are not restricted, but only GET requests are supported - CORS: The key to CORS communication is the backend and server Settings
Access-Control-Allow-Origin
You can start a highly regarded cross-domain solution that is much simpler than JSONP - Node middleware proxy or Nginx reverse proxy: mainly through the same origin policy to the server without restriction
6. Storage
The difference between sessionStorage, localStorage and cookie
- Common: Both are saved in the browser and comply with the same origin policy.
- Difference: difference in life cycle and scope
Scope: As long as localStorage is in the same protocol, the same host name, the same port, can read/modify to the same localStorage data. SessionStorage is more stringent than localStorage. In addition to protocol, host name, port, sessionStorage also requires the same window (that is, the browser TAB)
Six, modular
A brief introduction to several common modular specifications (see front End Modular details (full version)) :
- The CommonJS specification is mainly used for server-side programming, loading modules are synchronous, which is not suitable in a browser environment because synchronization means blocking loading and browser resources are loaded asynchronously, hence the AMD CMD solution
- The AMD specification loads modules asynchronously in the browser environment, and multiple modules can be loaded in parallel. However, AMD specifications are expensive to develop, code is difficult to read and write, and semantics are not smooth in the way modules are defined.
- The CMD specification is similar to the AMD specification in that it is used for browser programming, relies on proximity, executes lazily, and can be easily run in Node.js. However, depending on SPM packaging, the loading logic of modules is biased
- ES6 in the language standard level, the realization of module function, and the implementation is quite simple, can completely replace CommonJS and AMD specifications, become a common browser and server module solution
Welcome to pay attention to the public number: front-end craftsmen, we will witness your growth together!
The resources
- Sailing in the waves blog
- The way of the front-end interview
- Web front-end interview guide
- Front-end introduction 4–DOM and BOM
- Web front-end interview guide and high-frequency test question analysis