充满信心地准备下一次 JavaScript 面试!
在本文中,我整理了 30 多个最重要的 JavaScript 面试问题以及详细的答案和代码示例。
深入了解这个宝贵的资料,让您在 JavaScript 面试中取得好成绩。
1级:基本
-
Javascript 是单线程的吗?
-
解释 JavaScript 引擎的主要组件及其工作原理。
-
解释 JavaScript 中的事件循环以及它如何帮助异步编程。
-
var、let 和 const 之间的区别?
-
Javascript 中不同的数据类型?
-
什么是回调函数和回调地狱?
-
什么是 Promise 和 Promise 链?
-
什么是异步/等待?
9.== 和 === 运算符和有什么不一样?
-
在 Javascript 中创建对象的不同方法?
-
什么是休息和传播操作员?
-
什么是高阶函数?
二级:中级
-
什么是闭合?闭包有哪些用例?
-
解释 JavaScript 中吊装的概念。
-
什么是时间盲区?
-
什么是原型链?和 Object.create() 方法?
-
Call、Apply 和 Bind 方法之间有什么区别?
-
什么是 lambda 函数或箭头函数?
-
什么是咖喱功能?
-
ES6有哪些特点?
3级:专家
-
什么是执行上下文、执行堆栈、变量对象、范围链?
-
callback、promise、setTimeout、process.nextTick()的执行优先级是多少?
-
什么是工厂功能和发电机功能?
-
克隆对象(对象的浅层和深度副本)的不同方法?
-
如何使对象不可变?(密封和冷冻方法)?
-
什么是事件和事件流、事件冒泡和事件捕获?
-
什么是活动委派?
-
什么是服务器发送的事件?
-
javascript 中的 Web Worker 或 Service Worker 是什么?
-
如何在 javascript 中比较 2 个 JSON 对象?
=============================================
1. Javascript 是单线程的吗?
是的,JavaScript 是一种 单线程语言。这意味着它只有一个调用堆栈和一个内存堆。一次只执行一组指令。
此外,Javascript 本质上是同步和阻塞的。这意味着代码是逐行执行的,并且必须在下一个任务开始之前完成一个任务
但是,JavaScript 还具有异步功能,允许独立于主执行线程执行某些操作。这通常是通过回调、promise、async/await 和事件侦听器等机制实现的。这些异步特性使 JavaScript 能够在不阻塞主线程的情况下处理诸如获取数据、处理用户输入和执行 I/O 操作等任务,使其适用于构建响应式和交互式 Web 应用程序。
2. 解释 JavaScript 引擎的主要组件及其工作原理。
每个浏览器都有一个 Javascript 引擎,用于执行 javascript 代码并将其转换为机器代码。
执行 JavaScript 代码时,解析器首先读取代码并生成 AST,并将其存储在内存中。然后,解释器处理此 AST 并生成字节码或机器码,由计算机执行。
探查器是 JavaScript 引擎的一个组件,用于监视代码的执行。
字节码通过优化编译器和分析数据来使用。“优化编译器”或实时 (JIT) 编译器根据分析数据做出某些假设,并生成高度优化的机器代码。
有时,“优化”假设不正确,然后通过“取消优化”阶段回到以前的版本(它实际上成为我们的开销)
JS 引擎通常会优化“热函数”,并使用内联缓存技术来优化代码。
在此过程中,调用堆栈会跟踪当前正在执行的函数,内存堆用于内存分配。
最后,垃圾回收器开始发挥作用,通过从未使用的对象中回收内存来管理内存。
谷歌浏览器 V8 引擎:
- 解释器称为 “点火” 。
- 优化编译器称为 “TurboFan” 。
- 除了 Parser,还有一个“预解析器”,用于检查语法和标记
- 引入了“火花塞”,它存在于“Ignition”和“TurboFan”之间,也称为快速编译器。
3. 解释 JavaScript 中的事件循环。
事件循环是 JavaScript 运行时环境的核心组件。它负责调度和执行异步任务。事件循环通过持续监视两个队列来工作:调用堆栈和事件队列。
调用堆栈是一个堆栈 (LIFO) 数据结构,用于存储当前正在执行的函数( 存储在代码执行期间创建的执行上下文 )。
Web API 是异步操作(setTimeout、fetch requests、promises)及其回调等待完成的地方。它从线程池中借用线程, 在不阻塞主线程的情况下在后台完成任务。
作业队列(或微任务) 是一个 FIFO(先进先出)结构,用于保存准备执行的 async/await、promises、process.nextTick() 的回调。例如,已履行承诺的解析或拒绝回调在作业队列中排队。
任务队列(或宏任务) 是一种 FIFO(先进先出)结构,用于保存准备执行的异步操作(计时器,如 setInterval、setTimeout)的回调。例如,超时的回调(准备执行)在任务队列中排队。setTimeout()
事件循环永久监视调用堆栈是否为空。如果调用堆栈为空,则事件循环将查看作业队列或任务队列,并将准备执行的任何回调取消排队到调用堆栈中。
4. var、let 和 const 之间的区别?
在浏览器中,window 对象是浏览器的窗口,是 HTML 树中的顶部结构。使用 var 全局声明的变量将附加到 window 对象。在浏览器控制台中键入 var dog = 'bowser' ,然后键入 window.dog。 出现值“bowser”!这使得控制变量的范围变得更加困难。相比之下,let 和 const 不附加到 window 对象。
5. Javascript 中不同的数据类型?
JavaScript 是一种动态的松散类型或鸭子类型的语言。这意味着我们不需要指定变量的类型,因为 JavaScript 引擎会根据变量的值动态确定变量的数据类型。
JavaScript 中的基元数据类型是表示单个值的最基本数据类型。它们是不可变的(无法更改), 并直接保存特定值。
在 JavaScript 中,Symbol 是 ECMAScript 6 (ES6) 中引入的原始数据类型,它表示唯一且不可变的值。它通常用作对象属性的标识符,以避免名称冲突
const mySymbol = Symbol('key');
const obj = {
[mySymbol]: 'value'
};
当 Symbol 用作属性键时,它不会与其他属性键(包括字符串键)冲突。
6. 什么是回调函数和回调地狱?
在 JavaScript 中,回调通常用于处理异步操作。
回调函数是作为参数传递给另一个函数的函数,旨在在完成特定任务后或在给定时间执行。
function fetchData(url, callback) {
// Simulate fetching data from a server
setTimeout(() => {
const data = 'Some data from the server';
callback(data);
}, 1000);
}
function processData(data) {
console.log('Processing data:', data);
}
fetchData('https://example.com/data', processData);
在此示例中,fetchData 函数将 URL 和回调函数作为参数。从服务器获取数据(使用 setTimeout 模拟)后,它会调用回调函数并将检索到的数据传递给它。
回调地狱,也称为 “末日金字塔” ,是 JavaScript 编程中使用的一个术语,用于描述在异步函数中使用多个嵌套回调的情况。
“当异步操作依赖于先前异步操作的结果时,就会发生这种情况,从而导致深度嵌套且通常难以阅读的代码。”
Callback Hell 是一种具有多个嵌套回调的反模式,这使得代码在处理异步逻辑时难以读取和调试。
fs.readFile('file1.txt', 'utf8', function (err, data) {
if (err) {
console.error(err);
} else {
fs.readFile('file2.txt', 'utf8', function (err, data) {
if (err) {
console.error(err);
} else {
fs.readFile('file3.txt', 'utf8', function (err, data) {
if (err) {
console.error(err);
} else {
// Continue with more nested callbacks...
}
});
}
});
}
});
在此示例中,我们使用该函数按顺序读取三个文件,并且每个文件读取操作都是异步的。因此,我们必须将回调嵌套在彼此内部,从而创建回调的金字塔结构。fs.readFile
为了避免回调地狱,现代 JavaScript 提供了 Promise 和 async/await 等替代方案。 下面是使用 Promise 的相同代码:
const readFile = (file) => {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
readFile('file1.txt')
.then((data1) => {
return readFile('file2.txt');
})
.then((data2) => {
return readFile('file3.txt');
})
.then((data3) => {
// Continue with more promise-based code...
})
.catch((err) => {
console.error(err);
});
7. What is Promise and Promise chaining?
Promise: A Promise is an object in JavaScript used for asynchronous computations. It represents the result of an asynchronous operation, the result may be resolved or rejected.
Promises have three states:
- Pending: The initial state. This is the state in which the Promise’s eventual value is not yet available.
- Fulfilled: The state in which the Promise has been resolved successfully, and the eventual value is now available.
- Rejected: The state in which the Promise has encountered an error or has been rejected, and the eventual value cannot be provided.
Promise constructor has two parameters (resolve, reject) which are functions. If the async task has been completed without errors then call the resolve function with message or fetched data to resolve the promise.
If an error occurred then call the reject function and pass the error to it.
we can access the result of promise using .then() handler.
we can catch the error in the .catch() handler.
// Creating a Promise
const fetchData = new Promise((resolve, reject) => {
// Simulate fetching data from a server
setTimeout(() => {
const data = 'Some data from the server';
// Resolve the Promise with the retrieved data
resolve(data);
// Reject the Promise with an error
// reject(new Error('Failed to fetch data'));
}, 1000);
});
// Consuming the Promise
fetchData
.then((data) => {
console.log('Data fetched:', data);
})
.catch((error) => {
console.error('Error fetching data:', error);
});
Promise chaining: The process of executing a sequence of asynchronous tasks one after another using promises is known as Promise chaining.
It involves chaining multiple methods to a Promise to perform a series of tasks in a specific order. .then()
new Promise(function (resolve, reject) {
setTimeout(() => resolve(1), 1000);
})
.then(function (result) {
console.log(result); // 1
return result * 2;
})
.then(function (result) {
console.log(result); // 2
return result * 3;
})
.then(function (result) {
console.log(result); // 6
return result * 4;
});
8. What is async/await ?
Async/await is a modern approach to handling asynchronous code in JavaScript. It provides a more concise and readable way to work with Promises and async operations, effectively avoiding the “Callback Hell” and improving the overall structure of asynchronous code.
In JavaScript, the async keyword is used to define an asynchronous function, which returns a Promise.
Within an async function, the await keyword is used to pause the execution of the function until a Promise is resolved, effectively allowing for synchronous-looking code while working with asynchronous operations.
async function fetchData() {
try {
const data = await fetch('https://example.com/data');
const jsonData = await data.json();
return jsonData;
} catch (error) {
throw error;
}
}
// Using the async function
fetchData()
.then((jsonData) => {
// Handle the retrieved data
})
.catch((error) => {
// Handle errors
});
In this example, the fetchData function is defined as an async function, and it uses the await keyword to pause the execution and wait for the fetch and json operations, effectively working with Promises in a way that resembles synchronous code.
9. What is the difference between == and === operators ?
== (Loose Equality Operator): This operator performs type coercion, which means it converts the operands to the same type before making the comparison. It checks if the values are equal without considering their data types. For example, will return because JavaScript converts the string to a number before comparison.1 == '1'``true``'1'
=== (Strict Equality Operator): This operator performs a strict comparison without type coercion. It checks if both the values and their data types are equal. For example, will return because the data types are different (number and string).1 === '1'``false
In summary, checks for equality after type coercion, whereas checks for strict equality, considering both the values and their data types.==``===
Execution of == will be fast as compared to the === statement.
Some of the example that covers the above cases:
0 == false // true
0 === false // false
1 == "1" // true
1 === "1" // false
null == undefined // true
null === undefined // false
'0' == false // true
'0' === false // false
[]==[] or []===[] //false, refer different objects in memory
{}=={} or {}==={} //false, refer different objects in memory
10. Different ways to create an Object in Javascript ?
In JavaScript, there are several ways to create objects. Some common methods for object creation include:
a)Object Literals: The most straightforward way to create an object is by using object literals, which define an object’s properties and methods in a comma-separated list enclosed in curly braces.
let person = {
firstName: 'John',
lastName: 'Doe',
greet: function() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
}
};
b)Constructor Function: Constructor functions can be used to create multiple instances of an object with the new keyword. Inside the constructor function, properties and methods can be assigned to the this keyword.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.greet = function() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
};
}
let person1 = new Person('John', 'Doe');
let person2 = new Person('Jane', 'Smith');
c)Object.create() : The Object.create() method allows you to create a new object with a specified prototype object. This method provides more control over the prototype of the newly created object.
let personProto = {
greet: function() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
}
};
let person = Object.create(personProto);
person.firstName = 'John';
person.lastName = 'Doe';
d)Class Syntax (ES6) : With the introduction of ES6, JavaScript supports class syntax for defining objects using the class keyword. This provides a more familiar and structured way to create objects and define their properties and methods.
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
greet() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
}
}
let person = new Person('John', 'Doe');
e)Factory Functions: Factory functions are functions that return an object. This approach allows you to encapsulate the object creation process and easily create multiple instances with custom properties.
function createPerson(firstName, lastName) {
return {
firstName: firstName,
lastName: lastName,
greet: function() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
}
};
}
let person1 = createPerson('John', 'Doe');
let person2 = createPerson('Jane', 'Smith');
f)Object.setPrototypeOf() : The Object.setPrototypeOf() method can be used to set the prototype of a specified object. This offers an alternative approach to setting the prototype of an object after its creation.
let personProto = {
greet: function() {
return 'Hello, ' + this.firstName + ' ' + this.lastName;
}
};
let person = {};
person.firstName = 'John';
person.lastName = 'Doe';
Object.setPrototypeOf(person, personProto);
g)Object.assign() : The Object.assign() method can be used to create a new object by copying the values of all enumerable own properties from one or more source objects to a target object. This is particularly useful for merging objects or creating a shallow copy.
let target = { a: 1, b: 2 };
let source = { b: 3, c: 4 };
let mergedObject = Object.assign({}, target, source);
h)Prototype Inheritance: JavaScript uses prototypal inheritance, allowing objects to inherit properties and methods from other objects. You can create objects by leveraging prototypal inheritance and using the prototype property of constructor functions or classes to define shared behavior.
function Animal(name) {
this.name = name;
}
Animal.prototype.greet = function() {
return 'Hello, I am ' + this.name;
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
let myDog = new Dog('Max', 'Poodle');
i)Singleton Pattern: The singleton pattern is used to restrict an object to a single instance. It can be implemented in JavaScript using a combination of closures and immediately invoked function expressions (IIFE). This ensures that only one instance of the object is created.
let singleton = (() => {
let instance;
function createInstance() {
return {
// properties and methods
};
}
return {
getInstance: () => {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
11. What is rest and spread operator?
The rest operator, represented by three dots (), is used in function parameters to collect a variable number of arguments into an array. It allows you to pass an arbitrary number of arguments to a function without explicitly defining them as named parameters. ...
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // Outputs 10
The spread operator, also denoted by three dots (), is used to spread the elements of an array or object into another array or object. It allows you to easily clone arrays, concatenate arrays, and merge objects....
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const mergedArray = [...array1, ...array2];
// mergedArray is [1, 2, 3, 4, 5, 6]
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const mergedObject = { ...obj1, ...obj2 };
// mergedObject is { a: 1, b: 3, c: 4 }
12. What is a higher-order function?
JavaScript 中的高阶函数是将一个或多个函数作为参数或返回函数作为其结果的函数。换句话说,它对函数进行操作,要么将它们作为参数,要么返回它们,或者两者兼而有之。
function operationOnArray(arr, operation) {
let result = [];
for (let element of arr) {
result.push(operation(element));
}
return result;
}
function double(x) {
return x * 2;
}
let numbers = [1, 2, 3, 4];
let doubledNumbers = operationOnArray(numbers, double);
console.log(doubledNumbers); // Output: [2, 4, 6, 8]
它们支持强大的技术,例如函数组合、咖喱和基于回调的异步操作。理解高阶函数对于编写富有表现力和函数式风格的 JavaScript 代码至关重要。
一元函数(即一元函数)是一个只接受一个参数的函数。 它代表函数接受的单个参数。
13. 什么是闭合?闭包有哪些用例?
闭包是一项功能,它允许函数捕获定义它的环境(或保留对作用域中变量的访问),即使在该作用域关闭之后也是如此。
我们可以说闭包是定义该函数的函数和词法环境的组合。
换句话说,闭包使函数能够访问其自身的作用域、外部函数的作用域和全局作用域,从而允许它“记住”并继续访问这些作用域中的变量和参数。
function outerFunction() {
let outerVariable = 'I am from the outer function';
return innerFunction() {
console.log(outerVariable); // Accessing outerVariable from the outer function's scope
}
}
let myFunction = outerFunction();
myFunction(); // Output: I am from the outer function
每次在创建函数时创建函数以及在另一个函数中定义函数时,都会创建闭包。
执行上下文是执行 JavaScript 代码的环境。对于每个函数调用,都会创建一个单独的执行上下文,并将其推送到执行堆栈中。函数执行完成后,它将从堆栈中弹出。
每个执行上下文在内存中都有一个空间来存储其变量和函数,一旦函数从执行堆栈中弹出,JavaScript 垃圾收集器就会清除所有这些东西。
在 JavaScript 中,只有当没有对它的引用时,才会对任何内容进行垃圾回收。
在上面的示例中,匿名执行上下文仍然具有对其外部环境的内存空间的变量的引用。即使 outerFunction() 已完成。(它可以访问 outerVariable 变量并在 console.log(outerVariable)) 中使用它)。
闭包在 JavaScript 中有几个重要的用例:
- 数据隐私和封装:闭包可用于在有限的范围内创建私有数据和封装功能。通过在另一个函数中定义函数,内部函数可以访问外部函数的变量,但这些变量无法从外部函数外部访问。这允许创建无法从外部直接访问的私有数据和方法,从而增强数据隐私和封装。
- 维护状态:闭包通常用于维护异步操作和事件处理中的状态。例如,在处理异步任务时,闭包可以捕获和保留多个异步操作中的变量状态,从而确保在异步任务完成时访问正确的变量。
- 咖喱和部分应用:闭包促进了功能编程技术,如咖喱和部分应用。通过使用闭包来捕获和记住特定参数,并返回使用这些捕获参数的新函数,可以实现咖喱和部分应用。这允许使用预设参数创建专用函数,从而提供灵活性和可重用性。
- 模块模式:闭包对于在 JavaScript 中实现模块模式至关重要。通过使用闭包创建私有变量并仅公开必要的公共方法,开发人员可以创建模块化和有组织的代码,从而防止对内部模块数据进行不必要的访问和修改。
- 回调函数:在使用回调函数时,通常使用闭包。闭包可用于在异步操作的上下文中捕获和维护变量的状态,确保在调用回调函数时可以访问正确的变量。
14. 解释 JavaScript 中吊装的概念。
在 JavaScript 中提升是默认行为,在编译阶段,在实际代码执行之前,变量和函数声明被移动到其包含范围的顶部。这意味着,在代码中声明变量之前,可以使用变量或调用函数。
当您使用 声明变量时,该声明将被提升到其包含函数或块的顶部,并使用默认值“undefined”进行初始化。 var
console.log(x); // Outputs: undefined
var x = 5;
使用 和 声明的变量也被吊起,但有一个“时间盲区”,在声明之前无法访问它们。let``const
console.log(x); // Throws an error (ReferenceError)
let x = 5;
函数声明也被提升到其包含范围的顶部。可以在代码中声明函数之前调用函数。
sayHello(); // Outputs: "Hello, world!"
function sayHello() {
console.log("Hello, world!");
}
Hoisting is not happening with an arrow function, function expression, or variable initialization.
15. What is a Temporal dead zone?
The Temporal Dead Zone (TDZ) is a concept in JavaScript related to variable declarations using and .let``const
When you declare a variable with or , it is hoisted to the top of its containing scope, However, unlike , variables declared with and remain uninitialized in the TDZ.let``const``var``let``const
Any attempt to access or use the variable before its actual declaration within the scope will result in a . This is to prevent the use of variables before they have been properly defined.ReferenceError
Understanding the Temporal Dead Zone is important because it helps prevent bugs related to variable usage before initialization. It also promotes best practices in JavaScript coding by encouraging proper variable declarations before use.
16. What is a prototype chain? and Object.create() method?
In JavaScript, every function and object has a property named prototype by default.
Every object in JavaScript has a prototype. A prototype is another object from which the current object inherits properties and methods. You can think of the prototype as a template or a parent object.
The prototype chain is a mechanism that allows objects to inherit properties and methods from other objects
When you access a property or method on an object, JavaScript first looks for it on the object itself. If it doesn’t find it, it looks up the prototype chain until it finds the property or method. This process continues until it reaches the at the top of the chain.Object.prototype
17. What is the difference between Call, Apply, and Bind methods?
Call: The call() method invokes a function with a specified value and individual arguments passed as comma-separated valuesthis
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
function greet(greeting) {
console.log(greeting + ' ' + this.name);
}
greet.call(person1, 'Hello'); // Output: Hello John
greet.call(person2, 'Hi'); // Output: Hi Jane
With call() method an object can use a method belonging to another object.
const o1 = {
name: 'ravi',
getName: function(){
console.log(`Hello, ${this.name}`)
}
}
const o2 = {
name: 'JavaScript Centric'
}
o1.getName.call(o2) // Hello, JavaScript Centric
Apply: Invokes the function with a given value but it accepts arguments as an array. It is useful when the number of arguments to be passed is not known in advance or when the arguments are already in an array.this
const numbers = [1, 2, 3, 4, 5];
const max = Math.max.apply(null, numbers);
console.log(max); // Output: 5
bind: instead of invoking it returns a new function and allows you to pass any number of arguments. bind() method takes an object as first argument and create a new function.
const module = {
x: 42,
getX: function() {
return this.x;
}
};
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // Output: 42
18. What are lambda or arrow functions?
There are two types of functions in JavaScript
- Regular Functions
- Arrow Functions (Introduced in ES6)
Regular Function: We can write the regular function in two ways, i.e. Function declaration, and Function expression.
Arrow or Fat Arrow Function: Lambda functions, also known as arrow functions, are a feature introduced in JavaScript (ES6) that is a more concise syntax for writing function expressions. They have a shorter syntax compared to traditional function expressions and are particularly useful for creating anonymous functions and working with functional programming concepts.
There is no declaration approach here, we can write by using Function expressions only.
Arrow 和 Regular 函数之间存在某些差异,即
- 语法
- 无参数 (参数是类似数组的对象)
- 没有 Arrow 函数的原型对象
- 不能使用 new 关键字 (不是构造函数) 调用
- 没有这个 (调用,应用和绑定将无法按预期工作)
- 它不能用作生成器功能
- 不允许使用重复命名的参数
19. 什么是咖喱功能?
Currying 是函数式编程中的一种技术 ,它将具有多个参数的函数转换为一系列函数,每个函数都采用一个参数。 这些咖喱函数可以组合在一起以构建更复杂的函数。
在 JavaScript 中,您可以使用闭包和返回函数来实现咖喱。
// Regular function taking two arguments
function add(x, y) {
return x + y;
}
// Curried version of the function
function curryAdd(x) {
return function(y) {
return x + y;
};
}const add5 = curryAdd(5); // Partial application, creates a new function
console.log(add5(3)); // Output: 8
Currying 在函数式编程中是有益的,可以使代码更加模块化和可重用。在想要创建具有不同数量参数的函数或生成数据转换管道的方案中,它特别有用。
20. ES6有哪些特点?
ES6,也称为 ECMAScript 2015,为 JavaScript 引入了一些新特性和增强功能,显着扩展了该语言的功能。ES6 的一些主要功能包括:
- 箭头函数
- 块范围的变量
- 类
- 模块
- 模板文字:模板文字允许使用反引号嵌入表达式和多行字符串,从而提供了一种更方便的方式在 JavaScript 中创建复杂字符串。
- 默认参数
- 休息和传播运算符
- 解构赋值
- 承诺
- Map、Set、WeakMap、WeakSet:ES6 引入了新的内置数据结构,如 Map 和 Set,用于更高效、更专业地处理集合和键值对。
- 迭代器和生成器
- 增强的对象文本
21. 什么是执行上下文、执行堆栈、变量对象和范围链?
执行上下文: 执行上下文是指执行一段代码的环境。它由作用域、变量对象和“this”关键字的值组成。
每当执行函数时,都会创建一个执行上下文,它包含该函数的所有变量或属性。
JavaScript 中有三种类型的执行上下文:
全局执行上下文
功能执行上下文
评估函数执行上下文
执行堆栈: 它也被称为“调用堆栈”,这是一种 LIFO(后进先出)数据结构,用于存储正在进行的函数调用的所有执行上下文。调用函数时,将创建一个新的执行上下文并将其推送到堆栈上。当函数完成时,其上下文将从堆栈中弹出。
引擎执行其执行上下文位于堆栈顶部的函数。当此函数完成时,其执行堆栈将从堆栈中弹出,并且控件将到达当前堆栈中其下方的上下文。
执行上下文是在创建阶段创建的。在创建阶段会发生以下情况:
- 创建 LexicalEnvironment 组件。
- VariableEnvironment 组件已创建。
变量对象: 它是执行上下文的一部分,其中包含该上下文中定义的所有变量、函数声明和参数。
范围链: 作用域链是一种用于解析 JavaScript 中变量值的机制。引用变量时,JavaScript 引擎首先在当前执行上下文的变量对象中查找该变量。如果在那里找不到它,它会继续到下一个外部执行上下文,跟随作用域链,直到找到变量或到达全局执行上下文。
22. callback、promise、setTimeout、process.nextTick()的执行优先级是多少?
可以根据事件循环和处理不同异步操作的顺序来理解执行的优先级:
- process.nextTick(): 调度的回调具有最高优先级。使用 时,回调将在当前操作完成后、事件循环进入下一阶段之前立即执行。这使它成为确保在事件循环中尽早执行函数的一种方式。
process.nextTick()``process.nextTick() - 承诺: 承诺通常在 之后执行。但是,它们的优先级优先于使用 .
process.nextTick()``setTimeout() - setTimeout(): 安排的回调放置在事件循环的计时器阶段。它们将在当前操作、承诺和任何先前计划的回调完成后执行。
setTimeout()``setTimeout() - 回调: 常规回调(未使用 计划)的优先级最低。它们在事件循环进程、承诺和回调之后执行。
process.nextTick()``process.nextTick()``setTimeout()
23. 什么是工厂功能和发电机功能?
JavaScript 中的工厂函数是返回对象的函数。它是一种用于以简单和有组织的方式创建对象的模式。工厂函数不使用构造函数和 new 关键字来创建新对象,而是封装对象创建过程并返回一个新对象。**
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function() {
return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
}
};
}
const person1 = createPerson('Alice', 25);
const person2 = createPerson('Bob', 30);
console.log(person1.greet()); // Output: Hello, my name is Alice and I am 25 years old.
console.log(person2.greet()); // Output: Hello, my name is Bob and I am 30 years old.
JavaScript 中的 Generator 函数是一种特殊类型的函数 ,可以在其执行过程中暂停和恢复。
生成器函数生成一系列结果,而不是单个值。
当生成器函数调用时,它会返回一个 生成器对象,该对象可用于通过调用 next() 方法控制函数的执行。
可以使用 yield 关键字在正文中暂停函数的代码,以后可以从暂停的确切点恢复。
function* numberGenerator() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = numberGenerator();
console.log(gen.next().value); // Output: 0
console.log(gen.next().value); // Output: 1
console.log(gen.next().value); // Output: 2
这为创建迭代器和处理异步代码提供了强大的机制。
24. 克隆对象(对象的浅层和深度副本)的不同方法?
浅拷贝是对象的副本,其引用与原始对象相同。这意味着,如果更改浅拷贝中的属性值,则也会更改原始对象中属性的值。
const user = {
name: "Kingsley",
age: 28,
job: "Web Developer"
}
const clone = user
深层副本是对象的副本,其引用与原始对象不同。这意味着,如果更改深层拷贝中的属性值,则不会更改原始对象中的属性值。
有不同的方法可以创建对象的深层副本。
a)JSON.parse 和 JSON.stringify:对嵌套对象也很有用。
const originalObject = { name: "Alice", age: 25 };
const deepCopy = JSON.parse(JSON.stringify(originalObject));
b)结构化克隆:
const myDeepCopy = structuredClone(myOriginal);
c)Spread Operator(...): 任何带有嵌套对象的对象都不会被深度复制。
const originalObject = { name: "Alice", age: 25 };
const deepCopy = {...originalObject};
deepCopy.name = "ravi"
console.log("originalObject", originalObject.name) // Alice
d)Object.assign(): 该方法应用于深度复制没有嵌套对象的对象。Object.assign()
const originalObject = { name: "Alice", age: 25 };
const shallowCopy = Object.assign({}, originalObject);
e)递归:
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
newObj[key] = deepCopy(obj[key]);
}
}
return newObj;
}
const originalObject = { name: "Alice", nested: { age: 25 } };
const deepCopy = deepCopy(originalObject);
25. 如何使对象不可变?(密封和冷冻方法)?
在 JavaScript 中,您可以使用 Object.seal() 和 Object.freeze() 方法使对象不可变。
Object.freeze() :(完全不可变) 此方法冻结对象,使其既密封又将其所有属性标记为只读。冻结对象后,无法修改、添加或删除其属性。
const obj = { name: 'Alice', age: 25 };
Object.freeze(obj);
obj.name = 'Bob'; // Not allowed
obj.address = '123 Street'; // Not allowed
delete obj.age; // Not allowed
Object.seal():(部分不可变) 此方法密封对象,防止添加新属性并将所有现有属性标记为不可配置。但是,您仍然可以修改可写的现有属性的值。
const obj = { name: 'Alice', age: 25 };
Object.seal(obj);
obj.name = 'Bob'; // Allowed
obj.address = '123 Street'; // Not allowed (no new properties can be added)
delete obj.age; // Not allowed (existing properties cannot be deleted)
26. 什么是事件和事件流、事件冒泡和事件捕获?
在 JavaScript 中,事件流是在网页上接收单击或按键等事件或由 Web 浏览器处理的顺序。事件流分为两个阶段:事件捕获和事件冒泡。
单击嵌套在各种其他元素中的元素时,在单击实际到达其目标或目标元素之前,它必须首先触发其每个父元素的 click 事件,从顶部的全局窗口对象开始。
<div id="parent">
<button id="child">Click me!</button>
</div>
现在,让我们用这个例子来解释事件流:
- 事件捕获阶段: 单击该按钮时,事件将从顶部(文档的根目录)开始其旅程,并向下移动到目标元素。在这种情况下,它从文档的根目录传输到 (父元素),然后传输到 (子元素)。这称为捕获阶段。
<div>``<button> - 事件目标阶段: 事件到达目标元素,在本例中为 。
<button> - 事件冒泡阶段: 到达目标后,事件开始冒泡。它从文档的背面到文档的根目录。这称为冒泡阶段。
<button>``<div>
下面是一个简单的 JavaScript 代码片段,可以看到它的实际效果:
document.getElementById('parent').addEventListener('click', function() {
console.log('Div clicked (capturing phase)');
}, true); // The 'true' here indicates capturing phase.
document.getElementById('child').addEventListener('click', function() {
console.log('Button clicked (target phase)');
});document.getElementById('parent').addEventListener('click', function() {
console.log('Div clicked (bubbling phase)');
});
单击该按钮时,您将按以下顺序在控制台中看到这些消息:
- “Div clicked(捕获阶段)”
- “单击按钮(目标阶段)”
- “Div clicked (bubbling phase)”
27. 什么是活动委派?
事件委派是一种 JavaScript 编程技术 ,用于优化多个元素的事件处理。
事件委派不是将事件侦听器附加到每个单独的元素,而是将单个事件侦听器附加到 DOM(文档对象模型)层次结构中较高的公共祖先元素。
当事件发生在其中一个后代元素上时,它会“冒泡”到事件侦听器正在等待的共同祖先。
事件委派是一种侦听事件的技术,在该技术中,您将父元素委派为其中发生的所有事件的侦听器。
var form = document.querySelector("#registration-form");
// Listen for changes to fields inside the form
form.addEventListener(
"input",
function (event) {
// Log the field that was changed
console.log(event.target);
},
false
);
28. 什么是服务器发送的事件?
服务器发送事件 (SSE) 是一种简单高效的技术,用于通过单个 HTTP 连接实现从服务器到客户端的实时更新。
SSE 允许服务器在有新信息可用时立即将数据推送到 Web 客户端(通常是浏览器),使其成为需要实时更新而无需依赖复杂协议或第三方库的场景的绝佳选择。
a)SSE 提供从服务器到客户端的单向数据流。服务器启动通信,向客户端发送更新。
b)SSE 使用基于文本的协议, 这意味着从服务器发送到客户端的数据通常采用文本格式(通常是 JSON 或纯文本)。
c)SSE handles reconnection automatically.
d)SSE establishes a persistent connection between the client and the server, allowing the server to send a stream of events to the client. Each event can have a unique type and data associated with it.
e)The EventSource object is used to receive server-sent event notifications. For example, you can receive messages from server as below,
if (typeof EventSource !== "undefined") {
var source = new EventSource("sse_generator.js");
source.onmessage = function (event) {
document.getElementById("output").innerHTML += event.data + "<br>";
};
}
f)以下是可用于服务器发送的事件(onopen、onmessage、onerror)列表。
29. javascript 中的 Web Worker 或 Service Worker 是什么?
Web Worker 和 Service Worker 是 JavaScript 中的两个不同概念,
Web Worker 设计用于在后台并发执行 JavaScript,而 Service Worker 用于创建具有脱机功能和高级功能的渐进式 Web 应用。两者都是增强 Web 应用程序性能和功能的重要工具。
每个在 Web 开发中都有不同的目的:
网络工作者:
- 并发性: Web Workers 是一种浏览器功能,允许您在后台运行 JavaScript 代码,与主浏览器线程分开。 这样可以在不阻塞用户界面的情况下并发执行任务。
- 使用案例: Web Worker 通常用于计算密集型或耗时的任务,例如数据处理、图像处理或复杂计算。通过在单独的线程中运行这些任务,它们不会影响网页的响应能力。
- 通信: Web Worker 可以使用消息传递系统与主线程进行通信。它们可以发送和接收消息,从而允许主线程和工作线程之间的协调。
- 浏览器支持: Web Worker 在大多数现代浏览器中都受支持。
服务人员:
- 离线功能: Service Worker 是用于创建渐进式 Web 应用 (PWA) 的更高级功能。它们充当在后台运行的代理服务器,可以拦截和缓存网络请求。这将启用脱机功能,例如在用户脱机时提供缓存的内容。
- 使用案例: Service Worker 主要用于实现离线访问、 推送通知和后台同步等功能。它们使 Web 应用程序即使在没有 Internet 连接时也能正常运行。
- 生命周期:Service Worker 有自己的生命周期,其中包含 、 和 等事件。它们通常在 Web 应用生命周期开始时注册。
install``activate``fetch - 浏览器支持:现代浏览器支持 Service Worker,是创建可靠且引人入胜的 Web 应用程序的关键技术。
30. 如何在 javascript 中比较 2 个 JSON 对象?
a) 比较两个 JSON 对象的一种简单方法是使用 JSON.stringify 将它们转换为字符串,然后比较字符串。
function areEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
console.log(areEqual(obj1, obj2)); // Output: true
b) You can use the Ramda library to compare two JSON objects as well. Ramda provides a function called equals for this purpose.
const R = require('ramda');
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
console.log(R.equals(obj1, obj2)); // Output: true
c) 另一种选择是使用库,例如 Lodash,它提供了一种对对象进行深入比较的方法。
const _ = require('lodash');
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
console.log(_.isEqual(obj1, obj2)); // Output: true
希望你喜欢这篇文章。非常感谢您的阅读。
在 Medium 上关注我以获取更多指导文章。
在LinkedIn上关注我:www.linkedin.com/in/ravics09…