原型链的本质是对象的 proto 指向其构造函数的 prototype;它基于[[Prototype]]内部属性实现动态属性查找,以Object.create()构建更安全,class只是语法糖,核心仍是原型继承。

原型链的本质是对象的 __proto__ 指向其构造函数的 prototype
JavaScript 中每个对象都有一个内部属性 [[Prototype]](可通过 __proto__ 访问),它指向该对象“继承自”的另一个对象。这个被指向的对象,通常就是创建它的构造函数的 prototype 属性值。比如:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
const p1 = new Person('Alice');
console.log(p1.__proto__ === Person.prototype); // true这不是“类继承”,而是运行时动态查找:当访问 p1.sayHello 时,JS 引擎先在 p1 自身找,找不到就查 p1.__proto__(即 Person.prototype),再找不到就继续查 Person.prototype.__proto__(即 Object.prototype),最终到 null 终止。
用 Object.create() 手动构建原型链比直接改 __proto__ 更安全可靠
直接赋值 obj.__proto__ = otherObj 虽然可行,但存在兼容性与性能问题(部分引擎会降级优化),且容易掩盖构造函数信息。推荐方式是用 Object.create() 显式指定原型:
-
Object.create(null)创建无原型的对象(适合做纯字典,避免污染) -
Object.create(Person.prototype)创建一个空对象,其[[Prototype]]指向Person.prototype - 配合
call或apply初始化实例属性,模拟“子类构造”
function Student(name, grade) {
Person.call(this, name); // 复用父构造逻辑
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复 constructor 指针
const s1 = new Student('Bob', 95);
s1.sayHello(); // Hello, Bob
ES6 的 class 只是语法糖,底层仍是原型链,但隐藏了关键细节
class 写法看着像 Java,实际编译后仍生成基于 prototype 和 __proto__ 的结构。例如:
-
extends Parent等价于Child.prototype = Object.create(Parent.prototype) -
super()在构造函数中本质是Parent.call(this, ...) - 静态方法会挂到
Class函数本身,而非prototype;但Class.__proto__指向Parent,实现静态继承
这意味着:用 class 无法直接访问或替换实例的 __proto__ 来切换行为(不像 Object.setPrototypeOf() 那样灵活),也容易误以为“类是实体”,而忽略原型对象是可被多个实例共享的普通对象——一旦在 prototype 上修改引用类型(如数组、对象),所有实例都会受影响。
原型继承真正有效的场景是共享不可变方法和轻量级行为复用,不是替代组合
把通用工具方法(toString、isValid、toJson)放 prototype 是合理的;但把状态、配置、回调函数塞进去,往往导致隐式耦合。常见陷阱包括:
立即学习“Java免费学习笔记(深入)”;
- 忘记重设
constructor,导致instance.constructor指向父构造函数 - 在
prototype上直接写arr: []或cache: {},造成实例间数据污染 - 过度依赖
instanceof判断类型,而该操作依赖__proto__链,在跨 iframe 或模块热更新时可能失效 - 用
for...in遍历对象时未过滤hasOwnProperty,意外读到原型上的方法
真正需要多态或复杂继承关系时,优先考虑组合(如把行为封装成独立对象,通过属性委托调用),而不是拉长原型链。原型链越深,属性查找越慢,调试时也越难追踪来源。










