Frontend Interview Knowledge Collection - JavaScript
JavaScript Interview Knowledge Summary
1. Introduce the basic data types of JS.
JS has six basic data types: Undefined, Null, Boolean, Number, String, and the Symbol type added in ES6,
which represents a unique and immutable data type after creation. Its appearance is mainly to solve the problem of possible global variable conflicts.
2. How many types of values does JavaScript have? Can you draw their memory diagram?
Related knowledge points:
- Stack: primitive data types (Undefined, Null, Boolean, Number, String)
- Heap: reference data types (objects, arrays, and functions)
The difference between the two types is: the storage location is different.
Primitive data types are directly stored in the stack as simple data segments, occupying small space with fixed size, and are frequently used data, so they are stored in the stack.
Reference data types are stored as objects in the heap, occupying large space with variable size. If stored in the stack, it would affect program performance. Reference data types
store a pointer in the stack, which points to the starting address of the entity in the heap. When the interpreter looks for a reference value, it first retrieves its address in the stack, then obtains the entity from the heap.
Answer:
JS can be divided into two types of values: basic data types and complex data types.
Basic data types... (refer to 1)
Complex data types refer to the Object type. All other data types such as Array, Date, etc. can be understood as subclasses of the Object type.
The main difference between the two types is their storage location. The values of basic data types are directly stored in the stack, while the values of complex data types are stored in the heap, and the values in the heap are obtained through the corresponding pointers stored in the stack.
Detailed references: 《JavaScript 有几种类型的值?》 《JavaScript 有几种类型的值?能否画一下它们的内存图;》
3. What is a heap? What is a stack? What are the differences and connections between them?
The concepts of heap and stack exist in data structures and operating system memory.
In data structures, the data access method of a stack is Last In First Out. A heap is a priority queue, sorted by priority, and priority can be defined by size. A complete
binary tree is one implementation of a heap.
In operating systems, memory is divided into stack area and heap area.
Stack area memory is automatically allocated and released by the compiler, storing function parameter values, local variable values, etc. Its operation method is similar to the stack in data structures.
Heap area memory is generally allocated and released by the programmer. If the programmer does not release it, it may be reclaimed by the garbage collection mechanism when the program ends.
Detailed references: 《什么是堆?什么是栈?他们之间有什么区别和联系?》
4. What is the internal property [[Class]]?
All objects (such as arrays) whose typeof return value is "object" contain an internal property [[Class]] (which we can think of as an internal classification, rather than
a traditional object-oriented class). This property cannot be accessed directly, but can generally be viewed through Object.prototype.toString(..). For example:
Object.prototype.toString.call( [1,2,3] );
// "[object Array]"
Object.prototype.toString.call( /regex-literal/i );
// "[object RegExp]"
5. What built-in objects does JS have?
Related knowledge points:
Global objects, or standard built-in objects, should not be confused with the "global object". Global objects here refer to objects in the global scope.
Other objects in the global scope can be created by user scripts or provided by the host program.
Classification of standard built-in objects
(1) Value properties, these global properties return a simple value, and these values have no properties or methods of their own.
For example Infinity, NaN, undefined, null literals
(2) Function properties, global functions can be called directly, without specifying the owning object when calling, and the result is directly returned to the caller after execution.
For example eval(), parseFloat(), parseInt(), etc.
(3) Basic objects, basic objects are the foundation for defining or using other objects. Basic objects include general objects, function objects, and error objects.
For example Object, Function, Boolean, Symbol, Error, etc.
(4) Number and date objects, used to represent numbers, dates, and perform mathematical calculations.
For example Number, Math, Date
(5) Strings, objects used to represent and manipulate strings.
For example String, RegExp
(6) Indexed collections, these objects represent data collections sorted by index values, including arrays, typed arrays, and array-like objects. For example Array
(7) Keyed collections, these collection objects use keys when storing data and support iterating elements in insertion order.
For example Map, Set, WeakMap, WeakSet
(8) Vector collections, data in SIMD vector collections is organized as a data sequence.
For example SIMD, etc.
(9) Structured data, these objects are used to represent and manipulate structured buffer data, or data encoded with JSON.
For example JSON, etc.
(10) Control abstraction objects
For example Promise, Generator, etc.
(11) Reflection
For example Reflect, Proxy
(12) Internationalization, objects added to ECMAScript to support multilingual processing.
For example Intl, Intl.Collator, etc.
(13) WebAssembly
(14) Others
For example arguments
Answer:
The built-in objects in JS mainly refer to some global value properties, functions, and constructor objects used to instantiate other objects that are defined by JS in the global scope before program execution.
Common ones we use include global variable values like NaN, undefined, global functions like parseInt(), parseFloat(), constructors for instantiating objects
like Date, Object, etc., and built-in singleton objects for mathematical calculations like Math.
Detailed references: 《标准内置对象的分类》 《JS 所有内置对象属性和方法汇总》
6. The difference between undefined and undeclared?
A variable that has been declared in the scope but has not been assigned a value is undefined. Conversely, a variable that has not been declared in the scope is undeclared.
Referring to an undeclared variable will cause a reference error in the browser, such as ReferenceError: b is not defined. However, we can use typeof's safety
guard mechanism to avoid errors, because for undeclared (or not defined) variables, typeof returns "undefined".
7. The difference between null and undefined?
First, Undefined and Null are both basic data types. Each of these two basic data types has only one value, which is undefined and null respectively.
undefined means not defined, null means an empty object. Generally, when a variable is declared but not defined, it returns undefined. null
is mainly used to assign to variables that may return objects, as initialization.
undefined is not a reserved word in JS, which means we can use undefined as a variable name. This practice is very dangerous because it
affects our judgment of the undefined value. However, we can obtain a safe undefined value through some methods, such as void 0.
When we use typeof to judge the two types, Null type returns "object", which is a legacy issue. When we use double equals
to compare the two types, it returns true, while using triple equals returns false.
Detailed references: 《JavaScript 深入理解之 undefined 与 null》
8. How to get a safe undefined value?
Because undefined is an identifier, it can be used and assigned as a variable, but this affects the normal judgment of undefined.
The expression void ___ has no return value, so the result is undefined. void does not change the result of the expression, it just makes the expression not return a value.
By convention, we use void 0 to get undefined.
9. List a few basic rules for writing JavaScript?
In everyday project development, we follow some basic rules, such as:
(1) All variable declarations in a function scope should be hoisted to the top of the function as much as possible, using one var declaration. Two consecutive var declarations are not allowed. When declaring,
if the variable has no value, it should be assigned the initial value of the corresponding type, so that others can easily understand the variable's type value when reading the code.
(2) Strings such as addresses and times in the code should be replaced with constants.
(3) When comparing, try to use '===', '!==' instead of '==', '!='.
(4) Do not add methods to the prototypes of built-in objects, such as Array, Date.
(5) switch statements must have a default branch.
(6) for loops must use curly braces.
(7) if statements must use curly braces.
10. JavaScript prototype, prototype chain? What are the characteristics?
In JS, we use constructors to create new objects. Each constructor has a prototype property value, which is an
object containing properties and methods that can be shared by all instances created by that constructor. When we use a constructor to create a new object, the object will contain a pointer
that points to the value corresponding to the constructor's prototype property. In ES5, this pointer is called the object's prototype. Generally, we
should not be able to access this value, but now browsers have implemented the __proto__ property for us to access this property, but we'd better not use
it because it is not specified in the specification. ES5 added a new method Object.getPrototypeOf(), through which we can get the
object's prototype.
When we access a property of an object, if this property does not exist inside the object, it will look for it in its prototype object. This prototype object
will have its own prototype, and so on, which is the concept of the prototype chain. The end of the prototype chain is generally Object.prototype, which is
why our newly created objects can use methods like toString().
Characteristics:
JavaScript objects are passed by reference. Each new object entity we create does not have its own copy of the prototype. When we modify the prototype, the
related objects will also inherit this change.
Detailed references: 《JavaScript 深入理解之原型与原型链》
11. Methods to get the prototype in JS?
- p.__proto__
- p.constructor.prototype
- Object.getPrototypeOf(p)
12. How to represent numbers in different bases in JS
Starting with 0X, 0x represents hexadecimal.
Starting with 0, 0O, 0o represents octal.
Starting with 0B, 0b represents binary.
13. What is the safe integer range in JS?
Safe integers refer to integers within this range that do not lose precision when converted to binary storage. The maximum integer that can be "safely" represented is 2^53 - 1,
which is 9007199254740991, defined as Number.MAX_SAFE_INTEGER in ES6. The minimum integer is -9007199254740991, defined
as Number.MIN_SAFE_INTEGER in ES6.
If the result of a calculation exceeds the numerical range of JavaScript, the value will be automatically converted to a special Infinity value. If a calculation
returns a positive or negative Infinity value, that value cannot participate in the next calculation. To determine whether a number is finite, you can use the isFinite function.
14. What is the result of typeof NaN?
NaN stands for "not a number". NaN is a sentinel value (a regular value with a special purpose), used to indicate
an error condition in the number type, i.e., "the mathematical operation was not successful, this is the result returned after failure."
typeof NaN; // "number"
NaN is a special value that is not equal to itself. It is the only non-reflexive (reflexive, i.e., x === x does not hold) value. And NaN != NaN
is true.
15. What is the difference between isNaN and Number.isNaN functions?
The function isNaN, after receiving a parameter, will try to convert this parameter to a number. Any value that cannot be converted to a number will return true,
so non-numeric values passed in will also return true, affecting the judgment of NaN.
The function Number.isNaN will first determine whether the passed parameter is a number, and if so, continue to determine whether it is NaN. This method is more accurate for NaN judgment.
16. The behavior of the Array constructor with only one parameter value?
When the Array constructor takes only one numeric parameter, this parameter is used as the preset length of the array, rather than just serving as an element in the array. This
creates an empty array, but its length property is set to the specified value.
The constructor Array(..) does not require the new keyword. If not used, it will be automatically added.
17. Conversion rules for other values to strings?
Section 9.8 of the specification defines the abstract operation ToString, which handles the coercion from non-string to string.
(1) Null and Undefined types, null is converted to "null", undefined is converted to "undefined".
(2) Boolean type, true is converted to "true", false is converted to "false".
(3) Number type values are directly converted, but very small and very large numbers will use exponential form.
(4) Symbol type values are directly converted, but only explicit coercion is allowed. Implicit coercion will cause an error.
(5) For ordinary objects, unless a custom toString() method is defined, toString() (Object.prototype.toString()) will be called
to return the value of the internal property [[Class]], such as "[object Object]". If the object has its own toString() method, it will be called
when stringifying, and its return value will be used.
18. Conversion rules for other values to numbers?
Sometimes we need to use non-numeric values as numbers, such as in mathematical operations. For this, the ES5 specification defines the abstract operation ToNumber in section 9.3.
(1) Undefined type values are converted to NaN.
(2) Null type values are converted to 0.
(3) Boolean type values, true is converted to 1, false is converted to 0.
(4) String type values are converted as if using the Number() function. If they contain non-numeric values, they are converted to NaN, empty strings become 0.
(5) Symbol type values cannot be converted to numbers and will throw an error.
(6) Objects (including arrays) will first be converted to the corresponding primitive type values. If the returned value is a non-numeric primitive type value, it will then be coerced to a number following the above rules.
To convert a value to the corresponding primitive type value, the abstract operation ToPrimitive will first (via the internal operation DefaultValue) check whether the value has a valueOf() method. If it does and returns a primitive type value, that value is used for coercion. If not, the return value of toString() (if it exists) is used for coercion.
If neither valueOf() nor toString() returns a primitive type value, a TypeError is thrown.
19. Conversion rules for other values to boolean?
Section 9.2 of the ES5 specification defines the abstract operation ToBoolean, listing all possible results of boolean coercion.
The following are falsy values:
• undefined
• null
• false
• +0, -0, and NaN
• ""
The boolean coercion result of falsy values is false. Logically speaking, values outside the falsy value list should be truthy.
20. What are the results of valueOf and toString for {} and []?
{}'s valueOf result is {}, toString result is "[object Object]"
[]'s valueOf result is [], toString result is ""
21. What are falsy objects?
In certain specific situations, browsers create some foreign values based on regular JavaScript syntax. These are "falsy objects". Falsy objects look
no different from ordinary objects (both have properties, etc.), but when coerced to boolean, the result is false. The most common example is document.all, which
is an array-like object containing all elements on the page, provided to JavaScript programs by the DOM (rather than the JavaScript engine).
22. What does the ~ operator do?
~ returns the two's complement, and ~ converts the number to a 32-bit integer, so we can use ~ for integer truncation.
~x is roughly equivalent to -(x+1).
23. Parsing numbers from strings and coercing strings to numbers both return numbers. What’s the difference?
Parsing (such as parseInt()) allows non-numeric characters in the string. Parsing proceeds from left to right and stops when a non-numeric character is encountered. Conversion (such as
Number()) does not allow non-numeric characters, otherwise it fails and returns NaN.
24. When does the + operator perform string concatenation?
According to section 11.6.1 of the ES5 specification, if an operand is a string or can be converted to a string through the following steps, + will perform concatenation. If
one of the operands is an object (including an array), it first calls the ToPrimitive abstract operation, which in turn calls [[DefaultValue]] with
number as the context. If it cannot be converted to a string, it will be converted to a number type for calculation.
Simply put, if one of the operands of + is a string (or ultimately becomes a string through the above steps), string concatenation is performed; otherwise, numeric
addition is performed.
For operators other than addition, if one side is a number, the other side will be converted to a number.
25. When does implicit boolean coercion occur?
(1) The condition expression in if (..) statements.
(2) The condition expression in for (.. ; .. ; .. ) statements (the second one).
(3) The condition expression in while (..) and do..while(..) loops.
(4) The condition expression in ? :.
(5) The left operand of logical operators || (logical OR) and && (logical AND) (as condition expressions).
26. Return values of || and && operators?
|| and && first perform a condition test on the first operand. If it's not a boolean, they first perform ToBoolean coercion, then execute the condition
test.
For ||, if the condition test result is true, it returns the value of the first operand; if false, it returns the value of the second operand.
&& is the opposite: if the condition test result is true, it returns the value of the second operand; if false, it returns the value of the first operand.
|| and && return the value of one of their operands, not the result of the condition test.
27. Coercion of Symbol values?
ES6 allows explicit coercion from symbols to strings, but implicit coercion will produce an error.
Symbol values cannot be coerced to numbers (both explicit and implicit will produce errors), but can be coerced to booleans (both explicit and implicit result
are true).
28. Coercion rules for the == operator?
(1) Equality comparison between strings and numbers: the string is converted to a number before comparison.
(2) Equality comparison between other types and boolean types: the boolean is first converted to a number, then other rules are applied for comparison.
(3) Equality comparison between null and undefined: the result is true. Comparisons with other values all return false.
(4) Equality comparison between objects and non-objects: the object first calls the ToPrimitive abstract operation, then comparison is made.
(5) If one operand is NaN, the equality comparison returns false (NaN itself is not equal to NaN).
(6) If both operands are objects, they are compared to see if they point to the same object. If both operands point to the same object, the equality operator returns true; otherwise, it returns false.
Detailed references: 《JavaScript 字符串间的比较》
29. How to convert a string to a number, e.g., ‘12.3b’?
(1) Use the Number() method, provided that the string does not contain illegal characters.
(2) Use the parseInt() method. The parseInt() function parses a string and returns an integer. You can also set the radix of the number to parse. When the radix value is 0, or when this parameter is not set, parseInt() determines the radix based on the string.
(3) Use the parseFloat() method. This function parses a string parameter and returns a floating point number.
(4) Use the implicit conversion of the + operator.
Detailed references: 《详解 JS 中 Number()、parseInt() 和 parseFloat() 的区别》
30. How to add a comma every three digits to the left of the decimal point, e.g., 12000000.11 to ‘12,000,000.11’?
function format(number) {
return number && number.replace(/(?!^)(?=(\d{3})+\.)/g, ",");
}
31. Common regular expressions
// (1) Match 16-color hex value
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
// (2) Match dates in yyyy-mm-dd format
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
// (3) Match QQ number
var regex = /^[1-9][0-9]{4,10}$/g;
// (4) Mobile phone number regex
var regex = /^1[34578]\d{9}$/g;
// (5) Username regex
var regex = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;
Detailed references: 《前端表单验证常用的 15 个 JS 正则表达式》 《JS 常用正则汇总》
32. Various methods for generating random numbers?
《JS - 生成随机数的方法汇总(不同范围、类型的随机数)》
33. How to randomly sort an array?
// (1) Use the array sort method to randomly sort array elements, comparing Math.random() with 0.5. If greater, return 1 to swap positions; if less, return -1, keep positions.
function randomSort(a, b) {
return Math.random() > 0.5 ? -1 : 1;
}
// Drawback: each element's position in the new array is not truly random because the sort() method compares sequentially.
// (2) Randomly extract an element from the original array and add it to the new array
function randomSort(arr) {
var result = [];
while (arr.length > 0) {
var randomIndex = Math.floor(Math.random() * arr.length);
result.push(arr[randomIndex]);
arr.splice(randomIndex, 1);
}
return result;
}
// (3) Randomly swap elements in the array (similar to shuffle algorithm)
function randomSort(arr) {
var index,
randomIndex,
temp,
len = arr.length;
for (index = 0; index < len; index++) {
randomIndex = Math.floor(Math.random() * (len - index)) + index;
temp = arr[index];
arr[index] = arr[randomIndex];
arr[randomIndex] = temp;
}
return arr;
}
// es6
function randomSort(array) {
let length = array.length;
if (!Array.isArray(array) || length <= 1) return;
for (let index = 0; index < length - 1; index++) {
let randomIndex = Math.floor(Math.random() * (length - index)) + index;
[array[index], array[randomIndex]] = [array[randomIndex], array[index]];
}
return array;
}
Detailed references: 《Fisher and Yates 的原始版》 《javascript 实现数组随机排序?》 《JavaScript 学习笔记:数组随机排序》
34. Several ways to create objects in JavaScript?
We generally use literal form to create objects directly, but this method generates a lot of duplicate code when creating many similar objects. But JS
is different from general object-oriented languages. Before ES6, it didn't have the concept of classes. However, we can use functions to simulate it, thus producing reusable object
creation methods. The methods I know are:
(1) The first is the factory pattern. The main working principle of the factory pattern is to use functions to encapsulate the details of creating objects, thus achieving reuse by calling functions. But it has a big problem: the created objects cannot be associated with a certain type. It simply encapsulates reusable code without establishing a relationship between objects and types.
(2) The second is the constructor pattern. In JS, every function can be a constructor. As long as a function is called with new, we can call it a constructor. Executing a constructor first creates an object, then sets the object's prototype to the constructor's prototype property, then points this in the execution context to this object, and finally executes the entire function. If the return value is not an object, it returns the newly created object. Because the value of this points to the newly created object, we can use this to assign values to the object. The advantage of the constructor pattern over the factory pattern is that the created objects establish a connection with the constructor, so we can identify the object's type through the prototype. However, the constructor has a drawback: it creates unnecessary function objects. Because in JS, functions are also objects, if object properties contain functions, we will create a new function object every time, wasting unnecessary memory space, since functions can be shared by all instances.
(3) The third pattern is the prototype pattern. Because every function has a prototype property, which is an object that contains properties and methods that can be shared by all instances created through the constructor. Therefore, we can use prototype objects to add common properties and methods, thus achieving code reuse. Compared to the constructor pattern, this pattern solves the problem of function object reuse. However, this pattern also has some problems: one is that there is no way to initialize values by passing parameters; another is that if there is a reference type value like Array, all instances will share one object, and one instance's modification of the reference type value will affect all instances.
(4) The fourth pattern is the combination of constructor pattern and prototype pattern. This is the most common way to create custom types. Because using the constructor pattern and prototype pattern separately both have some problems, we can combine these two patterns: initialize object properties through the constructor, and achieve function method reuse through prototype objects. This method well solves the shortcomings of using each pattern alone, but it has a shortcoming: because two different patterns are used, the code encapsulation is not good enough.
(5) The fifth pattern is the dynamic prototype pattern. This pattern moves the creation process of prototype method assignment into the constructor. By checking whether a property exists, it can achieve the effect of assigning the prototype object only once when the function is first called. This method encapsulates the above hybrid pattern well.
(6) The sixth pattern is the parasitic constructor pattern. This pattern is basically the same as the factory pattern. My understanding of this pattern is that it is mainly based on an existing type and extends the instantiated object when instantiating. This way, we don't need to modify the original constructor while achieving the purpose of extending the object. Its drawback, like the factory pattern, is that it cannot achieve object identification.
Those are the methods I know so far.
Detailed references: 《JavaScript 深入理解之对象创建》
35. Several implementation methods of JavaScript inheritance?
The several ways to implement inheritance in JS that I know:
(1) The first is to implement inheritance through the prototype chain. But the disadvantage of this implementation is that when it contains reference type data, it will be shared by all instance objects, which can easily cause modification confusion. Also, when creating a subtype, parameters cannot be passed to the supertype.
(2) The second method is to use constructor stealing. This method is implemented by calling the supertype's constructor in the subtype's function. This method solves the problem of not being able to pass parameters to the supertype, but it has a problem: it cannot achieve function method reuse, and the methods defined on the supertype's prototype are not accessible to the subtype.
(3) The third method is combination inheritance. Combination inheritance uses a combination of prototype chain and constructor stealing. It achieves inheritance of type properties through constructor stealing, and achieves method inheritance by setting the subtype's prototype as an instance of the supertype. This method solves the problems of using the above two patterns separately, but because we use the supertype's instance as the subtype's prototype, it calls the supertype's constructor twice, adding many unnecessary properties to the subtype's prototype.
(4) The fourth method is prototypal inheritance. The main idea of prototypal inheritance is to create new objects based on existing objects. The implementation principle is to pass an object to a function, and then return an object that uses this passed object as its prototype. This inheritance idea is mainly not for creating a new type, but for implementing a simple inheritance of an object. The Object.create() method defined in ES5 is an implementation of prototypal inheritance. The disadvantage is the same as the prototype chain method.
(5) The fifth method is parasitic inheritance. The idea of parasitic inheritance is to create a function that encapsulates the inheritance process. It passes in an object, creates a copy of the object, then extends the object, and finally returns this object. This extension process can be understood as a form of inheritance. The advantage of this inheritance is that it implements inheritance for a simple object, especially when this object is not our custom type. The disadvantage is that it cannot achieve function reuse.
(6) The sixth method is parasitic combination inheritance. The disadvantage of combination inheritance is that it uses an instance of the supertype as the prototype of the subtype, which adds unnecessary prototype properties. Parasitic combination inheritance uses a copy of the supertype's prototype as the subtype's prototype, thus avoiding the creation of unnecessary properties.
Detailed references: 《JavaScript 深入理解之继承》
36. Implementation of parasitic combination inheritance?
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log("My name is " + this.name + ".");
};
function Student(name, grade) {
Person.call(this, name);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.sayMyGrade = function() {
console.log("My grade is " + this.grade + ".");
};
37. JavaScript scope chain?
The role of the scope chain is to ensure orderly access to all variables and functions that the execution environment has the right to access. Through the scope chain, we can access variables and functions of the outer environment.
The scope chain is essentially a pointer list pointing to variable objects. A variable object is an object containing all variables and functions in the execution environment. The front of the scope chain
is always the variable object of the current execution context. The variable object of the global execution context (i.e., the global object) is always the last object of the scope chain.
When we look up a variable, if we don't find it in the current execution environment, we can look backward along the scope chain.
The creation process of the scope chain is related to the establishment of the execution context...
Detailed references: 《JavaScript 深入理解之作用域链》
38. Talk about your understanding of the This object.
this is a property in the execution context that points to the object that last called this method. In actual development, the direction of this can be determined through four call
modes.
- The first is the function call mode. When a function is called directly as a function rather than as a property of an object, this points to the global object.
- The second is the method call mode. If a function is called as a method of an object, this points to that object.
- The third is the constructor call mode. If a function is called with new, a new object is created before the function executes, and this points to this newly created object.
- The fourth is the apply, call, and bind call mode. These three methods can explicitly specify the this point of the calling function. The apply method receives two parameters: one is the object bound to this, and the other is an array of parameters. The call method receives parameters where the first is the object bound to this, and the remaining parameters are the parameters passed to the function execution. When using the call() method, the parameters passed to the function must be listed one by one. The bind method returns a new function with this bound to the passed object. The this of this function will not change except when using new.
Among these four methods, the constructor call mode has the highest priority, followed by the apply, call, and bind call mode, then the method call mode, and finally the function call mode.
39. What does eval do?
Its function is to parse the corresponding string into JS code and run it.
Using eval should be avoided as it is unsafe and very performance-intensive (2 passes: one to parse into JS statements, one to execute).
Detailed references: 《eval()》
40. What are DOM and BOM?
DOM stands for Document Object Model. It refers to treating a document as an object. This object mainly defines methods and interfaces for processing web page content.
BOM stands for Browser Object Model. It refers to treating the browser as an object. This object mainly defines methods and interfaces for interacting with the browser. The core of BOM
is window, and the window object has a dual role: it is both an interface for accessing the browser window through JS and a Global
object. This means that any object, variable, and function defined in a web page exists as a property or method of the global object. The window object contains
sub-objects like location, navigator, screen, etc., and the most fundamental object of DOM, the document object, is also a sub-object of BOM's window
object.
Detailed references: 《DOM, DOCUMENT, BOM, WINDOW 有什么区别?》 《Window 对象》 《DOM 与 BOM 分别是什么,有何关联?》 《JavaScript 学习总结(三)BOM 和 DOM 详解》
41. Write a general event listener function.
const EventUtils = {
// Bind event using dom0||dom2||IE methods depending on capability
// Add event
addEvent: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
// Remove event
removeEvent: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
// Get event target
getTarget: function(event) {
return event.target || event.srcElement;
},
// Get reference to event object, capture all event information, ensure event can be used anytime
getEvent: function(event) {
return event || window.event;
},
// Stop event propagation (mainly event bubbling, since IE doesn't support event capture)
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
},
// Cancel default event behavior
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
};
Detailed references: 《JS 事件模型》
42. What is an event? What’s the difference between IE and Firefox event mechanisms? How to stop bubbling?
- An event is an interaction action when a user operates a web page, such as click/move. Besides user-triggered actions, events can also be document loading, window scrolling and resizing. Events are encapsulated into an event object, which contains all relevant information when the event occurs (event properties) and operations that can be performed on the event (event methods).
- Event handling mechanism: IE supports event bubbling, Firefox supports both event models simultaneously: event bubbling and event capture.
- event.stopPropagation() or IE method event.cancelBubble = true;
Detailed references: 《JavaScript 事件模型系列(一)事件及事件的三种模型》 《JavaScript 事件模型:事件捕获和事件冒泡》
43. What are the three event models?
Events are interaction actions when users operate a web page or some operations of the web page itself. Modern browsers have three event models.
The first event model is the earliest DOM0 level model. This model does not propagate, so there is no concept of event flow, but some browsers now support implementation through bubbling.
It can directly define listener functions in the web page, or specify listener functions through JS properties. This method is compatible with all browsers.
The second event model is the IE event model. In this event model, an event has two processes: the event handling phase and the event bubbling phase. The event handling phase first executes the listener event bound to the target element. Then comes the event bubbling phase, where the event bubbles from the target element to the document, checking in turn whether the passing nodes have event listener functions bound, and if so, executing them. This model uses attachEvent to add listener functions, can add multiple listener functions, and they execute in order.
The third is the DOM2 level event model. In this event model, an event has three processes. The first process is the event capture phase. Capture means the event propagates from the document down to the target element, checking in turn whether the passing nodes have event listener functions bound, and if so, executing them. The latter two phases are the same as the two phases of the IE event model. In this event model, the function for event binding is addEventListener, where the third parameter can specify whether the event should be executed in the capture phase.
Detailed references: 《一个 DOM 元素绑定多个事件时,先执行冒泡还是捕获》
44. What is event delegation?
Event delegation essentially uses the browser's event bubbling mechanism. Because events bubble up to the parent node during the bubbling process, and the parent node can get the
target node through the event object, we can define the child node's listener function on the parent node, and the parent node's listener function handles events of multiple child elements uniformly. This method is called event delegation.
Using event delegation, we don't need to bind a listener event for every child element, which reduces memory consumption. And using event delegation, we can also achieve dynamic binding of events. For example, when a new child node is added, we don't need to add a listener event for it separately; the events that occur on it will be handled by the listener function in the parent element.
Detailed references: 《JavaScript 事件委托详解》
45. What’s the result of [“1”, “2”, “3”].map(parseInt)?
The parseInt() function parses a string and returns an integer. It requires two parameters (val, radix), where radix represents the radix of the number to parse. (The value is between 2 and 36, and the number in the string cannot be greater than radix to correctly return a number result.)
Here, map passes 3 parameters (element, index, array). The third parameter is ignored by default, so the three passed parameters are "1-0", "2-1", "3-2"
Because the string value cannot be greater than the radix, the latter two calls fail and return NaN. The first radix is 0, which is parsed as decimal, returning 1.
Detailed references: 《为什么 [“1”, “2”, “3”].map(parseInt) 返回 [1,NaN,NaN]?》
46. What is a closure and why use it?
A closure is a function that has access to variables in the scope of another function. The most common way to create a closure is to create another function inside a function, and the created function can
access the local variables of the current function.
Closures have two common uses.
The first use of closures is to enable us to access variables inside a function from outside the function. By using closures, we can access variables inside the function from outside by calling the closure function.
This method can be used to create private variables.
Another use of functions is to keep the variable object in the context of a function that has finished running in memory, because the closure function retains a reference to this variable object, so
this variable object will not be recycled.
In fact, the essence of a closure is a special application of the scope chain. As long as you understand the creation process of the scope chain, you can understand the implementation principle of closures.
Detailed references: 《JavaScript 深入理解之闭包》
47. What does “use strict”; mean in JavaScript code? What’s the difference?
Related knowledge points:
use strict is a (strict) running mode added in ECMAScript 5. This mode makes JavaScript run under more stringent conditions.
The main purposes of establishing "strict mode" are:
- Eliminate some unreasonable and imprecise aspects of JavaScript syntax, reduce some weird behavior;
- Eliminate some unsafe aspects of code execution, ensure code execution safety;
- Improve compiler efficiency, increase running speed;
- Pave the way for future new versions of JavaScript.
Differences:
- Prohibit the use of with statements.
- Prohibit the this keyword from pointing to the global object.
- Objects cannot have duplicate property names.
Answer:
use strict refers to strict running mode, which adds some restrictions to JS usage. For example, it prohibits this from pointing to the global object, and prohibits the use
of with statements. The purpose of establishing strict mode is mainly to eliminate some unsafe usage methods in code usage, and also to eliminate some unreasonable aspects of JS syntax itself,
thereby reducing some runtime weird behavior. At the same time, using strict running mode can also improve compilation efficiency, thus improving code running speed.
I think strict mode represents a more reasonable, safer, and more rigorous development direction for JS.
Detailed references: 《JavaScript 严格模式详解》
48. How to determine if an object belongs to a certain class?
The first method is to use the instanceof operator to determine whether the constructor's prototype property appears anywhere in the object's prototype chain.
The second method can be judged through the object's constructor property. The object's constructor property points to the object's constructor, but this method is not very safe because the constructor property can be overwritten.
The third method, if you need to determine a built-in reference type, you can use the Object.prototype.toString() method to print the object's
[[Class]] property for judgment.
Detailed references: 《js 判断一个对象是否属于某一类》
49. What does instanceof do?
// The instanceof operator is used to determine whether the constructor's prototype property appears anywhere in the object's prototype chain.
// Implementation:
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left), // Get the object's prototype
prototype = right.prototype; // Get the constructor's prototype object
// Determine whether the constructor's prototype object is on the object's prototype chain
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
Detailed references: 《instanceof》
50. What exactly does the new operator do? How to implement it?
// (1) First, create a new empty object
// (2) Set the prototype, set the object's prototype to the function's prototype object.
// (3) Let the function's this point to this object, execute the constructor's code (add properties to this new object)
// (4) Determine the return value type of the function. If it's a value type, return the created object. If it's a reference type, return that reference type object.
// Implementation:
function objectFactory() {
let newObject = null,
constructor = Array.prototype.shift.call(arguments),
result = null;
// Parameter check
if (typeof constructor !== "function") {
console.error("type error");
return;
}
// Create a new empty object, with the prototype set to the constructor's prototype object
newObject = Object.create(constructor.prototype);
// Point this to the new object and execute the function
result = constructor.apply(newObject, arguments);
// Determine the returned object
let flag =
result && (typeof result === "object" || typeof result === "function");
// Determine the return result
return flag ? result : newObject;
}
// Usage
// objectFactory(Constructor, initialization parameters);
Detailed references: 《new 操作符具体干了什么?》 《JavaScript 深入之 new 的模拟实现》
51. In JavaScript, there is a function that, when looking up an object, never looks up the prototype chain. What function is it?
hasOwnProperty
All objects that inherit Object will inherit the hasOwnProperty method. This method can be used to check whether an object has a specific own property. Unlike
the in operator, this method ignores properties inherited from the prototype chain.
Detailed references: 《Object.prototype.hasOwnProperty()》
52. Knowledge about JSON?
Related knowledge points:
JSON is a data interchange format, based on text, lightweight, used for data exchange.
JSON can represent numbers, booleans, strings, null, arrays (ordered sequences of values), and objects (mappings of strings to
values) composed of these values (or arrays, objects).
JSON uses JavaScript syntax, but JSON format is just text. Text can be read by any programming language and passed as a data format.
Answer:
JSON is a text-based lightweight data interchange format. It can be read by any programming language and passed as a data format.
In project development, we use JSON as a way to exchange data between the frontend and backend. On the frontend, we serialize a data structure that conforms to JSON format into a JSON string, then pass it to the backend. The backend parses the JSON format string to generate the corresponding data structure, thus achieving data transfer between frontend and backend.
Because JSON's syntax is based on JS, it's easy to confuse JSON with JS objects. But we should note that JSON and JS objects are not the same thing. JSON's object format is stricter. For example, in JSON, property values cannot be functions, and property values like NaN cannot appear. Therefore, most JS objects do not conform to JSON object format.
JS provides two functions to handle conversion between JS data structures and JSON format. One is the JSON.stringify function, which takes a data structure that conforms to JSON format and converts it to a JSON string. If the input data structure does not conform to JSON format, these values will be specially handled during serialization to make them conform to the specification. When sending data from the frontend to the backend, we can call this function to convert the data object into a JSON format string.
The other function is JSON.parse(), which is used to convert a JSON format string into a JS data structure. If the input string is not a standard JSON format string, an error will be thrown. When we receive a JSON format string from the backend, we can use this method to parse it into a JS data structure for data access.
Detailed references: 《深入了解 JavaScript 中的 JSON 》
53. Can you explain the meaning of this code: [].forEach.call($$(""),function(a){a.style.outline=“1px solid #"+(~~(Math.random()(1«24))).toString(16)})?
(1) Select all DOM elements on the page. In the browser console, you can use the $$() method to get corresponding elements on the page. This is a command-line API provided by modern browsers, equivalent to the document.querySelectorAll method.
(2) Loop through DOM elements
(3) Add outline to elements. Because the rendered outline is not in the CSS box model, adding outline to elements does not affect the element's size and page layout.
(4) Generate random color function. Math.random()*(1<<24) can get a random number between 0 and 2^24 - 1. Since the result is a floating-point number but we only need the integer part, we use the bitwise NOT operator ~ twice consecutively to get the integer part, and then use toString(16) to convert it to a hexadecimal string.
Detailed references: 《通过一行代码学 JavaScript》
54. What are the ways to lazy load JS?
Related knowledge points:
JS lazy loading means loading JavaScript files after the page has finished loading. JS lazy loading helps improve page loading speed.
Generally, there are the following methods:
- defer attribute
- async attribute
- Dynamically create DOM method
- Use setTimeout to delay method
- Let JS load last
Answer:
The loading, parsing, and execution of JS will block the page rendering process, so we want JS scripts to be loaded as lazily as possible to improve page rendering speed.
The methods I know are:
The first method is generally to place JS scripts at the bottom of the document to make JS scripts load and execute last.
The second method is to add the defer attribute to JS scripts. This attribute makes the loading of the script synchronized with the parsing of the document, and then executes the script file after the document parsing is complete, so the page rendering is not blocked. Multiple scripts with the defer attribute are supposed to execute in order according to the specification, but this may not be the case in some browsers.