Javascript面试题及详细答案150道之(076-090)

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

76. 什么是JavaScript中的“生成器函数”(Generator Function)?如何使用?

生成器函数
ES6引入的特殊函数,使用function*定义,通过yield关键字暂停和恢复执行,返回一个迭代器对象。

基本语法

function* generator() {
  yield 1;
  yield 2;
  return 3;
}

const iterator = generator();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: true }

核心特性

  1. 暂停执行:每次调用next()执行到下一个yield,并返回其值。

  2. 传值给yield:通过iterator.next(value)向生成器内部传值。

    function* generator() {
      const result = yield "请输入值";
      console.log("接收到:", result);
    }
    
    const it = generator();
    console.log(it.next().value); // "请输入值"
    it.next(100); // "接收到: 100"
    
  3. 生成无限序列

    function* infiniteSequence() {
      let i = 0;
      while (true) {
        yield i++;
      }
    }
    
    const seq = infiniteSequence();
    console.log(seq.next().value); // 0
    console.log(seq.next().value); // 1
    

77. JavaScript中Object.keys()Object.values()Object.entries()的区别是什么?

核心区别

方法返回值类型返回内容
Object.keys(obj)字符串数组对象自身的可枚举属性名(不含Symbol)
Object.values(obj)值数组对象自身的可枚举属性值(不含Symbol)
Object.entries(obj)[键, 值] 元组数组对象自身的可枚举属性键值对(不含Symbol)

代码示例

const obj = { a: 1, b: 2, [Symbol('c')]: 3 };

console.log(Object.keys(obj)); // ["a", "b"]
console.log(Object.values(obj)); // [1, 2]
console.log(Object.entries(obj)); // [["a", 1], ["b", 2]]

应用场景

  • 遍历对象

    for (const [key, value] of Object.entries(obj)) {
      console.log(`${key}: ${value}`);
    }
    
  • 转换为Map

    const map = new Map(Object.entries(obj));
    

78. 如何在JavaScript中实现一个简单的事件总线(Event Bus)?

事件总线
实现组件间通信的机制,通过发布-订阅模式解耦事件发送者和接收者。

代码示例

class EventBus {
  constructor() {
    this.events = {}; // 存储事件及其回调函数
  }

  // 注册事件监听
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  // 触发事件
  emit(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback(data));
    }
  }

  // 移除事件监听
  off(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(
        cb => cb!== callback
      );
    }
  }
}

// 使用示例
const eventBus = new EventBus();

// 订阅事件
const callback = (data) => console.log('接收到:', data);
eventBus.on('message', callback);

// 发布事件
eventBus.emit('message', 'Hello, Event Bus!'); // 输出:接收到: Hello, Event Bus!

// 取消订阅
eventBus.off('message', callback);
eventBus.emit('message', '再次发送'); // 无输出

79. JavaScript中Number.isNaN()和全局isNaN()的区别是什么?

核心区别

方法判断逻辑示例
Number.isNaN(value)仅当值为NaN时返回trueNumber.isNaN(NaN)true
isNaN(value)先将值转为数字,再判断是否为NaNisNaN("abc")true("abc"转数字为NaN)

代码示例

// 实际值为NaN的情况
console.log(Number.isNaN(NaN)); // true
console.log(isNaN(NaN)); // true

// 非NaN但转换后为NaN的情况
console.log(Number.isNaN("abc")); // false("abc"本身不是NaN)
console.log(isNaN("abc")); // true("abc"转数字为NaN)

// 其他情况
console.log(Number.isNaN(undefined)); // false
console.log(isNaN(undefined)); // true(undefined转数字为NaN)

推荐使用
优先使用Number.isNaN(),避免隐式类型转换带来的误判。

80. 如何在JavaScript中实现一个简单的缓存(Cache)?

缓存实现
基于Map或对象,存储已计算的结果,避免重复计算。

方法1:基于对象的简单缓存

function cachedFunction(fn) {
  const cache = {};
  return function(arg) {
    const key = JSON.stringify(arg);
    if (cache[key] === undefined) {
      cache[key] = fn(arg);
    }
    return cache[key];
  };
}

// 使用示例
const expensiveCalculation = (n) => {
  console.log(`计算 ${n} 的结果...`);
  return n * n;
};

