【2026前端最新面试题——day10】JavaScript 高频面试题


每天 10 道,每题带参考答案和代码。Day 1 专攻 JS 核心:类型、作用域、原型、异步、手写题。

更多免费题目

第 1 题:JavaScript 有哪些数据类型?如何判断?

参考答案

原始类型(7 种): stringnumberbigintbooleanundefinedsymbolnull

引用类型: object(含 Array、Function、Date、RegExp 等)

判断类型常用三种方式:

javascript 复制代码
// 1. typeof ------ 适合原始类型 + function
typeof "hello";           // "string"
typeof 42;                // "number"
typeof undefined;         // "undefined"
typeof null;              // "object" ← 历史 bug
typeof [];                // "object"
typeof function(){};      // "function"

// 2. Array.isArray ------ 判断数组
Array.isArray([1, 2]);    // true

// 3. Object.prototype.toString ------ 最精确
Object.prototype.toString.call(null);       // "[object Null]"
Object.prototype.toString.call([]);         // "[object Array]"
Object.prototype.toString.call(new Date()); // "[object Date]"

面试加分点: typeof null === 'object' 是遗留问题;跨 iframe 判断类型优先用 toString.call


第 2 题:===== 有什么区别?

参考答案

  • ===严格相等 ,类型不同直接 false,不做类型转换
  • ==宽松相等,类型不同时按规则强制转换后再比
javascript 复制代码
0 == false;         // true   →  false 转 0
"" == false;        // true   →  都转成 0
null == undefined;  // true   →  特殊规则
null === undefined; // false

// 对象比较比引用
{} === {};          // false
const a = {};
const b = a;
a === b;            // true

工程建议: 团队规范一般禁用 ==,用 === + 显式转换。

javascript 复制代码
// 显式转数字
Number(input) === 0;

// 显式转字符串
String(id) === "123";

第 3 题:什么是闭包?有哪些应用场景?

参考答案

闭包 = 函数 + 其词法作用域中的自由变量。

内层函数引用了外层函数的变量,且外层已执行完毕,变量仍被保留。

javascript 复制代码
function createCounter() {
  let count = 0;
  return {
    inc() { return ++count; },
    get() { return count; },
  };
}

const counter = createCounter();
counter.inc(); // 1
counter.inc(); // 2
counter.get(); // 2
// count 无法从外部直接访问,但被闭包持有

常见场景:

场景 示例
数据私有化 模块模式、计数器
柯里化 参数分步传入
防抖/节流 保存 timer 引用
循环 + 异步 用 IIFE 或 let 固定 index
javascript 复制代码
// 经典坑:var + 循环
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 3 3 3
}

// 闭包修复
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 0); // 0 1 2
  })(i);
}

// 更简洁:let 块级作用域
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 0 1 2
}

第 4 题:解释 JavaScript 的原型链

参考答案

每个对象有内部属性 [[Prototype]](可通过 __proto__Object.getPrototypeOf 访问)。

访问属性时:自身没有 → 沿原型链向上找 → 直到 null

javascript 复制代码
function Person(name) {
  this.name = name;
}
Person.prototype.sayHi = function () {
  console.log(`Hi, ${this.name}`);
};

const p = new Person("Alice");
p.sayHi(); // "Hi, Alice"

// 原型链关系
p.__proto__ === Person.prototype;                  // true
Person.prototype.__proto__ === Object.prototype;   // true
Object.prototype.__proto__ === null;             // true

手写 new 四步:

javascript 复制代码
function myNew(Ctor, ...args) {
  const obj = Object.create(Ctor.prototype);
  const result = Ctor.apply(obj, args);
  return result instanceof Object ? result : obj;
}

ES6 class 是语法糖,本质仍是原型:

javascript 复制代码
class Animal {
  speak() { return "..."; }
}
typeof Animal; // "function"
Animal.prototype.speak; // 仍在 prototype 上

第 5 题:说说 this 的指向规则

参考答案

调用方式 this 指向
普通函数直接调用 非严格模式 window,严格模式 undefined
对象方法调用 调用对象
new 新实例
call/apply/bind 指定对象
箭头函数 定义时的外层 this(词法绑定)
javascript 复制代码
const obj = {
  name: "obj",
  fn() { return this.name; },
  arrow: () => this?.name,
};

obj.fn();        // "obj"
const f = obj.fn;
f();             // undefined(严格模式)或 window

obj.arrow();     // 外层 this,不是 obj

// 丢失 this 的修复
const bound = obj.fn.bind(obj);
bound();         // "obj"

