JavaScript 面向对象编程:从入门到精通完全指南
目录
- 面向对象基础概念
- 对象的创建方式
- [this 关键字](#this 关键字 "#3-this-%E5%85%B3%E9%94%AE%E5%AD%97")
- 原型与原型链
- [构造函数与 new](#构造函数与 new "#5-%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E-new")
- [ES6 Class 语法](#ES6 Class 语法 "#6-es6-class-%E8%AF%AD%E6%B3%95")
- 继承
- 封装与私有成员
- 多态与抽象
- [Mixin 与组合](#Mixin 与组合 "#10-mixin-%E4%B8%8E%E7%BB%84%E5%90%88")
- 属性描述符与元编程
- [Symbol 与内置协议](#Symbol 与内置协议 "#12-symbol-%E4%B8%8E%E5%86%85%E7%BD%AE%E5%8D%8F%E8%AE%AE")
- [Proxy 与 Reflect](#Proxy 与 Reflect "#13-proxy-%E4%B8%8E-reflect")
- 设计模式实战
- [SOLID 原则在 JS 中的应用](#SOLID 原则在 JS 中的应用 "#15-solid-%E5%8E%9F%E5%88%99%E5%9C%A8-js-%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8")
- 性能与最佳实践
- 常见陷阱与面试题
1. 面向对象基础概念
面向对象编程(OOP)的四大支柱:
| 概念 | 含义 | JS 实现手段 |
|---|---|---|
| 封装 | 隐藏内部细节,暴露接口 | 闭包、#私有字段、Symbol |
| 继承 | 复用父类的属性和方法 | 原型链、extends |
| 多态 | 同一接口,不同实现 | 方法重写、鸭子类型 |
| 抽象 | 提取共性,忽略细节 | 抽象基类模拟、接口约定 |
JS 的特殊之处 :JavaScript 是基于**原型(prototype)**的语言,而非基于类。ES6 的 class 只是原型机制的语法糖。理解这一点是精通 JS OOP 的关键。
2. 对象的创建方式
2.1 对象字面量
js
const user = {
name: 'Alice',
age: 25,
greet() {
return `Hi, I'm ${this.name}`;
},
// 计算属性名
['dyn' + 'Key']: 42,
// getter / setter
get info() { return `${this.name} (${this.age})`; },
set info(v) { [this.name, this.age] = v.split(','); }
};
2.2 Object.create
js
const animal = {
speak() { return `${this.name} makes a sound`; }
};
const dog = Object.create(animal, {
name: { value: 'Rex', writable: true, enumerable: true }
});
dog.speak(); // "Rex makes a sound"
// 创建无原型的纯净对象(适合做字典)
const dict = Object.create(null);
2.3 工厂函数
js
function createUser(name, age) {
let secret = 'hidden'; // 闭包实现私有
return {
name,
age,
getSecret: () => secret
};
}
优点 :无需 new,天然私有;缺点:方法不共享,内存占用高。
2.4 构造函数
js
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.greet = function () {
return `Hi, I'm ${this.name}`;
};
const u = new User('Bob', 30);
3. this 关键字
this 的值在调用时确定,遵循以下优先级(高 → 低):
javascript
new 绑定 > 显式绑定 (call/apply/bind) > 隐式绑定 (obj.fn()) > 默认绑定 (window/undefined)
js
function show() { return this?.name; }
const obj = { name: 'obj', show };
show(); // undefined(严格模式)/ window.name
obj.show(); // 'obj' 隐式绑定
show.call({ name: 'x' }); // 'x' 显式绑定
new show(); // this 指向新对象
3.1 常见丢失场景
js
const fn = obj.show;
fn(); // this 丢失
setTimeout(obj.show, 0); // this 丢失
// 解决方案
setTimeout(() => obj.show(), 0);
setTimeout(obj.show.bind(obj), 0);
3.2 箭头函数
箭头函数没有自己的 this ,它捕获定义时所在词法作用域的 this:
js
class Timer {
seconds = 0;
start() {
setInterval(() => {
this.seconds++; // 正确指向 Timer实例
}, 1000);
}
}
3.3 手写 call / apply / bind
js
Function.prototype.myCall = function (ctx, ...args) {
ctx = ctx ?? globalThis;
const key = Symbol('fn');
ctx[key] = this;
const result = ctx[key](...args);
delete ctx[key];
return result;
};
Function.prototype.myBind = function (ctx, ...args) {
const self = this;
return function bound(...rest) {
// 支持 new 调用
return self.apply(new.target ? this : ctx, [...args, ...rest]);
};
};
4. 原型与原型链
4.1 三个核心概念
prototype:函数才有,指向原型对象__proto__(即[[Prototype]]):所有对象 都有,指向其构造函数的prototypeconstructor:原型对象上的属性,指回构造函数
js
function Foo() {}
const f = new Foo();
f.__proto__ === Foo.prototype; // true
Foo.prototype.constructor === Foo; // true
Foo.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
Foo.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Function.__proto__ === Function.prototype; // true(自指)
Object.__proto__ === Function.prototype; // true
4.2 原型链图
javascript
f ──__proto__──▶ Foo.prototype ──__proto__──▶ Object.prototype ──▶ null
│
constructor
▼
Foo ──__proto__──▶ Function.prototype ──▶ Object.prototype
4.3 属性查找
js
Object.prototype.z = 3;
Foo.prototype.y = 2;
f.x = 1;
f.x; // 1 自身属性
f.y; // 2 沿原型链查找
f.z; // 3
f.w; // undefined 直到 null 都找不到
4.4 相关 API
js
Object.getPrototypeOf(f); // 推荐替代 __proto__
Object.setPrototypeOf(f, proto); // 性能差,避免使用
f.hasOwnProperty('x'); // true
Object.hasOwn(f, 'x'); // ES2022 推荐
'y' in f; // true(含原型链)
f instanceof Foo; // true
Foo.prototype.isPrototypeOf(f); // true
// 手写 instanceof
function myInstanceof(obj, Ctor) {
let p = Object.getPrototypeOf(obj);
while (p) {
if (p === Ctor.prototype) return true;
p = Object.getPrototypeOf(p);
}
return false;
}
4.5 属性遮蔽(Shadowing)
js
const proto = { count: 0 };
const o = Object.create(proto);
o.count++; // 等价于 o.count = o.count + 1,在 o 上创建新属性
proto.count; // 0,未被修改
5. 构造函数与 new
5.1 new 做了什么
- 创建空对象
obj obj.__proto__ = Ctor.prototype- 以
obj为this执行构造函数 - 若构造函数返回对象则返回该对象,否则返回
obj
js
function myNew(Ctor, ...args) {
const obj = Object.create(Ctor.prototype);
const result = Ctor.apply(obj, args);
return result !== null && typeof result === 'object' ? result : obj;
}
5.2 new.target
js
function Shape() {
if (!new.target) throw new Error('必须使用 new 调用');
if (new.target === Shape) throw new Error('Shape 是抽象类');
}
5.3 避免忘记 new
js
function User(name) {
if (!(this instanceof User)) return new User(name);
this.name = name;
}
6. ES6 Class 语法
6.1 完整语法一览
js
class Person {
// 公共实例字段
name;
age = 0;
// 私有字段
#id;
static #count = 0;
// 静态字段
static species = 'Homo sapiens';
constructor(name, age) {
this.name = name;
this.age = age;
this.#id = ++Person.#count;
}
// 实例方法(挂在 prototype 上)
greet() {
return `Hi, I'm ${this.name}`;
}
// 私有方法
#validate() {
return this.age >= 0;
}
// getter / setter
get id() { return this.#id; }
get isAdult() { return this.age >= 18; }
set nickname(v) { this._nick = v.trim(); }
// 静态方法
static create(name, age) {
return new Person(name, age);
}
// 静态私有方法
static #reset() { Person.#count = 0; }
// 静态块(ES2022)
static {
console.log('Class initialized');
}
// Symbol 方法
[Symbol.toPrimitive](hint) {
return hint === 'number' ? this.age : this.name;
}
// 生成器方法
*[Symbol.iterator]() {
yield this.name;
yield this.age;
}
// 异步方法
async load() {
return await fetch(`/api/${this.#id}`);
}
}
6.2 Class 与构造函数的区别
| 特性 | 构造函数 | Class |
|---|---|---|
| 提升 | 函数提升 | 存在 TDZ,不可提前使用 |
| 严格模式 | 需手动开启 | 默认严格模式 |
| 方法可枚举 | 是 | 否(enumerable: false) |
| 不用 new 调用 | 允许 | 抛出 TypeError |
| 私有成员 | 闭包模拟 | 原生 # 语法 |
js
typeof Person; // 'function'
Object.keys(Person.prototype); // [] 方法不可枚举
6.3 类表达式与匿名类
js
const Animal = class {
speak() { return 'sound'; }
};
// 立即实例化
const singleton = new (class {
constructor() { this.time = Date.now(); }
})();
6.4 字段初始化顺序
js
class A {
x = this.init(); // 字段初始化在 constructor 体之前执行
constructor() {
console.log('ctor');
}
init() { console.log('field'); return 1; }
}
new A(); // "field" → "ctor"
7. 继承
7.1 ES5 继承演进
js
// ① 原型链继承:引用类型共享,无法传参
Child.prototype = new Parent();
// ② 借用构造函数:方法无法复用
function Child() { Parent.call(this); }
// ③ 组合继承:调用两次父构造函数
function Child() { Parent.call(this); }
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// ④ 寄生组合继承(最优 ES5 方案)
function inherit(Child, Parent) {
Child.prototype = Object.create(Parent.prototype, {
constructor: { value: Child, writable: true, configurable: true }
});
Object.setPrototypeOf(Child, Parent); // 继承静态成员
}
function Parent(name) { this.name = name; }
Parent.prototype.say = function () { return this.name; };
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inherit(Child, Parent);
7.2 ES6 extends
js
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a sound`; }
static kingdom() { return 'Animalia'; }
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 必须在使用 this 前调用
this.breed = breed;
}
speak() {
return `${super.speak()} --- Woof!`; // 调用父类方法
}
static kingdom() {
return super.kingdom() + ' > Canidae'; // 静态方法中的 super
}
}
7.3 super 的原理
super 依赖方法的内部槽 [[HomeObject]],因此:
js
const obj = {
__proto__: parent,
method() { super.method(); } // ✔ 方法简写有 HomeObject
// method: function () { super.method(); } // ✘ 语法错误
};
7.4 继承内置类型
js
class MyArray extends Array {
sum() { return this.reduce((a, b) => a + b, 0); }
}
const arr = MyArray.from([1, 2, 3]);
arr.map(x => x * 2) instanceof MyArray; // true(Symbol.species)
class MyError extends Error {
constructor(msg, code) {
super(msg);
this.name = 'MyError';
this.code = code;
Error.captureStackTrace?.(this, MyError);
}
}
7.5 extends null 与 extends 表达式
js
class Base extends null {} // 原型为 null 的类
// 动态父类
function withLogging(Base) {
return class extends Base {
log(m) { console.log(`[${this.constructor.name}] ${m}`); }
};
}
class Service extends withLogging(Object) {}
7.6 class 继承的底层原理
js
class B extends A {}
// 等价于
Object.setPrototypeOf(B.prototype, A.prototype); // 实例方法继承
Object.setPrototypeOf(B, A); // 静态方法继承
8. 封装与私有成员
8.1 方案对比
| 方案 | 真正私有 | 可继承访问 | 调试友好 | 性能 |
|---|---|---|---|---|
_ 约定 |
✘ | ✔ | ✔ | 高 |
| 闭包 | ✔ | ✘ | ✘ | 低(方法不共享) |
| Symbol | 半私有 | ✔ | ✔ | 高 |
| WeakMap | ✔ | ✘ | 一般 | 中 |
# 字段 |
✔ | ✘ | ✔ | 高 |
8.2 WeakMap 方案
js
const _private = new WeakMap();
class Account {
constructor(balance) {
_private.set(this, { balance });
}
get balance() { return _private.get(this).balance; }
}
8.3 # 私有字段进阶
js
class Wallet {
#balance = 0;
// 私有字段品牌检查(ES2022)
static isWallet(obj) {
return #balance in obj;
}
// 私有字段不能通过 this[...] 动态访问,也不会被 Proxy 拦截
transfer(to, amount) {
this.#balance -= amount;
to.#balance += amount; // 同类实例可以访问
}
}
注意 :# 字段不会出现在原型链上,子类无法访问父类的私有字段。
9. 多态与抽象
9.1 方法重写与鸭子类型
js
class Shape {
area() { throw new Error('必须实现 area()'); }
toString() { return `${this.constructor.name}: ${this.area()}`; }
}
class Circle extends Shape {
constructor(r) { super(); this.r = r; }
area() { return Math.PI * this.r ** 2; }
}
class Rect extends Shape {
constructor(w, h) { super(); this.w = w; this.h = h; }
area() { return this.w * this.h; }
}
[new Circle(1), new Rect(2, 3)].forEach(s => console.log(String(s)));
9.2 模拟抽象类
js
class AbstractRepo {
constructor() {
if (new.target === AbstractRepo) {
throw new TypeError('抽象类不可实例化');
}
for (const m of ['find', 'save']) {
if (typeof this[m] !== 'function') {
throw new TypeError(`必须实现 ${m}()`);
}
}
}
}
9.3 模拟接口
js
const Comparable = {
check(obj) {
if (typeof obj.compareTo !== 'function') {
throw new TypeError('未实现 Comparable');
}
}
};
TypeScript 是更好的选择,但纯 JS 中可用 JSDoc + 运行时检查。
10. Mixin 与组合
10.1 对象 Mixin
js
const Serializable = {
serialize() { return JSON.stringify(this); }
};
const Validatable = {
validate() { return Object.values(this).every(v => v != null); }
};
class User {}
Object.assign(User.prototype, Serializable, Validatable);
10.2 类工厂 Mixin(推荐)
js
const Timestamped = Base => class extends Base {
created = Date.now();
};
const Taggable = Base => class extends Base {
tags = new Set();
tag(t) { this.tags.add(t); return this; }
};
class Post extends Taggable(Timestamped(Object)) {}
const p = new Post().tag('js');
10.3 组合优于继承
js
// 继承:Dog extends Animal(is-a)
// 组合:Dog has Walker, Barker(has-a)
const canWalk = state => ({ walk: () => `${state.name} walks` });
const canBark = state => ({ bark: () => `${state.name} barks` });
function createDog(name) {
const state = { name };
return { ...state, ...canWalk(state), ...canBark(state) };
}
何时用继承 :明确的 is-a 关系、需要共享构造逻辑、框架要求(如 React.Component、HTMLElement)。 何时用组合:行为可复用于不相关的类、避免深层继承树、需要运行时动态组合。
11. 属性描述符与元编程
11.1 数据描述符与访问器描述符
js
Object.defineProperty(obj, 'key', {
value: 42,
writable: false, // 不可修改
enumerable: false, // for-in / Object.keys 不可见
configurable: false // 不可删除、不可再修改描述符
});
Object.defineProperty(obj, 'computed', {
get() { return this._v * 2; },
set(v) { this._v = v; },
enumerable: true,
configurable: true
});
Object.getOwnPropertyDescriptor(obj, 'key');
Object.getOwnPropertyDescriptors(obj);
11.2 对象不可变性
js
Object.preventExtensions(o); // 不能添加
Object.seal(o); // 不能添加、删除
Object.freeze(o); // 不能添加、删除、修改(浅)
function deepFreeze(o) {
Object.values(o).forEach(v => typeof v === 'object' && v && deepFreeze(v));
return Object.freeze(o);
}
11.3 属性遍历方法对比
| 方法 | 自身 | 继承 | 不可枚举 | Symbol |
|---|---|---|---|---|
for...in |
✔ | ✔ | ✘ | ✘ |
Object.keys |
✔ | ✘ | ✘ | ✘ |
Object.getOwnPropertyNames |
✔ | ✘ | ✔ | ✘ |
Object.getOwnPropertySymbols |
✔ | ✘ | ✔ | ✔ |
Reflect.ownKeys |
✔ | ✘ | ✔ | ✔ |
12. Symbol 与内置协议
js
class Money {
constructor(amount, currency) {
this.amount = amount;
this.currency = currency;
}
// 类型转换
[Symbol.toPrimitive](hint) {
if (hint === 'number') return this.amount;
if (hint === 'string') return `${this.amount} ${this.currency}`;
return this.amount; // default
}
// Object.prototype.toString.call(m) → "[object Money]"
get [Symbol.toStringTag]() { return 'Money'; }
// 自定义 instanceof
static [Symbol.hasInstance](obj) {
return 'amount' in obj && 'currency' in obj;
}
// 可迭代
*[Symbol.iterator]() {
yield this.amount;
yield this.currency;
}
// 异步迭代
async *[Symbol.asyncIterator]() { /* ... */ }
// 派生方法返回的类型(Array.map 等)
static get [Symbol.species]() { return Array; }
}
const m = new Money(100, 'USD');
+m; // 100
`${m}`; // "100 USD"
[...m]; // [100, 'USD']
13. Proxy 与 Reflect
13.1 基础
js
const target = { name: 'x' };
const proxy = new Proxy(target, {
get(t, key, receiver) {
console.log(`读取 ${String(key)}`);
return Reflect.get(t, key, receiver);
},
set(t, key, value, receiver) {
if (key === 'age' && typeof value !== 'number') {
throw new TypeError('age 必须是数字');
}
return Reflect.set(t, key, value, receiver);
},
has(t, key) { return key.startsWith('_') ? false : key in t; },
deleteProperty(t, key) { /* ... */ },
ownKeys(t) { return Reflect.ownKeys(t).filter(k => !k.startsWith('_')); },
apply(fn, thisArg, args) { /* 函数调用 */ },
construct(Ctor, args, newTarget) { /* new 操作 */ }
});
13.2 响应式系统(Vue 3 原理)
js
const deps = new WeakMap();
let activeEffect = null;
function track(target, key) {
if (!activeEffect) return;
let map = deps.get(target) ?? (deps.set(target, new Map()), deps.get(target));
let set = map.get(key) ?? (map.set(key, new Set()), map.get(key));
set.add(activeEffect);
}
function trigger(target, key) {
deps.get(target)?.get(key)?.forEach(fn => fn());
}
function reactive(obj) {
return new Proxy(obj, {
get(t, k, r) { track(t, k); return Reflect.get(t, k, r); },
set(t, k, v, r) {
const res = Reflect.set(t, k, v, r);
trigger(t, k);
return res;
}
});
}
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
const state = reactive({ count: 0 });
effect(() => console.log('count:', state.count));
state.count++; // 自动打印 count: 1
13.3 Proxy 与私有字段的冲突
js
class A { #x = 1; getX() { return this.#x; } }
const p = new Proxy(new A(), {});
p.getX(); // TypeError:this 是 proxy 而非原始对象
// 解决:get 陷阱中绑定原始对象
new Proxy(new A(), {
get(t, k) {
const v = Reflect.get(t, k);
return typeof v === 'function' ? v.bind(t) : v;
}
});
14. 设计模式实战
14.1 单例
js
class Config {
static #instance;
static getInstance() {
return Config.#instance ??= new Config();
}
constructor() {
if (Config.#instance) throw new Error('使用 getInstance()');
}
}
14.2 观察者 / 发布订阅
js
class EventEmitter {
#listeners = new Map();
on(evt, fn) {
(this.#listeners.get(evt) ?? this.#listeners.set(evt, new Set()).get(evt)).add(fn);
return () => this.off(evt, fn);
}
off(evt, fn) { this.#listeners.get(evt)?.delete(fn); }
once(evt, fn) {
const off = this.on(evt, (...a) => { off(); fn(...a); });
}
emit(evt, ...args) {
this.#listeners.get(evt)?.forEach(fn => fn(...args));
}
}
14.3 策略模式
js
const strategies = {
credit: amount => amount * 0.98,
paypal: amount => amount - 1,
crypto: amount => amount * 0.995
};
class Checkout {
constructor(strategy) { this.pay = strategies[strategy]; }
}
14.4 装饰器模式(含 Stage 3 装饰器语法)
js
// 函数式装饰
const withRetry = (fn, n = 3) => async (...args) => {
for (let i = 0; i < n; i++) {
try { return await fn(...args); } catch (e) { if (i === n - 1) throw e; }
}
};
// TC39 装饰器(需 Babel / TS 5+)
function log(value, { kind, name }) {
if (kind === 'method') {
return function (...args) {
console.log(`调用 ${name}`);
return value.apply(this, args);
};
}
}
class Api {
@log
fetch() {}
}
14.5 工厂 / 抽象工厂
js
class ShapeFactory {
static #registry = new Map();
static register(type, Ctor) { this.#registry.set(type, Ctor); }
static create(type, ...args) {
const Ctor = this.#registry.get(type);
if (!Ctor) throw new Error(`未知类型 ${type}`);
return new Ctor(...args);
}
}
ShapeFactory.register('circle', Circle);
14.6 责任链
js
class Handler {
#next = null;
setNext(h) { this.#next = h; return h; }
handle(req) { return this.#next?.handle(req) ?? null; }
}
class AuthHandler extends Handler {
handle(req) {
if (!req.user) return 'Unauthorized';
return super.handle(req);
}
}
15. SOLID 原则在 JS 中的应用
S --- 单一职责:一个类只有一个变化的理由。
js
// ✘ User 既管理数据又负责持久化
// ✔ 拆分为 User + UserRepository
O --- 开闭原则:对扩展开放,对修改关闭(用策略/注册表替代 if-else)。
L --- 里氏替换:子类必须能替换父类而不破坏程序。
js
// ✘ Square extends Rectangle 破坏了 setWidth/setHeight 语义
I --- 接口隔离:不强迫类实现用不到的方法(用小 Mixin 替代大基类)。
D --- 依赖倒置:依赖抽象而非具体实现(构造函数注入)。
js
class OrderService {
constructor(repo, mailer) { // 注入依赖而非 new 出来
this.repo = repo;
this.mailer = mailer;
}
}
16. 性能与最佳实践
16.1 V8 隐藏类与内联缓存
js
// ✔ 保持属性初始化顺序一致,避免动态增删属性
class Point {
constructor(x, y) { this.x = x; this.y = y; } // 所有实例同样的形状
}
// ✘ 动态添加导致隐藏类分裂
const p = new Point(1, 2);
p.z = 3;
// ✘ 避免 Object.setPrototypeOf / __proto__ 赋值,会使 IC 失效
// ✘ 避免 delete,改为赋 undefined
16.2 方法定义位置
js
class A {
method() {} // ✔ 原型上共享,一份内存
arrow = () => {}; // ✘ 每个实例一份,仅在需要绑定 this 时使用
}
16.3 通用建议
- 优先
class语法,避免手写原型 - 优先组合,慎用深层继承(超过 2-3 层需反思)
- 用
#私有字段替代_约定 - 用
Object.hasOwn替代hasOwnProperty - 用
Object.freeze保护常量配置 - 不要修改内置原型(
Array.prototype.xxx = ...) - 使用 TypeScript 获得接口、抽象类、访问修饰符
17. 常见陷阱与面试题
17.1 陷阱
js
// 1. 原型上的引用类型被共享
class A { static list = []; } // 所有子类共享同一个数组
// 2. class 方法作为回调 this 丢失
button.addEventListener('click', this.handleClick); // ✘
button.addEventListener('click', () => this.handleClick()); // ✔
// 3. 箭头函数不能作为构造函数
const F = () => {}; new F(); // TypeError
// 4. super 之前访问 this
class B extends A { constructor() { this.x = 1; super(); } } // ReferenceError
// 5. 私有字段与 Proxy 不兼容(见 13.3)
// 6. 字段初始化时父类方法已可用,但子类字段尚未初始化
class P { constructor() { this.init(); } }
class C extends P { value = 1; init() { console.log(this.value); } }
new C(); // undefined!
17.2 经典面试题
Q1:输出什么?
js
function Foo() {
getName = function () { console.log(1); };
return this;
}
Foo.getName = function () { console.log(2); };
Foo.prototype.getName = function () { console.log(3); };
var getName = function () { console.log(4); };
function getName() { console.log(5); }
Foo.getName(); // 2
getName(); // 4(函数声明提升后被表达式覆盖)
Foo().getName(); // 1(Foo() 中修改了全局 getName)
getName(); // 1
new Foo.getName(); // 2(成员访问优先级高于 new 无参)
new Foo().getName(); // 3(new 有参优先级最高,然后访问原型方法)
new new Foo().getName(); // 3
Q2:实现一个支持链式调用和延迟执行的类。
js
class Lazy {
#tasks = [];
constructor(name) { this.#tasks.push(() => console.log(`Hi ${name}`)); setTimeout(() => this.#run()); }
sleep(s) { this.#tasks.push(() => new Promise(r => setTimeout(r, s * 1000))); return this; }
eat(f) { this.#tasks.push(() => console.log(`Eat ${f}`)); return this; }
async #run() { for (const t of this.#tasks) await t(); }
}
new Lazy('Tom').sleep(1).eat('lunch');
Q3:Object.create(null) 与 {} 的区别? 前者没有原型,无 toString/hasOwnProperty 等,适合做纯字典,避免键名冲突和原型污染。
Q4:如何判断一个对象是否由某个类直接实例化?
js
obj.constructor === Cls // 可被篡改
Object.getPrototypeOf(obj) === Cls.prototype // 更可靠
学习路线总结
javascript
入门 ─── 对象字面量 / this / 构造函数 / class 基础
│
进阶 ─── 原型链 / 继承机制 / 封装 / 多态 / Mixin
│
精通 ─── 属性描述符 / Symbol 协议 / Proxy 响应式 / 设计模式 / V8 优化
│
超越 ─── TypeScript 类型系统 / 装饰器 / 架构设计 / 函数式与 OOP 融合
记住核心思想:JavaScript 中一切皆对象,一切继承皆原型链,class 只是语法糖。 掌握这一点,其他所有知识都是它的自然延伸。