JavaScript原型链是属性访问的真实查找路径,读取时沿__proto__向上搜索至null,写入仅作用于自身;__proto__、prototype、constructor职责不同,混用易致链断裂;class是语法糖,本质仍基于原型链。

JavaScript 原型链不是“继承机制的模拟”,它就是对象查找属性的真实路径——每次读取 obj.prop,引擎都会顺着 obj.__proto__ 一层层向上找,直到找到或抵达 null。
属性访问时原型链如何被触发
原型链只在「读取属性」或「调用方法」时动态启用,写入或删除操作默认只作用于对象自身(除非属性是 setter 或 configurable: false)。
- 访问
obj.name:先查obj自身属性,没找到 → 查obj.__proto__→ 再查obj.__proto__.__proto__,依此类推 - 赋值
obj.name = 'x':直接在obj上新建或覆盖属性,**不修改原型** -
in操作符和for...in会遍历整个原型链上的可枚举属性;而Object.hasOwn(obj, 'name')(或旧版obj.hasOwnProperty('name'))只检查自身
__proto__、prototype 和 constructor 的关系容易混淆
三者职责完全不同,混用会导致链断裂或意外行为:
-
obj.__proto__是每个对象都有的内部属性,指向其原型对象(即构造函数的prototype) -
Fn.prototype是函数独有的属性,仅当该函数被用作构造函数(new Fn())时,新对象的__proto__才会指向它 -
Fn.prototype.constructor默认回指Fn,但手动重写Fn.prototype = {...}后这个指向会丢失,需显式修复:Fn.prototype.constructor = Fn
function Person(name) {
this.name = name;
}
Person.prototype.say = function() { return 'hi'; };
const p = new Person('Alice');
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
用 Object.create(null) 创建无原型对象的后果
这类对象没有 __proto__,也不继承任何方法(如 toString、hasOwnProperty),常用于实现纯哈希表或避免原型污染,但必须小心使用:
立即学习“Java免费学习笔记(深入)”;
-
obj.toString()会报TypeError: obj.toString is not a function -
Object.hasOwn(obj, key)仍可用(它是静态方法,不依赖obj的原型) - 不能用
obj instanceof Something,因为instanceof依赖原型链查找 - 若需基础方法,可手动挂载:
obj.toString = Object.prototype.toString
ES6 class 并未改变原型链本质
class 只是语法糖,底层仍是基于 prototype 和 __proto__。所有 class 定义的方法都放在 ClassName.prototype 上,实例的 __proto__ 依然指向它。
- 静态方法(
static)挂在类本身上,不是原型链的一部分;实例无法访问 - 继承(
extends)实际设置的是子类的__proto__指向父类(实现静态继承),同时子类prototype.__proto__指向父类prototype(实现实例方法继承) - 不要试图用
class A extends null—— 这会让子类prototype.__proto__为null,导致实例无法访问Object.prototype方法
class Animal {
speak() { return 'sound'; }
}
class Dog extends Animal {
bark() { return 'woof'; }
}
const d = new Dog();
console.log(d.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
真正难的不是理解“链”本身,而是判断某个属性到底落在哪一层——尤其当多个库修改 prototype、或使用 Object.setPrototypeOf() 动态变更时,链可能在运行时被悄悄改写。调试时优先用 Object.getPrototypeOf(obj) 而非 obj.__proto__,更安全也更标准。











