JavaScript原型链与对象血缘关系—从prototype到class的完整继承体系|新宇宙博客const
x
1
console
log
Object
getPrototypeOf
Object
prototype
console
log
__proto__
Object
prototype
console
log
Reflect
getPrototypeOf
Object
prototype
原型链的终点
console.log(Object.getPrototypeOf(Object.prototype));
const arr = [1, 2, 3];
console.log(Object.getPrototypeOf(arr) === Array.prototype);
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
2. prototype 属性与构造函数
function Person(name) {
this.name = name;
}
console.log(typeof Person.prototype);
console.log(Person.prototype.constructor === Person);
const alice = new Person('Alice');
console.log(Object.getPrototypeOf(alice) === Person.prototype);
console.log(alice.constructor === Person);
constructor 属性的脆弱性
function Animal(type) {
this.type = type;
}
Animal.prototype = {
speak() { return `I am a ${this.type}`; }
};
const dog = new Animal('dog');
console.log(dog.constructor === Animal);
console.log(dog.constructor === Object);
Animal.prototype = {
constructor: Animal,
speak() { return `I am a ${this.type}`; }
};
Object.defineProperty(Animal.prototype, 'constructor', {
value: Animal,
enumerable: false,
writable: true,
configurable: true
});
3. 原型链查找算法
ECMAScript §10.1.8 定义了 [[Get]] 操作的查找过程:
function lookup(obj, prop) {
let current = obj;
while (current !== null) {
if (Object.prototype.hasOwnProperty.call(current, prop)) {
return { found: true, value: current[prop], owner: current };
}
current = Object.getPrototypeOf(current);
}
return { found: false, value: undefined };
}
const grandparent = { family: 'Smith', legacy: 'old' };
const parent = Object.create(grandparent);
parent.generation = 2;
parent.legacy = 'updated';
const child = Object.create(parent);
child.name = 'Bob';
console.log(lookup(child, 'name'));
console.log(lookup(child, 'generation'));
console.log(lookup(child, 'family'));
console.log(lookup(child, 'legacy'));
console.log(lookup(child, 'missing'));
V8 的隐藏类优化
function Point(x, y) {
this.x = x;
this.y = y;
}
const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
const p3 = new Point(5, 6);
p3.z = 7;
4. Object.create 与纯净原型
const dict = Object.create(null);
dict.key = 'value';
console.log(dict.toString);
console.log('key' in dict);
const immutable = Object.create(Object.prototype, {
id: { value: 1, writable: false, enumerable: true, configurable: false },
name: { value: 'fixed', writable: false, enumerable: true, configurable: false }
});
immutable.id = 2;
console.log(immutable.id);
Object.create(null) 的实际应用
function createSafeCache() {
const cache = Object.create(null);
return {
get(key) { return cache[key]; },
set(key, value) { cache[key] = value; },
has(key) { return key in cache; },
delete(key) { delete cache[key]; }
};
}
const cache = createSafeCache();
cache.set('__proto__', 'safe');
cache.set('constructor', 'safe');
5. class 语法糖的原型本质
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound.`;
}
static create(name) {
return new this(name);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
speak() {
return `${this.name} barks.`;
}
}
function AnimalES5(name) {
this.name = name;
}
AnimalES5.prototype.speak = function() {
return `${this.name} makes a sound.`;
};
AnimalES5.create = function(name) {
return new this(name);
};
function DogES5(name) {
AnimalES5.call(this, name);
}
DogES5.prototype = Object.create(AnimalES5.prototype);
DogES5.prototype.constructor = DogES5;
Object.setPrototypeOf(DogES5, AnimalES5);
DogES5.prototype.speak = function() {
return `${this.name} barks.`;
};
验证 class 的原型结构
const rex = new Dog('Rex');
console.log(rex instanceof Dog);
console.log(rex instanceof Animal);
console.log(Object.getPrototypeOf(rex) === Dog.prototype);
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype);
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Dog) === Animal);
console.log(Object.getPrototypeOf(Animal) === Function.prototype);
6. instanceof 与 Symbol.hasInstance
function myInstanceof(obj, Constructor) {
let proto = Object.getPrototypeOf(obj);
const target = Constructor.prototype;
while (proto !== null) {
if (proto === target) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
}
class EvenNumber {
static [Symbol.hasInstance](instance) {
return typeof instance === 'number' && instance % 2 === 0;
}
}
console.log(2 instanceof EvenNumber);
console.log(3 instanceof EvenNumber);
console.log(4 instanceof EvenNumber);
7. 属性遮蔽与原型污染
属性遮蔽的三种情况
const proto = {};
Object.defineProperty(proto, 'readOnly', {
value: 'original',
writable: false
});
const child = Object.create(proto);
child.readOnly = 'modified';
console.log(child.readOnly);
console.log(child.hasOwnProperty('readOnly'));
原型污染攻击
function vulnerableMerge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
vulnerableMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
const malicious = JSON.parse('{"__proto__": {"isAdmin": true}}');
vulnerableMerge({}, malicious);
console.log(({}).isAdmin);
function safeMerge(target, source) {
for (const key of Object.keys(source)) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
safeMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
8. 实战案例
实战案例 1:高效的继承模式 — 组合寄生继承
function inheritPrototype(SubType, SuperType) {
const proto = Object.create(SuperType.prototype);
Object.defineProperty(proto, 'constructor', {
value: SubType,
enumerable: false,
writable: true,
configurable: true
});
SubType.prototype = proto;
Object.setPrototypeOf(SubType, SuperType);
}
function EventEmitter() {
this._events = Object.create(null);
}
EventEmitter.prototype.on = function(event, listener) {
(this._events[event] || (this._events[event] = [])).push(listener);
return this;
};
EventEmitter.prototype.emit = function(event, ...args) {
const listeners = this._events[event] || [];
listeners.forEach(fn => fn.apply(this, args));
};
function Server(port) {
EventEmitter.call(this);
this.port = port;
}
inheritPrototype(Server, EventEmitter);
Server.prototype.start = function() {
this.emit('start', this.port);
};
实战案例 2:Mixin 模式 — 多继承的替代方案
const Serializable = (Base) => class extends Base {
serialize() {
return JSON.stringify(this);
}
static deserialize(json) {
return Object.assign(new this(), JSON.parse(json));
}
};
const Validatable = (Base) => class extends Base {
validate() {
const rules = this.constructor.validationRules || {};
const errors = [];
for (const [field, rule] of Object.entries(rules)) {
if (!rule(this[field])) {
errors.push(`Invalid ${field}: ${this[field]}`);
}
}
return errors.length === 0 ? { valid: true } : { valid: false, errors };
}
};
class User extends Serializable(Validatable(class {})) {
static validationRules = {
name: (v) => typeof v === 'string' && v.length > 0,
age: (v) => typeof v === 'number' && v >= 0
};
constructor(name, age) {
super();
this.name = name;
this.age = age;
}
}
const user = new User('Alice', 30);
console.log(user.validate());
console.log(user.serialize());
实战案例 3:性能优化 — 避免原型链过长
class A { methodA() {} }
class B extends A { methodB() {} }
class C extends B { methodC() {} }
class D extends C { methodD() {} }
class E extends D { methodE() {} }
class OptimizedE {
constructor() {
this.methodA = () => { };
}
methodB() {}
methodC() {}
methodD() {}
methodE() {}
}
9. 深度追问
Q1:为什么 Object.setPrototypeOf 被认为是性能杀手?
修改对象的 [[Prototype]] 会使 V8 的隐藏类(Map)失效,导致所有与该对象相关的内联缓存(IC)需要重新计算。V8 甚至会将受影响的函数从优化代码降级回解释执行。建议在对象创建时通过 Object.create 设定原型,而非后期修改。
Q2:class 中的私有字段如何影响原型继承?
私有字段(#field)存储在实例上,通过 WeakMap 语义实现(V8 实际使用 Brand Check)。它们不参与原型链查找,子类无法访问父类的私有字段,即使通过 super 也不行。
Q3:如何安全地检测一个属性是否来自原型链?
Object.prototype.hasOwnProperty.call(obj, 'prop');
Object.hasOwn(obj, 'prop');
10. 总结表格
| 概念 | 描述 | 访问方式 |
|---|
[[Prototype]] | 对象的内部原型槽 | Object.getPrototypeOf() |
__proto__ | 访问器属性(Annex B) | obj.__proto__ |
.prototype | 函数的 prototype 属性 | Fn.prototype |
constructor | 指回构造函数 | obj.constructor |
| 创建方式 | 原型设置 | 使用场景 |
|---|
new Fn() | Fn.prototype | 构造函数模式 |
Object.create(proto) | 指定 proto | 纯原型继承 |
class extends | 自动设置两条链 | 现代继承 |
{} 字面量 | Object.prototype | 普通对象 |
Object.create(null) | null | 纯净字典 |
| 性能建议 | 原因 |
|---|
避免 Object.setPrototypeOf | 破坏隐藏类优化 |
| 保持原型链 ≤ 4 层 | 查找深度影响 IC 效率 |
| 同形对象共享结构 | Hidden Class 复用 |
| 避免运行时增删原型方法 | Map 转换开销 |