js基础题

54 阅读2分钟

基础不牢、面试白考,快给我练起来!

u=3008892747,838558618&fm=253&fmt=auto&app=120&f=JPEG.webp


#️⃣ 1. 闭包 —— 别背概念,用例子秒懂

闭包出现的关键:
👉 函数里返回一个函数,并引用了外部的变量

最经典示例:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

输出:

3
3
3

原因:
var 只有函数作用域,没有块级作用域 → 循环里所有回调共享同一个 i → 最后是 3。

解决:

✔ 用 let
✔ 用立即执行函数(IIFE)让每次循环都有自己的作用域

for (var i = 0; i < 3; i++) {
  (function(i){
    setTimeout(() => console.log(i), 100);
  })(i)
}

闭包的本质:
函数记住了它诞生时所在的作用域(变量环境)。


#️⃣ 2. 防抖 debounce —— 只执行最后一次

适用于:搜索框输入、窗口 resize、按钮点太快…

function debounce(fn, delay) {
  let timer;

  return function (...args) {
    const context = this;

    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
}

🤩 效果:你点 10 次按钮,它只执行一次。


#️⃣ 3. 节流 throttle —— 一段时间内只执行一次

适用于:滚动、拖拽、抢红包狂点。

function throttle(fn, delay) {
  let last = 0;

  return function (...args) {
    const now = Date.now();
    if (now - last >= delay) {
      last = now;
      fn.apply(this, args);
    }
  };
}

#️⃣ 4. call / apply / bind —— 三兄弟的区别

方法是否立刻执行参数写法
call✔ 是多个参数
apply✔ 是数组
bind❌ 否(返回新函数)多个参数

真正用途:
✔ 改变 this 指向
✔ 借用构造函数实现“继承”

例如:

function fn(a, b) {
  console.log(this, a + b);
}

fn.call({x:1}, 1, 2);
fn.apply({x:1}, [1, 2]);
fn.bind({x:1}, 1)(2);

#️⃣ 5. 寄生组合继承 —— ES5 最优继承方案

function Parent(name) {
  this.name = name;
  this.hobby = ["game", "music"];
}

Parent.prototype.sayHi = function () {
  console.log("Hi, I am " + this.name);
};

function Child(name, age) {
  Parent.call(this, name); // 继承实例属性
  this.age = age;
}

// 关键代码
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

优点:
✔ 实例属性靠 call 拿到
✔ 原型方法靠 Object.create 继承
✔ 避免多次调用父构造函数(经典组合继承最大问题)

这就是传说中的 “JavaScript 继承最优解”。


#️⃣ 6. ES6 class 继承 —— 语法糖版

class Parent {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    console.log("Hi from Parent");
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
}

语法更现代,但本质仍是原型链。


#️⃣ 7. 柯里化 —— 一个函数拆成多次调用

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    }
    return function (...next) {
      return curried(...args, ...next);
    };
  };
}

使用:

const add = (a,b,c) => a + b + c;

const curried = curry(add);

curried(1)(2)(3); // 6

意义:
✔ 参数复用
✔ 让函数更灵活
✔ 常用于函数式编程 / React hook 工具


#️⃣ 8. Promise —— 异步编程的轻量写法

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    const ok = true;
    ok ? resolve("成功") : reject("失败");
  }, 1000);
});

p.then(res => console.log(res))
 .catch(err => console.log(err))
 .finally(() => console.log("总会执行"));

Promise 的状态:
✔ pending
✔ fulfilled
✔ rejected
状态一旦改变就不会再变(不可逆)。


#️⃣ 9. 手写发布订阅 —— EventBus

实现事件中心:Vue2、Node.js 都有类似机制。

const EventBus = {
  events: {},

  on(event, cb) {
    this.events[event] = this.events[event] || [];
    this.events[event].push(cb);
  },

  emit(event, data) {
    (this.events[event] || []).forEach(cb => cb(data));
  },

  off(event, cb) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(fn => fn !== cb);
  }
};

使用:

function handler(msg) {
  console.log("收到:", msg);
}
EventBus.on("msg", handler);
EventBus.emit("msg", "Hello!");