javascript原型链如何工作_它与类继承有何不同?

19次阅读

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

javascript原型链如何工作_它与类继承有何不同?

原型链的查找过程是怎么发生的

javascript 原型链本质是对象内部的 [[Prototype]] 隐式引用(可通过 Object.getPrototypeOf(obj) 读取,或非标准但广泛支持的 __proto__ 访问)。当访问一个对象的属性时,引擎按以下顺序查找:

  • 先在对象自身属性中查找(hasOwnPropertytrue 的属性)
  • 没找到,则顺着 [[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() 必须在子类 constructorthis 使用前调用,否则报 ReferenceError: Must call super constructor
  • 箭头函数没有自己的 thisarguments,也不绑定 super,因此不能用于覆盖父类方法并调用 super.xxx()

真正复杂的不是链本身,而是开发者误以为它像 Java/c++ 那样“编译期确定继承结构”——而 JavaScript 中,原型可以随时被修改、替换甚至切断,Object.setPrototypeOf()__proto__ 都可能让运行时行为变得难以追踪。

text=ZqhQzanResources