关于前端:2023年前端面试真题之编码篇

9次阅读

共计 9587 个字符,预计需要花费 24 分钟才能阅读完成。

人的毕生,总是不免有浮沉。不会永远如旭日东升,也不会永远苦楚潦倒。重复地一浮一沉,对于一个人来说,正是磨难。因而,浮在下面的,不用自豪;沉在底下的,更用不着乐观。必须以率直、虚心的态度,乐观进取、向前迈进。——松下幸之助

大家好,我是江辰,在现在的互联网大环境下,想必大家都或多或少且有感触,塌实的社会之下,只有一直的放弃心性,能力感知不同的播种,互勉。

2023 年最新的面试题集锦,时刻做好筹备。

本文首发于微信公众号:家养程序猿江辰

欢送大家点赞,珍藏,关注

文章列表

  • 2023 年前端面试真题之 JS 篇
  • 2023 年前端面试真题之 CSS 篇
  • 2023 年前端面试真题之 HTML 篇
  • 2023 年前端面试真题之 React 篇
  • 2023 年前端面试真题之 Vue 篇

实现简易版 Promise

以下是一个根本的 Promise 实现:

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);
  }
);

这是一个十分根本的 Promise 实现,仅用于演示目标。在理论利用中,要思考更多的细节和错误处理。古代 JavaScript 曾经内置了 Promise,通常不须要手动实现它。

实现函数节流

函数节流是一种管制函数执行频率的技术,确保函数在肯定工夫距离内最多执行一次。以下是一个简略的 JavaScript 函数节流的实现:

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);
    }
  };
}

上述 throttle 函数承受两个参数:func 是要节流的函数,delay 是执行的工夫距离(以毫秒为单位)。

应用这个节流函数,您能够包装须要进行节流的函数,以确保它们不会在短时间内被频繁执行。例如:

// 原始函数,可能会频繁触发
function handleResize() {console.log('窗口大小扭转了');
}

// 应用节流包装后的函数
const throttledResize = throttle(handleResize, 200); // 200 毫秒的节流距离

// 监听窗口大小扭转事件,应用节流函数
window.addEventListener('resize', throttledResize);

当初,handleResize 函数将在 200 毫秒内最多执行一次,无论窗口大小扭转多频繁。这有助于缩小频繁的函数调用,进步性能。

实现函数防抖

函数防抖是一种管制函数执行频率的技术,确保函数在肯定工夫距离内只执行一次。以下是一个简略的 JavaScript 函数防抖的实现:

function debounce(func, delay) {
  let timerId;

  return function (...args) {clearTimeout(timerId);
    timerId = setTimeout(() => {func.apply(this, args);
    }, delay);
  };
}

上述 debounce 函数承受两个参数:func 是要防抖的函数,delay 是期待的工夫距离(以毫秒为单位)。

应用这个防抖函数,您能够包装须要进行防抖的函数,以确保它们只在肯定工夫距离后被执行。例如:

// 原始函数,可能会频繁触发
function handleInput(value) {console.log('输出值为:', value);
}

// 应用防抖包装后的函数
const debouncedInput = debounce(handleInput, 300); // 300 毫秒的防抖距离

// 监听输出事件,应用防抖函数
document.querySelector('input').addEventListener('input', (event) => {debouncedInput(event.target.value);
});

当初,handleInput 函数将在用户进行输出 300 毫秒后执行,从而缩小了频繁的函数调用,进步了性能。

实现观察者模式

观察者模式是一种设计模式,其中一个主题(被观察者)保护了一个观察者列表,并在状态变动时告诉观察者。以下是一个简略的 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 会收到更新

上述代码创立了一个简略的观察者模式实现,包含一个主题类 Subject 和一个观察者类 Observer。主题能够增加、移除观察者,并在状态变动时告诉所有观察者。

在示例中,咱们创立了一个主题 subject,并增加了两个观察者 observer1observer2。当主题状态发生变化时,它会告诉所有观察者。

这只是一个根本的示例,理论利用中,您可能须要更简单的实现以满足特定需要。

实现公布订阅模式

订阅者模式也被称为公布 - 订阅模式,它是一种设计模式,其中一个主题(发布者)保护了一个订阅者列表,并在事件产生时告诉所有订阅者。以下是一个简略的 JavaScript 订阅者模式的实现:

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 会收到告诉

在上述代码中,咱们创立了一个简略的订阅者模式实现,包含一个发布者类 Publisher 和一个订阅者类 Subscriber。发布者能够增加、移除订阅者,并在事件产生时告诉所有订阅者。

示例中,咱们创立了一个发布者 publisher,并增加了两个订阅者 subscriber1subscriber2。当发布者公布事件时,它会告诉所有订阅者。

这只是一个根本的示例,理论利用中,您能够依据须要扩大订阅者模式以满足特定需要。

实现 new 关键字

