There are always ups and downs in a person's life. It will not always rise like the rising sun, nor will it always be miserable. Repeated ups and downs are training for a person. Therefore, those who are floating above do not need to be proud; those who are sinking below do not need to be pessimistic. We must be frank and humble, optimistic and enterprising, and move forward. ——Konosuke Matsushita
Hello everyone, my name is Jiang Chen. In today's Internet environment, everyone must have felt it to some extent. In this impetuous society, only by constantly maintaining one's character can one perceive different gains and encourage each other.
A collection of the latest interview questions in 2023, so be prepared at all times.
This article was first published on WeChat public account: Wild Programmer Jiang Chen
Everyone is welcome to like, collect and follow
Article list
- 2023 front-end interview questions - JS
- 2023 front-end interview questions - CSS
- 2023 front-end interview questions - HTML
- 2023 front-end interview questions - React
- 2023 front-end interview questions - Vue chapter
Implement a simple version of Promise
The following is a basic Promise implementation:
class MyPromise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.onResolveCallbacks = [];
this.onRejectCallbacks = [];
const resolve = (value) => {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onResolveCallbacks.forEach((callback) => callback(this.value));
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.value = reason;
this.onRejectCallbacks.forEach((callback) => callback(this.value));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.value);
} else {
this.onResolveCallbacks.push(onFulfilled);
this.onRejectCallbacks.push(onRejected);
}
}
}
// 示例用法
const promise = new MyPromise((resolve, reject) => {
// 异步操作,比如请求数据
setTimeout(() => {
resolve('成功');
// 或者 reject('失败');
}, 1000);
});
promise.then(
(value) => {
console.log('成功:', value);
},
(reason) => {
console.log('失败:', reason);
}
);
This is a very basic Promise implementation, just for demonstration purposes. In actual applications, more details and error handling need to be considered. Modern JavaScript has Promises built into it, and there's usually no need to implement it manually.
Implement function throttling
Function throttling is a technique for controlling the frequency of function execution to ensure that the function is executed at most once within a certain time interval. The following is a simple JavaScript function throttling implementation:
function throttle(func, delay) {
let timerId;
let lastExecTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastExecTime >= delay) {
func.apply(this, args);
lastExecTime = now;
} else {
clearTimeout(timerId);
timerId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
}, delay);
}
};
}
The above throttle
function accepts two parameters: func
the function to be throttled, delay
and the time interval (in milliseconds) for execution.
Using this throttling function, you can wrap functions that need to be throttled to ensure that they are not executed frequently in a short period of time. For example:
// 原始函数,可能会频繁触发
function handleResize() {
console.log('窗口大小改变了');
}
// 使用节流包装后的函数
const throttledResize = throttle(handleResize, 200); // 200毫秒的节流间隔
// 监听窗口大小改变事件,使用节流函数
window.addEventListener('resize', throttledResize);
Now, handleResize
the function will execute at most once every 200 milliseconds, no matter how often the window size changes. This helps reduce frequent function calls and improve performance.
Implement function anti-shake
Function anti-shake is a technology that controls the execution frequency of functions to ensure that the function is only executed once within a certain time interval. The following is a simple JavaScript function anti-shake implementation:
function debounce(func, delay) {
let timerId;
return function (...args) {
clearTimeout(timerId);
timerId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
The above debounce
function accepts two parameters: func
the function to be anti-shake, delay
and the waiting time interval (in milliseconds).
Using this debounce function, you can wrap functions that need to be debounced to ensure that they are only executed after a certain time interval. For example:
// 原始函数,可能会频繁触发
function handleInput(value) {
console.log('输入值为:', value);
}
// 使用防抖包装后的函数
const debouncedInput = debounce(handleInput, 300); // 300毫秒的防抖间隔
// 监听输入事件,使用防抖函数
document.querySelector('input').addEventListener('input', (event) => {
debouncedInput(event.target.value);
});
Functions will now handleInput
execute 300 milliseconds after the user stops typing, reducing frequent function calls and improving performance.
Implement the observer pattern
The Observer pattern is a design pattern in which a subject (the observer) maintains a list of observers and notifies the observers when the state changes. The following is a simple implementation of the observer pattern in JavaScript:
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${
this.name} 收到更新,数据为:`, data);
}
}
// 示例用法
const subject = new Subject();
const observer1 = new Observer('观察者1');
const observer2 = new Observer('观察者2');
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('新数据更新了'); // 观察者1 收到更新,数据为: 新数据更新了
// 观察者2 收到更新,数据为: 新数据更新了
subject.removeObserver(observer1);
subject.notify('又有新数据更新了'); // 只有观察者2会收到更新
The above code creates a simple implementation of the Observer pattern, including a subject class Subject
and an observer class Observer
. Topics can add and remove observers and notify all observers when state changes.
In the example, we create a topic subject
and add two observers observer1
and observer2
. When the topic state changes, it notifies all observers.
This is just a basic example; in practice, you may need a more complex implementation to meet your specific needs.
Implement publish-subscribe model
Subscriber pattern, also known as publish-subscribe pattern, is a design pattern in which a topic (publisher) maintains a list of subscribers and notifies all subscribers when an event occurs. The following is a simple implementation of the JavaScript subscriber pattern:
class Publisher {
constructor() {
this.subscribers = [];
}
subscribe(subscriber) {
this.subscribers.push(subscriber);
}
unsubscribe(subscriber) {
this.subscribers = this.subscribers.filter(sub => sub !== subscriber);
}
publish(eventData) {
this.subscribers.forEach(subscriber => subscriber.notify(eventData));
}
}
class Subscriber {
constructor(name) {
this.name = name;
}
notify(eventData) {
console.log(`${
this.name} 收到通知,事件数据为:`, eventData);
}
}
// 示例用法
const publisher = new Publisher();
const subscriber1 = new Subscriber('订阅者1');
const subscriber2 = new Subscriber('订阅者2');
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
publisher.publish('新事件发生了'); // 订阅者1 收到通知,事件数据为: 新事件发生了
// 订阅者2 收到通知,事件数据为: 新事件发生了
publisher.unsubscribe(subscriber1);
publisher.publish('又有新事件发生了'); // 只有订阅者2会收到通知
In the above code, we created a simple subscriber pattern implementation, including a publisher class Publisher
and a subscriber class Subscriber
. Publishers can add and remove subscribers and notify all subscribers when events occur.
In the example, we create a publisher publisher
and add two subscribers subscriber1
and subscriber2
. When a publisher publishes an event, it notifies all subscribers.
This is just a basic example; in practice, you can extend the subscriber pattern as needed to meet your specific needs.
Implement new keyword
To implement new
the basic functionality of the operator in JavaScript, you write a function that accepts a constructor and constructor arguments and returns a new object instance. Here is an example implementation:
function myNew(constructor, ...args) {
// 创建一个新对象,并将其原型指向构造函数的原型
const obj = Object.create(constructor.prototype);
// 调用构造函数,将新对象绑定到构造函数的上下文中
const result = constructor.apply(obj, args);
// 如果构造函数返回的是一个对象,则返回该对象;否则返回新创建的对象
return typeof result === 'object' ? result : obj;
}
You can then use myNew
functions to simulate new
the behavior of the operator. For example:
function Person(name, age) {
this.name = name;
this.age = age;
}
// 使用 myNew 模拟 new 操作符
const person1 = myNew(Person, 'Alice', 30);
const person2 = myNew(Person, 'Bob', 25);
console.log(person1); // 输出: Person { name: 'Alice', age: 30 }
console.log(person2); // 输出: Person { name: 'Bob', age: 25 }
This myNew
function first creates a new object obj
and then points the new object's prototype to the constructor constructor
's prototype. Next, it calls the constructor and binds the new object to the constructor's context. Finally, it checks the return value of the constructor and returns that object if it is an object, otherwise it returns the newly created object.
This is a simple new
mock implementation of the operator. In reality, new
more complex features such as prototype chains are involved, but this example can demonstrate the basic principles.
Implement DeepClone
Deep clone is an operation that not only copies the object itself, but also recursively copies all nested objects and properties within the object. The following is a simple JavaScript deep copy implementation example:
function deepClone(obj, hash = new WeakMap()) {
// 如果是基本数据类型或 null,则直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 如果已经拷贝过这个对象,则直接返回之前的拷贝结果,防止循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 根据对象的类型创建新的对象
const clone = Array.isArray(obj) ? [] : {
};
// 将新对象添加到哈希表
hash.set(obj, clone);
// 递归拷贝对象的属性
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
This deepClone
function can deep copy complex data types including objects, arrays, and nested structures. It uses a hash table hash
to prevent circular references and ensure that it does not fall into infinite recursion.
Example usage:
const originalObj = {
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'New York'
}
};
const clonedObj = deepClone(originalObj);
console.log(clonedObj); // 输出深拷贝后的对象
console.log(originalObj === clonedObj); // 输出 false,说明是不同的对象
Please note that this is just a simple deep copy implementation example, and actual applications may require more complex processing to deal with various data types and situations.
Implement function Curry
Function currying is a technique that converts a function that accepts multiple parameters into a series of functions that accepts a single parameter. The following is a simple implementation example of JavaScript function currying:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function (...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
This curry
function takes a function fn
and returns a curried function. When the curried function is called, it checks whether the number of arguments passed in is sufficient to execute the original function fn
. If there are enough parameters, it will call it directly fn
; if there are not enough parameters, it will return a new function, wait for more parameters to be passed in, and continue to append parameters until there are enough parameters.
Example usage:
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 输出 6
console.log(curriedAdd(1, 2)(3)); // 输出 6
console.log(curriedAdd(1)(2, 3)); // 输出 6
In the example, we first use curry
the function to add
curry the function, which can then be called in a variety of ways curriedAdd
to implement the addition operation.
This is just a simple implementation example of function currying. In actual applications, you may need more complex processing to deal with different functions and parameters.
Implement Call
call
is a method used in JavaScript to call a function, which allows you to specify this
values inside the function and pass parameters. The following is a simple call
mock implementation of a JavaScript method:
Function.prototype.myCall = function (context, ...args) {
// 如果没有传递上下文对象,则使用全局对象(浏览器环境下为 window)
context = context || globalThis;
// 将当前函数作为上下文对象的一个属性
const uniqueKey = Symbol('uniqueKey');
context[uniqueKey] = this;
// 调用函数,并传递参数
const result = context[uniqueKey](...args);
// 删除临时属性
delete context[uniqueKey];
return result;
};
This simulated myCall
method can be added to Function.prototype
so that all functions can call it. It accepts a context object context
and a series of parameters args
.
Example usage:
function greet(greeting) {
console.log(`${
greeting}, ${
this.name}`);
}
const person = {
name: 'Alice' };
// 使用 myCall 来调用 greet 函数,并指定上下文对象为 person
greet.myCall(person, 'Hello'); // 输出: Hello, Alice
In the example, we myCall
call greet
the function through the method and specify person
the object as the context object, which makes the object this
pointed to inside the function person
.
Please note that this is just a simple call
method mock implementation, the actual call
method can also handle many more parameters and special cases.
Implement array flattening
In JavaScript, you can flatten an array using recursion or looping. Here are some ways to flatten an array:
Recursive method:
function flattenArray(arr) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
// 如果当前元素是数组,递归拍平
result = result.concat(flattenArray(arr[i]));
} else {
// 如果不是数组,直接添加到结果数组中
result.push(arr[i]);
}
}
return result;
}
const nestedArray = [1, [2, [3, 4], 5], 6];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5, 6]
How to use reduce
:
function flattenArray(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flattenArray(toFlatten) : toFlatten);
}, []);
}
const nestedArray = [1, [2, [3, 4], 5], 6];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5, 6]
Using ES6 Array.flat
:
const nestedArray = [1, [2, [3, 4], 5], 6];
const flattenedArray = nestedArray.flat(Infinity);
console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5, 6]
All of the above methods can flatten nested arrays into a one-dimensional array. Which method you choose depends on your project needs and compatibility requirements. If your environment supports the ES6 Array.flat
approach, that's the easiest way. If you need to be compatible with older environments, you can use recursion or reduce
methods.
Encapsulating Hooks timer
To encapsulate a custom Hooks timer that can be shared among multiple components, you can create a useTimer
custom Hook called. Here is an example:
import {
useState, useEffect } from 'react';
function useTimer(initialCount = 0, interval = 1000) {
const [count, setCount] = useState(initialCount);
useEffect(() => {
const timer = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, interval);
// 在组件卸载时清除定时器
return () => {
clearInterval(timer);
};
}, [interval]);
return count;
}
export default useTimer;
This useTimer
custom Hooks accepts two parameters: initialCount
(initial count value, default is 0) and interval
(timer interval, default is 1000 milliseconds). It returns a status variable representing the timer count value count
.
You can use it in multiple components useTimer
to create timers. Here is an example:
import React from 'react';
import useTimer from './useTimer'; // 导入自定义Hooks
function TimerComponent() {
const count = useTimer(); // 使用自定义Hooks创建定时器
return (
<div>
<h1>定时器示例</h1>
<p>计数:{
count}</p>
</div>
);
}
export default TimerComponent;
In the above example, we imported custom Hooks useTimer
and then TimerComponent
used it in the component to create a timer. Each used useTimer
component will have its own timer independently, but they can share the same timer logic.
You can create and manage timers by using them in as many components useTimer
as you need to implement shared timer functionality throughout your application.