const cachedCalc = cachedFunction(expensiveCalculation);
console.log(cachedCalc(5)); // 计算 5 的结果... → 25
console.log(cachedCalc(5)); // 直接返回缓存的 25

方法2:带过期时间的缓存

class Cache {
  constructor(expiryTime = 60000) {
    this.cache = new Map();
    this.expiryTime = expiryTime;
  }

  get(key) {
    const entry = this.cache.get(key);
    if (entry && Date.now() - entry.timestamp < this.expiryTime) {
      return entry.value;
    }
    this.cache.delete(key);
    return undefined;
  }

  set(key, value) {
    this.cache.set(key, { value, timestamp: Date.now() });
  }
}

// 使用示例
const cache = new Cache(1000); // 1秒过期
cache.set('data', [1, 2, 3]);
console.log(cache.get('data')); // [1, 2, 3]
setTimeout(() => console.log(cache.get('data')), 1500); // undefined

81. JavaScript中String.prototype.replace()的第二个参数可以是哪些类型?

replace()的第二个参数类型

  1. 字符串:直接替换匹配部分,支持特殊占位符:

    • $&:匹配的子字符串。
    • `$``:匹配部分之前的文本。
    • $':匹配部分之后的文本。
    • $n:第n个捕获组(如$1$2)。
    "Hello World".replace("World", "JavaScript"); // "Hello JavaScript"
    "abc".replace(/(a)(b)/, "$2$1"); // "bac"(交换捕获组)
    
  2. 函数:对每个匹配项动态生成替换值,函数参数为:

    • 匹配的子字符串。
    • 捕获组(若有)。
    • 匹配位置。
    • 原字符串。
    "a1b2c3".replace(/\d/g, (match) => parseInt(match) * 2); // "a2b4c6"
    

82. 什么是JavaScript中的“装饰器”(Decorator)?如何使用?

装饰器
ES7引入的元编程特性,用于修改类、方法或属性的行为,语法为@decorator

基本语法

// 类装饰器
function logClass(target) {
  console.log(`Class ${target.name} was decorated`);
  return target; // 必须返回原类或修改后的类
}

@logClass
class MyClass {}

// 方法装饰器
function logMethod(target, name, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`Calling ${name} with args:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${name} returned:`, result);
    return result;
  };
  return descriptor;
}

class Calculator {
  @logMethod
  add(a, b) {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(1, 2); // 输出:Calling add with args: [1, 2] → Method add returned: 3

注意
装饰器目前是ES提案(Stage 2),需通过Babel等工具编译后使用。

No.大剑师精品GIS教程推荐
0地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】
1Openlayers 【入门教程】 - 【源代码+示例 300+】
2Leaflet 【入门教程】 - 【源代码+图文示例 150+】
3MapboxGL【入门教程】 - 【源代码+图文示例150+】
4Cesium 【入门教程】 - 【源代码+综合教程 200+】
5threejs【中文API】 - 【源代码+图文示例200+】
6Shader 编程 【图文示例 100+】

83. JavaScript中Object.getOwnPropertyDescriptor()的作用是什么?

作用
返回对象自身属性的描述符(包括值、可写性、可枚举性、可配置性等)。

属性描述符结构

const obj = { a: 1 };
const descriptor = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor);
/* 输出:
{
  value: 1,
  writable: true,
  enumerable: true,
  configurable: true
}
*/

应用场景

  1. 修改属性配置

    Object.defineProperty(obj, 'a', {
      writable: false,
      enumerable: false,
      configurable: false
    });
    
  2. 获取不可枚举属性

    const arr = [1, 2, 3];
    Object.defineProperty(arr, 'length', { enumerable: false });
    console.log(Object.keys(arr)); // [0, 1, 2](length不可枚举)
    console.log(Object.getOwnPropertyDescriptor(arr, 'length')); // 可获取描述符
    

84. 如何在JavaScript中实现数组的随机打乱(Fisher-Yates洗牌算法)?

Fisher-Yates算法
从数组末尾开始,依次随机选择前面的元素交换位置,时间复杂度O(n)。

代码示例

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1)); // 随机选择0到i之间的索引
    [array[i], array[j]] = [array[j], array[i]]; // 交换元素
  }
  return array;
}

const arr = [1, 2, 3, 4, 5];
console.log(shuffleArray(arr)); // 例如:[3, 5, 1, 4, 2]

注意
直接修改原数组。若需返回新数组,先复制原数组:

const shuffled = shuffleArray([...arr]);

85. JavaScript中String.prototype.match()RegExp.prototype.exec()的区别是什么?

核心区别

方法返回值全局匹配(g标志)正则状态(lastIndex
str.match(regex)非全局:匹配数组(含捕获组)
全局:仅匹配结果数组(不含捕获组)
影响返回格式不更新
regex.exec(str)每次调用返回一个匹配(含捕获组)多次调用遍历所有匹配更新lastIndex

代码示例

const str = "a1b2c3";
const regex = /\d/g;

// match()
console.log(str.match(regex)); // ["1", "2", "3"](全局模式返回所有匹配)
console.log(str.match(/\d/)); // ["1", index: 1,


### 85. JavaScript中`String.prototype.match()``RegExp.prototype.exec()`的区别是什么?(续)

**代码示例**```javascript
const str = "a1b2c3";
const regex = /\d/g;

