进阶知识点补充
这一篇是收尾的进阶补充,把之前零散或没深入的知识点集中讲透:原型与原型链、深浅拷贝、防抖/节流、Symbol、迭代器与生成器,以及严格模式、柯里化、类型判断、逻辑赋值运算符、Proxy 等。
一、原型与原型链(深入)
1. 核心关系(基础回顾)
1 2 3 4 5 6 7 8 9
| function Person(name) { this.name = name; } Person.prototype.say = function () { console.log(this.name); };
const p = new Person("Tom"); p.say();
|
三条关键等式(原型链的本质):
1 2 3
| p.__proto__ === Person.prototype; Person.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null;
|
2. 现代 API:getPrototypeOf / setPrototypeOf
1
| Object.getPrototypeOf(p) === Person.prototype;
|
3. instanceof 的原理
instanceof 就是沿对象的原型链向上查找,看是否出现过某个构造函数的 prototype:
1 2
| p instanceof Person; p instanceof Object;
|
4. new 操作符内部做了什么
1 2 3 4 5 6 7 8
| function myNew(constructor, ...args) { const obj = Object.create(constructor.prototype); const result = constructor.apply(obj, args); return result instanceof Object ? result : obj; }
|
记忆点:new = 建对象 + 绑原型 + 执行构造 + 返回对象。理解它,就理解了原型和 class 的关系。
二、深浅拷贝
1. 赋值 vs 浅拷贝 vs 深拷贝
1 2 3 4 5 6 7 8 9 10 11
| const obj = { a: { b: 1 } };
const ref = obj; ref.a.b = 2;
const shallow = { ...obj }; shallow.a.b = 3; shallow.a = {};
const deep = structuredClone(obj); deep.a.b = 4;
|
2. 浅拷贝的方法
1 2 3 4
| Object.assign({}, obj); { ...obj }; arr.slice(); [...arr];
|
3. 深拷贝的方法
① JSON 方式(有局限):
1
| const copy = JSON.parse(JSON.stringify(obj));
|
⚠️ 局限:会丢失函数、undefined、Symbol,无法处理循环引用、Date 转字符串。只适合纯数据对象。
② 手写递归深拷贝:
1 2 3 4 5 6 7 8 9 10
| function deepClone(obj) { if (obj === null || typeof obj !== "object") return obj; const copy = Array.isArray(obj) ? [] : {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepClone(obj[key]); } } return copy; }
|
③ 原生 structuredClone(现代推荐):
1
| const copy = structuredClone(obj);
|
三、防抖 debounce
概念:触发后 n 秒内没有再次触发,才执行(只执行最后一次)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function debounce(fn, delay) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }
input.addEventListener("input", debounce(function () { console.log("发起搜索"); }, 500));
|
场景:搜索框输入、窗口 resize、按钮防连点。
四、节流 throttle
概念:n 秒内最多执行一次(固定频率)。
1 2 3 4 5 6 7 8 9 10
| function throttle(fn, delay) { let lastTime = 0; return function (...args) { const now = Date.now(); if (now - lastTime >= delay) { fn.apply(this, args); lastTime = now; } }; }
|
场景:滚动监听、鼠标移动(高频触发但无需每次都响应)。
防抖 vs 节流
|
防抖 debounce |
节流 throttle |
| 触发时机 |
停止触发 n 秒后才执行 |
每 n 秒固定执行一次 |
| 比喻 |
电梯等人(等人齐了才关门) |
红绿灯(固定间隔放行) |
| 场景 |
搜索框、resize |
滚动、mousemove |
记忆:防抖是”最后一下”,节流是”固定频率”。两者都用闭包保存定时器/时间戳。
五、Symbol
1. 概念:独一无二的值
1 2 3
| const s1 = Symbol("desc"); const s2 = Symbol("desc"); s1 === s2;
|
2. 用途:对象唯一属性名
1 2 3 4 5 6
| const key = Symbol("id"); const obj = { [key]: 123, name: "Tom" }; obj[key];
|
⚠️ Symbol 属性不会被 Object.keys / for...in 遍历到,适合做”隐藏”属性、避免命名冲突。
3. 全局注册表:Symbol.for / keyFor
1 2 3 4
| const s1 = Symbol.for("foo"); const s2 = Symbol.for("foo"); s1 === s2; Symbol.keyFor(s1);
|
4. 内置 Symbol
Symbol.iterator:定义对象的迭代行为(见下一节)。
Symbol.toPrimitive:对象转原始值时调用。
六、迭代器与生成器
1. 迭代器协议
有 next() 方法、返回 { value, done } 的对象就是迭代器:
1 2 3 4 5 6 7
| const arr = [1, 2, 3]; const it = arr[Symbol.iterator]();
it.next(); it.next(); it.next(); it.next();
|
for...of 的底层原理就是:调用 Symbol.iterator 拿迭代器,循环 next() 直到 done 为 true。这也是为什么普通对象不能用 for...of(没有 Symbol.iterator)。
2. 生成器 function* / yield
生成器是能暂停和恢复的函数,每次 yield 暂停,下次 next() 继续:
1 2 3 4 5 6 7 8 9 10
| function* gen() { yield 1; yield 2; yield 3; } const g = gen(); g.next(); g.next(); g.next(); g.next();
|
3. 应用:惰性求值 / 无限序列
1 2 3 4 5 6 7 8 9
| function* idMaker() { let id = 1; while (true) { yield id++; } } const ids = idMaker(); ids.next().value; ids.next().value;
|
七、其他重要知识点
1. 严格模式 “use strict”
主要影响:
- 未声明就赋值的变量会报错(非严格会静默创建全局变量)。
- 函数独立调用时
this 为 undefined(非严格是 window)。
- 禁止重复参数名、
with 等。
2. 柯里化与函数组合
柯里化:把多参函数变成”一次只收一个参数”的链式函数:
1 2 3 4 5 6
| function add(a) { return function (b) { return a + b; }; } add(1)(2);
|
函数组合:把多个函数拼成一个(从右到左执行):
1 2 3 4 5
| const compose = (f, g) => x => f(g(x)); const double = x => x * 2; const plusOne = x => x + 1; const fn = compose(double, plusOne); fn(3);
|
3. 类型判断全解
| 方式 |
适用 |
示例 |
typeof |
基本类型(null 除外) |
typeof "a" → "string" |
instanceof |
判断原型链 |
[] instanceof Array → true |
Object.prototype.toString.call |
最准确 |
call([]) → "[object Array]" |
1 2 3
| Object.prototype.toString.call([]); Object.prototype.toString.call(null); Object.prototype.toString.call({});
|
4. 逻辑赋值运算符
1 2 3
| a ||= b; a &&= b; a ??= b;
|
5. Proxy / Reflect(简要)
Proxy 拦截对象操作,是 Vue3 响应式的底层:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const target = { name: "Tom" }; const obj = new Proxy(target, { get(target, key) { console.log("读取", key); return target[key]; }, set(target, key, value) { console.log("设置", key, value); target[key] = value; } }); obj.name; obj.name = "Jerry";
|
八、速查表
| 需求 |
写法 |
| 取对象原型 |
Object.getPrototypeOf(obj) |
| 判断原型链 |
obj instanceof Constructor |
| 浅拷贝 |
{ ...obj } / Object.assign({}, obj) |
| 深拷贝 |
structuredClone(obj) |
| 防抖 |
clearTimeout + setTimeout |
| 节流 |
时间戳比较 Date.now() |
| 创建 Symbol |
Symbol("desc") / Symbol.for("key") |
| 拿迭代器 |
arr[Symbol.iterator]() |
| 生成器 |
function* g() { yield 1; } |
| 严格模式 |
"use strict" |
| 柯里化 |
a => b => a + b |
| 最准确类型判断 |
Object.prototype.toString.call(x) |
| 逻辑赋值 |
a ??= b |
| 拦截对象操作 |
new Proxy(target, handler) |
九、复习要点
- 原型链:
实例.__proto__ → 构造函数.prototype → Object.prototype → null;instanceof 就是沿链查找;new 做了四件事。
- 浅拷贝只复制第一层(
.../Object.assign),内层仍共享;深拷贝用 structuredClone(或递归/JSON,注意 JSON 丢函数)。
- 防抖停止 n 秒后才执行(搜索框),节流每 n 秒执行一次(滚动),都用闭包实现。
- Symbol 是唯一值,做对象唯一属性名,且不会被
Object.keys 遍历。
- 迭代器有
next() 返回 { value, done };for...of 靠 Symbol.iterator;生成器 function* 能暂停恢复。
- 严格模式让 this 更安全、未声明变量报错。
- 柯里化是”一次一个参数”,组合是”从右到左拼函数”。
- 类型判断:基本类型用
typeof、原型链用 instanceof、最准用 toString.call。