DOM 事件中的经典坑:

javascript 复制代码
button.addEventListener("click", function () {
  console.log(this); // button 元素
});

button.addEventListener("click", () => {
  console.log(this); // 外层 this,不是 button
});

第 6 题:解释事件循环(Event Loop)

参考答案

JS 是单线程。异步靠 任务队列 调度:

  1. 执行同步代码(调用栈)
  2. 微任务全部清空(Promise.thenMutationObserverqueueMicrotask
  3. 取一个宏任务(setTimeoutsetInterval、I/O)
  4. 重复 2~3
javascript 复制代码
console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

// 输出顺序:1 → 4 → 3 → 2

进阶题:

javascript 复制代码
console.log("start");

setTimeout(() => {
  console.log("timeout");
  Promise.resolve().then(() => console.log("promise in timeout"));
}, 0);

Promise.resolve()
  .then(() => {
    console.log("promise1");
    return Promise.resolve();
  })
  .then(() => console.log("promise2"));

console.log("end");

// start → end → promise1 → promise2 → timeout → promise in timeout

记忆口诀: 同步 → 微任务清完 → 一个宏任务 → 再清微任务。


第 7 题:Promise 常用 API 及手写思路

参考答案

Promise 三种状态:pendingfulfilled / rejected,不可逆。

javascript 复制代码
// 基础用法
fetchData()
  .then((data) => process(data))
  .catch((err) => console.error(err))
  .finally(() => hideLoading());

// Promise.all ------ 全部成功才成功,一个失败即失败
Promise.all([p1, p2, p3]).then(([a, b, c]) => {});

// Promise.race ------ 谁先 settle 用谁
Promise.race([timeout(3000), fetch("/api")]);

// Promise.allSettled ------ 等全部结束,不因单个失败中断
Promise.allSettled([p1, p2]).then((results) => {
  results.forEach((r) => {
    if (r.status === "fulfilled") console.log(r.value);
    else console.log(r.reason);
  });
});

极简手写 Promise.then 核心:

javascript 复制代码
class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.callbacks = [];

    const resolve = (val) => {
      if (this.state !== "pending") return;
      this.state = "fulfilled";
      this.value = val;
      this.callbacks.forEach((cb) => cb.onFulfilled(val));
    };

    executor(resolve);
  }

  then(onFulfilled) {
    return new MyPromise((resolve) => {
      if (this.state === "fulfilled") {
        resolve(onFulfilled(this.value));
      } else {
        this.callbacks.push({
          onFulfilled: (v) => resolve(onFulfilled(v)),
        });
      }
    });
  }
}

第 8 题:浅拷贝和深拷贝的区别?如何实现?

参考答案

浅拷贝 深拷贝
一层对象 新对象 新对象
嵌套引用 共享 递归复制
javascript 复制代码
const original = { a: 1, nested: { b: 2 } };

// 浅拷贝
const shallow1 = { ...original };
const shallow2 = Object.assign({}, original);
shallow1.nested.b = 99;
original.nested.b; // 99 ← 嵌套仍共享

// 深拷贝 ------ 简单场景
const deep1 = structuredClone(original); // 现代浏览器 / Node 17+

// 深拷贝 ------ 手写(处理循环引用)
function deepClone(value, cache = new WeakMap()) {
  if (value === null || typeof value !== "object") return value;
  if (cache.has(value)) return cache.get(value);

  if (Array.isArray(value)) {
    const arr = [];
    cache.set(value, arr);
    value.forEach((item, i) => {
      arr[i] = deepClone(item, cache);
    });
    return arr;
  }

  const obj = {};
  cache.set(value, obj);
  for (const key of Object.keys(value)) {
    obj[key] = deepClone(value[key], cache);
  }
  return obj;
}

const deep2 = deepClone(original);
deep2.nested.b = 0;
original.nested.b; // 99 ← 不受影响

注意: JSON.parse(JSON.stringify(obj)) 无法拷贝 undefined、函数、Symbol、循环引用。


第 9 题:手写防抖(debounce)和节流(throttle)

参考答案

  • 防抖: 连续触发时,只执行最后一次(搜索框输入)
  • 节流: 固定时间窗口内最多执行一次(滚动、resize)
javascript 复制代码
// 防抖
function debounce(fn, delay = 300) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 节流(时间戳版)
function throttle(fn, interval = 300) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}

// 使用示例
const onSearch = debounce((keyword) => {
  console.log("搜索:", keyword);
}, 500);