// match()
console.log(str.match(regex)); // ["1", "2", "3"](全局模式返回所有匹配)
console.log(str.match(/\d/)); // ["1", index: 1, input: "a1b2c3", groups: undefined](非全局返回首个匹配及捕获组)

// exec()
console.log(regex.exec(str)); // ["1", index: 1, input: "a1b2c3", groups: undefined]
console.log(regex.lastIndex); // 2(lastIndex更新为下一次匹配的起始位置)
console.log(regex.exec(str)); // ["2"]
console.log(regex.lastIndex); // 4
console.log(regex.exec(str)); // ["3"]
console.log(regex.exec(str)); // null(匹配结束)

应用场景

  • 获取所有匹配:用match()配合g标志。
  • 获取捕获组并遍历:用exec()循环调用(需注意重置lastIndex)。

86. 如何在JavaScript中实现深度克隆(Deep Clone)?

深拷贝与浅拷贝的区别

  • 浅拷贝:只复制对象的一层属性,嵌套对象共享引用。
  • 深拷贝:递归复制所有层级的属性,完全独立。

实现方法

  1. 简易版(使用JSON)

    function deepClone(obj) {
      return JSON.parse(JSON.stringify(obj));
    }
    
    const obj = { a: { b: 2 } };
    const clone = deepClone(obj);
    obj.a.b = 3;
    console.log(clone.a.b); // 2(不受原对象影响)
    

    局限性:无法处理函数、正则、循环引用等。

  2. 完整实现(递归+Map处理循环引用)

    function deepClone(obj, map = new Map()) {
      if (obj === null || typeof obj!== 'object') return obj;
      if (map.has(obj)) return map.get(obj); // 处理循环引用
    
      const clone = Array.isArray(obj)? [] : {};
      map.set(obj, clone);
    
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          clone[key] = deepClone(obj[key], map);
        }
      }
    
      return clone;
    }
    
    // 测试循环引用
    const obj = { a: 1 };
    obj.b = obj; // 循环引用
    const clone = deepClone(obj);
    console.log(clone.b === clone); // true
    

87. JavaScript中async/awaitPromise.then()的区别是什么?

核心区别

特性async/awaitPromise.then()
语法风格同步风格(代码线性执行)链式调用(嵌套可能导致回调地狱)
错误处理使用try...catch使用.catch()
调试断点调试更直观调试多层嵌套Promise较复杂
并行执行默认串行,需配合Promise.all可自然并行(多个then链)
返回值返回Promise返回Promise

代码示例

// Promise.then()
function fetchData() {
  return fetch('https://api.example.com/data')
   .then(response => response.json())
   .then(data => processData(data))
   .catch(error => console.error(error));
}

// async/await
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return processData(data);
  } catch (error) {
    console.error(error);
  }
}

并行执行对比

// 串行(async/await)
async function serial() {
  const result1 = await task1();
  const result2 = await task2(); // 等待task1完成
  return [result1, result2];
}

// 并行(async/await + Promise.all)
async function parallel() {
  const [result1, result2] = await Promise.all([task1(), task2()]);
  return [result1, result2];
}

