17370845950

javascript原型链的继承机制是什么_构建对象与理解__proto__的奥秘【教程】
JavaScript原型链是原生对象关系模型,通过__proto__链接对象实现属性共享;new操作创建对象并设置其__proto__指向构造函数的prototype;__proto__属对象,prototype属函数;应避免直接赋值__proto__,改用Object.setPrototypeOf或Object.create。

JavaScript 原型链不是“继承机制”的模拟,而是它唯一的、原生的对象关系模型——没有类继承,只有对象通过 __proto__ 链接另一个对象,从而共享属性和方法。

为什么 new 一个函数会得到有原型的对象

调用 new Foo() 时,JS 引擎实际做了三件事:创建空对象、把该对象的 __proto__ 指向 Foo.prototype、再以该对象为 this 执行 Foo 函数。关键点在于:__proto__ 是每个对象都有的内部引用,它决定属性查找路径的起点。

常见错误是认为 __proto__ 是构造函数的属性——其实不是:Foo.__proto__ 指向的是 Function.prototype,而 new Foo().__proto__ 才指向 Foo.prototype

  • __proto__ 是对象的属性(非标准但被所有主流引擎支持),prototype 是函数

    的属性,两者作用域完全不同
  • 箭头函数没有 prototype 属性,不能用 new 调用
  • 手动改写 obj.__proto__ = otherObj 会触发性能惩罚,应优先用 Object.setPrototypeOf()Object.create()

如何正确设置原型链:不要直接赋值 __proto__

直接操作 __proto__ 会导致隐藏类失效,V8 会将对象标记为“字典模式”,后续属性访问变慢。现代写法应使用标准 API 构建原型关系。

const parent = { say() { return 'hi'; } };
const child = Object.create(parent); // ✅ 正确:child.__proto__ === parent
child.name = 'alice';

// ❌ 避免:
const badChild = {};
badChild.__proto__ = parent;

如果需要模拟“子类构造函数”,应确保:构造函数的 prototype 对象本身继承自父类的 prototype

  • Object.setPrototypeOf(Child.prototype, Parent.prototype)
  • 或更安全地: Child.prototype = Object.create(Parent.prototype),再补回 constructor
  • ES6 class extends 底层就是这套逻辑,但禁止在子类构造器中不调用 super()

instanceof 和 isPrototypeOf 的底层判断逻辑

a instanceof B 实际执行的是:从 a.__proto__ 开始,沿原型链向上查找,看是否出现 B.prototype。它不关心构造函数名,只认 prototype 对象的恒等性。

因此这些会返回 true

function A() {}
const a = new A();
console.log(a instanceof A); // true
console.log(A.prototype.isPrototypeOf(a)); // true

但这些容易出错:

  • 重写了 A.prototype 后再创建实例 → 新旧实例的 instanceof 结果可能不一致
  • 跨 iframe 的对象:不同全局环境中的 Array.prototype 不相等,[].constructor === Arrayfalse
  • instanceof nullundefined 直接报 TypeError

真正难的不是画出原型链示意图,而是意识到:任何对象的属性读取(包括方法调用)都依赖这条链的实时遍历;一旦中间某个环节被意外切断(比如误删 prototype.constructor 或覆盖 __proto__),行为就会静默变化——而这类问题往往在深层嵌套或动态代理场景下才暴露。