要实现 JavaScript 中 new 操作符的基本功能,您能够编写一个函数,该函数承受构造函数和结构函数参数,并返回一个新的对象实例。以下是一个示例的实现:

function myNew(constructor, ...args) {
  // 创立一个新对象,并将其原型指向构造函数的原型
  const obj = Object.create(constructor.prototype);

  // 调用构造函数,将新对象绑定到构造函数的上下文中
  const result = constructor.apply(obj, args);

  // 如果构造函数返回的是一个对象,则返回该对象;否则返回新创建的对象
  return typeof result === 'object' ? result : obj;
}

而后,您能够应用 myNew 函数来模仿 new 操作符的行为。例如:

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}

这个 myNew 函数首先创立一个新对象 obj,而后将新对象的原型指向构造函数 constructor 的原型。接下来,它调用构造函数,并将新对象绑定到构造函数的上下文中。最初,它查看构造函数的返回值,如果是对象则返回该对象,否则返回新创建的对象。

这是一个简略的 new 操作符的模仿实现,实际上,new 还波及到原型链等更简单的个性,但这个示例能够演示根本的原理。

实现 DeepClone

深拷贝(deep clone)是一种在复制对象时,不仅复制对象自身,还递归复制对象外部所有嵌套的对象和属性的操作。以下是一个简略的 JavaScript 深拷贝的实现示例:

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;
}

这个 deepClone 函数能够深度复制包含对象、数组和嵌套构造在内的简单数据类型。它应用了一个哈希表 hash 来避免循环援用,确保不会陷入有限递归。

示例用法:

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,阐明是不同的对象

请留神,这只是一个简略的深拷贝实现示例,理论利用中可能须要更简单的解决,以应答各种数据类型和状况。

实现函数 Curry

函数柯里化(Currying)是一种将承受多个参数的函数转换为一系列承受单个参数的函数的技术。以下是一个简略的 JavaScript 函数柯里化的实现示例:

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));
      };
    }
  };
}

这个 curry 函数承受一个函数 fn,而后返回一个柯里化后的函数。当柯里化后的函数被调用时,它将查看传入的参数数量是否足够执行原始函数 fn。如果参数足够,它会间接调用 fn;如果参数不够,它将返回一个新的函数,期待更多参数传入,并继续追加参数,直到参数足够。

示例用法:

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

在示例中,咱们首先应用 curry 函数将 add 函数柯里化,而后能够通过多种形式调用 curriedAdd 来实现加法操作。

这只是一个简略的函数柯里化的实现示例,理论利用中,您可能须要更简单的解决,以应答不同的函数和参数状况。

实现 Call

call 是 JavaScript 中用于调用函数的办法,它容许您指定函数外部的 this 值并传递参数。以下是一个简略的 JavaScript call 办法的模仿实现:

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;
};

这个模仿的 myCall 办法能够增加到 Function.prototype 上,以使所有函数都可能调用它。它承受一个上下文对象 context 和一系列参数 args

示例用法:

function greet(greeting) {console.log(`${greeting}, ${this.name}`);
}

const person = {name: 'Alice'};

// 应用 myCall 来调用 greet 函数,并指定上下文对象为 person
greet.myCall(person, 'Hello'); // 输入: Hello, Alice

在示例中,咱们通过 myCall 办法来调用 greet 函数,并指定 person 对象作为上下文对象,这使得 this 在函数外部指向了 person 对象。

请留神,这只是一个简略的 call 办法模仿实现,理论的 call 办法还能够解决更多参数和非凡状况。

实现数组拍平

在 JavaScript 中,您能够应用递归或循环来实现数组的拍平(Flatten)。以下是一些拍平数组的办法:

递归办法:

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]

应用 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]

应用 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]

上述办法都能够将嵌套的数组拍平成一个一维数组。抉择哪种办法取决于您的我的项目需要和对兼容性的要求。如果您的环境反对 ES6 的 Array.flat 办法,那是最简略的形式。如果须要兼容旧的环境,能够应用递归或 reduce 办法。

封装 Hooks 定时器

要封装一个能够在多个组件中共享的自定义 Hooks 定时器,您能够创立一个名为 useTimer 的自定义 Hooks。以下是一个示例:

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;

这个 useTimer 自定义 Hooks 承受两个参数:initialCount(初始计数值,默认为 0)和 interval(定时器距离,默认为 1000 毫秒)。它返回一个示意定时器计数值的count 状态变量。

您能够在多个组件中应用 useTimer 来创立定时器。以下是一个示例:

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;

在上述示例中,咱们导入了自定义 HooksuseTimer,而后在 TimerComponent 组件中应用它创立了一个定时器。每个应用 useTimer 的组件都会独立领有本人的定时器,但它们能够共享雷同的定时器逻辑。

您能够在须要的多个组件中应用 useTimer 来创立和治理定时器,以便在整个应用程序中实现共享的定时器性能。

正文完
 0