88. 什么是JavaScript中的“事件冒泡”(Event Bubbling)和“事件捕获”(Event Capturing)?

事件流阶段

  1. 捕获阶段:事件从window开始,逐级向下传播到目标元素。
  2. 目标阶段:事件到达目标元素。
  3. 冒泡阶段:事件从目标元素开始,逐级向上传播到window。

事件监听参数
addEventListener(type, listener, options)中的options可设为:

  • capture: true:使用捕获阶段(默认false,使用冒泡阶段)。
  • once: true:只执行一次。
  • passive: true:提高滚动性能(不阻止默认行为)。

代码示例

<div id="parent">
  <button id="child">点击我</button>
</div>

<script>
  // 冒泡阶段(默认)
  document.getElementById('parent').addEventListener('click', () => {
    console.log('Parent - 冒泡');
  });

  // 捕获阶段
  document.getElementById('child').addEventListener('click', () => {
    console.log('Child - 捕获');
  }, true);

  // 点击按钮输出顺序:
  // Child - 捕获(捕获阶段先触发)
  // Parent - 冒泡(冒泡阶段后触发)
</script>

阻止传播

  • event.stopPropagation():阻止事件继续冒泡或捕获。
  • event.stopImmediatePropagation():阻止当前元素上的其他同类型事件处理函数。

89. JavaScript中Array.prototype.reduce()的常见用法有哪些?

reduce()语法

array.reduce((accumulator, currentValue, currentIndex, array) => {
  // 返回累加值
}, initialValue);

常见用法

  1. 求和/乘积

    const sum = [1, 2, 3].reduce((acc, num) => acc + num, 0); // 6
    const product = [1, 2, 3].reduce((acc, num) => acc * num, 1); // 6
    
  2. 数组扁平化

    const nested = [[1, 2], [3, 4]];
    const flat = nested.reduce((acc, arr) => acc.concat(arr), []); // [1, 2, 3, 4]
    
  3. 统计元素出现次数

    const count = ['a', 'b', 'a', 'c', 'b'].reduce((acc, char) => {
      acc[char] = (acc[char] || 0) + 1;
      return acc;
    }, {}); // { a: 2, b: 2, c: 1 }
    
  4. 分组对象

    const people = [
      { name: 'Alice', age: 25 },
      { name: 'Bob', age: 25 },
      { name: 'Charlie', age: 30 }
    ];
    
    const grouped = people.reduce((acc, person) => {
      const key = person.age;
      if (!acc[key]) acc[key] = [];
      acc[key].push(person);
      return acc;
    }, {});
    /* 结果:
    {
      25: [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 25 }],
      30: [{ name: 'Charlie', age: 30 }]
    }
    */
    

90. 如何在JavaScript中实现一个简单的观察者模式(Observer Pattern)?

观察者模式
定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新。

代码示例

class EventEmitter {
  constructor() {
    this.events = {}; // 存储事件及其观察者回调
  }

  // 注册观察者
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  // 发布事件
  emit(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback(data));
    }
  }

  // 移除观察者
  off(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(
        cb => cb!== callback
      );
    }
  }
}

// 使用示例
const emitter = new EventEmitter();

// 注册观察者
const callback = (data) => console.log('接收到:', data);
emitter.on('message', callback);

// 发布事件
emitter.emit('message', 'Hello, Observer!'); // 输出:接收到: Hello, Observer!

// 移除观察者
emitter.off('message', callback);
emitter.emit('message', '再次发送'); // 无输出

应用场景

  • 事件处理系统(如DOM事件)。
  • 状态管理库(如Redux的subscribe机制)。
  • 组件间通信。

二、150道面试题目录列表

文章序号Javascript面试题150道
1Javascript面试题及答案150道(001-015)
2Javascript面试题及答案150道(016-030)
3Javascript面试题及答案150道(031-045)
4Javascript面试题及答案150道(046-060)
5Javascript面试题及答案150道(061-075)
6Javascript面试题及答案150道(076-090)
7Javascript面试题及答案150道(091-105)
8Javascript面试题及答案150道(106-120)
9Javascript面试题及答案150道(121-135)
10Javascript面试题及答案150道(136-150)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是大剑师兰特

打赏一杯可口可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值