JavaScript原型链查找先在对象自身属性中查找,未找到则沿[[Prototype]]向上遍历直至null;构造函数通过prototype和new隐式赋值连接原型;class是语法糖,本质仍是原型继承;需注意共享属性污染、static方法位置及super调用时机等陷阱。

原型链的查找过程是怎么发生的
JavaScript 原型链本质是对象内部的 [[Prototype]] 隐式引用(可通过 Object.getPrototypeOf(obj) 读取,或非标准但广泛支持的 __proto__ 访问)。当访问一个对象的属性时,引擎按以下顺序查找:
- 先在对象自身属性中查找(
hasOwnProperty为true的属性) - 没找到,则顺着
[[Prototype]]指向的对象继续查找 - 重复该过程,直到找到该属性,或到达原型链顶端(
null)
这个过程是动态的:修改某个中间原型上的属性,会影响所有继承自它的实例——因为它们共享同一原型对象。
用 function 构造函数模拟类时原型怎么连
传统构造函数方式下,原型链连接靠两个关键点:prototype 属性和 new 操作符的隐式赋值。例如:
function Person(name) {
this.name = name;
}
Person.prototype.say = function() { return 'Hi'; };
const p = new Person('Alice');
console.log(p.say()); // 'Hi'
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
注意:Person.prototype 是给实例用的原型;而 Person.__proto__ 指向 Function.prototype(因为函数本身也是对象),这是常被混淆的一层。
立即学习“Java免费学习笔记(深入)”;
class 语法糖背后仍是原型链
class 并没有引入新继承模型,只是对原型机制的封装。下面两段代码行为一致:
// class 写法
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a noise.`; }
}
class Dog extends Animal {
speak() { return `${this.name} barks.`; }
}
// 等价的传统写法(忽略细节差异如不可枚举、不可重写等)
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { return `${this.name} makes a noise.`; };
function Dog(name) { Animal.call(this, name); }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() { return `${this.name} barks.`; };
区别在于:class 自动设置 [[Prototype]] 链(Dog.__proto__ === Animal),且方法默认不可枚举、不可配置;而手动写容易漏掉 Object.setPrototypeOf(Dog.prototype, Animal.prototype) 或忘记修复 constructor,导致 instanceof 失效或序列化异常。
继承时容易踩的坑:属性 vs 方法、静态成员、super 调用时机
原型链只负责「方法共享」和「动态属性查找」,但不处理实例属性初始化逻辑。常见问题包括:
- 在父类
prototype上定义对象/数组属性(如items: []),会被所有实例共享,造成数据污染 -
static方法不进原型链,而是直接挂在构造函数上,所以Dog.staticMethod()可用,但dogInstance.staticMethod()报错 -
super()必须在子类constructor中this使用前调用,否则报ReferenceError: Must call super constructor - 箭头函数没有自己的
this和arguments,也不绑定super,因此不能用于覆盖父类方法并调用super.xxx()
真正复杂的不是链本身,而是开发者误以为它像 Java/C++ 那样“编译期确定继承结构”——而 JavaScript 中,原型可以随时被修改、替换甚至切断,Object.setPrototypeOf() 和 __proto__ 都可能让运行时行为变得难以追踪。