const onScroll = throttle(() => {
  console.log("scroll", window.scrollY);
}, 200);

input.addEventListener("input", (e) => onSearch(e.target.value));
window.addEventListener("scroll", onScroll);

防抖「立即执行版」------ 第一次立刻触发,之后等待静默期:

javascript 复制代码
function debounceLeading(fn, delay) {
  let timer = null;
  return function (...args) {
    if (!timer) fn.apply(this, args);
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null;
    }, delay);
  };
}

第 10 题:数组去重有哪些方法?

参考答案

javascript 复制代码
const arr = [1, 2, 2, 3, "3", {}, {}];

// 1. Set(最常用)
const unique1 = [...new Set(arr)];
// [1, 2, 3, "3", {}, {}]  ← 对象引用不同,不会去重

// 2. filter + indexOf(仅 primitive)
const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);

// 3. reduce
const unique3 = arr.reduce((acc, cur) => {
  return acc.includes(cur) ? acc : [...acc, cur];
}, []);

// 4. 对象数组按 id 去重
const users = [
  { id: 1, name: "A" },
  { id: 2, name: "B" },
  { id: 1, name: "A2" },
];
const map = new Map();
users.forEach((u) => map.set(u.id, u));
const uniqueUsers = [...map.values()];
// [{ id:1, name:"A2" }, { id:2, name:"B" }]

// 5. 按 JSON 字符串去重(简单对象可用,有局限)
function uniqueByJson(arr) {
  const seen = new Set();
  return arr.filter((item) => {
    const key = JSON.stringify(item);
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

面试总结: 原始值用 Set;对象数组用 Map 按 key 去重;要知道 Set{} 是按引用判等。


Day 1 小结

题号 考点 必会程度
1 类型判断 ⭐⭐⭐
2 == vs === ⭐⭐⭐
3 闭包 ⭐⭐⭐
4 原型链 ⭐⭐⭐
5 this ⭐⭐⭐
6 事件循环 ⭐⭐⭐
7 Promise ⭐⭐⭐
8 深拷贝 ⭐⭐⭐
9 防抖节流 ⭐⭐⭐
10 数组去重 ⭐⭐

练习方式: 每题先遮住答案,出声答 1 分钟,再对照代码跑一遍。


系列预告 & 引流(文末)

📅 系列更新计划

  • Day 2:CSS 布局 10 题
  • Day 3:Vue 3 十题
  • Day 4:React 十题

🔗 面试帮站内还有更多 JS 专题题(含完整长答案):

https://mianshibang.cn/questions/frontend-javascript

刷完通用题,还可以用「简历 + JD」做 AI 押题:

https://mianshibang.cn/interview/start

新用户注册送 1 次免费 AI 简历押题 体验。

收藏本文,明天更 Day 2 👋


#JavaScript #前端面试 #每日十题 #闭包 #事件循环 #春招 #八股文 #面试帮


复制说明

  1. 打开本文件,从 「正文(以下整段复制到编辑器)」 下方 --- 之后复制到文末标签
  2. 粘贴到 CSDN Markdown 编辑器(代码块会保留格式)
  3. 若用富文本编辑器,建议切换为 Markdown 模式再粘贴

系列文件规划

文件 状态
引流-前端每日10题-Day1-JavaScript.md ✅ 本篇
引流-前端每日10题-Day2-CSS.md 待写
引流-前端每日10题-Day3-Vue3.md 待写
引流-前端每日10题-Day4-React.md 待写
相关推荐
零陵上将军_xdr1 小时前
后端转全栈学习-Day4-JavaScript 基础-2
开发语言·javascript·学习
小科先生2 小时前
初学者安装java
java·开发语言
zzz_23682 小时前
【Spring】面试突击系列(三):Spring Web MVC 深度解析
前端·spring·面试
colofullove2 小时前
小说上传中心与异步处理进度展示设计
前端
ID_180079054732 小时前
小红书笔记评论 API 接口深度解析(带全套 JSON 示例・技术实战版)
java·开发语言·windows
折戟不必沉沙2 小时前
C++四种类型转换是什么
开发语言·c++
天青色等烟雨..2 小时前
AI赋能R-Meta分析核心技术:从热点挖掘到高级模型、助力高效科研与论文发表
开发语言·人工智能·r语言
Marst Code2 小时前
⚙️ 2026 年推荐技术方案
前端
AI玫瑰助手2 小时前
Python函数:递归函数的定义与阶乘案例实现
开发语言·python·信息可视化