找回密码
 立即注册
首页 业界区 业界 一文搞清楚什么是JavaScript prototype原型链 ...

一文搞清楚什么是JavaScript prototype原型链

甘子萱 2025-6-6 09:54:42
JavaScript prototype 详解

JavaScript 中,prototype(原型) 是面向对象编程的核心概念之一。它通过 原型链(Prototype Chain) 实现继承,使对象可以共享其他对象的属性和方法。理解原型机制是掌握 JavaScript 面向对象编程的关键。
什么是 prototype?

每个 JavaScript 函数(构造函数)都有一个 prototype 属性,它是一个对象。所有由该函数创建的 实例对象 都会继承这个原型对象的属性和方法。
  1. function Person(name) {
  2.     this.name = name;
  3. }
  4. // 方法添加到原型,所有实例共享
  5. Person.prototype.sayHello = function() {
  6.     console.log(`Hello, my name is ${this.name}`);
  7. };
  8. const p1 = new Person("Joe");
  9. const p2 = new Person("Mary");
  10. p1.sayHello(); // Hello, my name is Joe
  11. p2.sayHello(); // Hello, my name is Mary
  12. // 实例的 __proto__ 指向构造函数的 prototype
  13. console.log(p1.__proto__ === Person.prototype); // true
复制代码
在上面的代码中:

  • Person.prototype 是 Person 构造函数的原型对象。
  • sayHello 方法被所有 Person 的实例共享,而不是每个实例都创建一份新的拷贝,节省内存。
  • p1.__proto__ 指向 Person.prototype,表示 p1 继承了 Person.prototype 上的方法。
  • __proto__ 是实例对象的隐式原型引用(非标准属性,可以用 Object.getPrototypeOf() 替代)。
属性__proto__ 与 prototype 关系

JavaScript 中每个对象都有一个隐藏的 __proto__ 属性(这个并非标准属性,虽然大部分浏览器都支持),它指向创建该对象的构造函数的 prototype:
  1. console.log(p1.__proto__ === Person.prototype); // true
  2. console.log(Person.prototype.__proto__ === Object.prototype); // true
  3. console.log(Object.prototype.__proto__ === null); // true
复制代码
这个原型链的结构如下:
  1. // 访问对象属性时,若当前对象没有,则沿原型链向上查找。
  2. // Object.prototype 是原型链的终点,其 __proto__ 为 null。
  3. p1 → Person.prototype → Object.prototype → null
复制代码
原型链继承

可以通过 prototype 让一个构造函数继承另一个构造函数的方法和属性。
注意,使用 Object.create 创建的子对象不会调用父构造函数,仅用于设置原型:
  1. function Parent(name) {
  2.     this.name = name;
  3. }
  4. Parent.prototype.makeSound = function() {
  5.     console.log("Parent are saying.");
  6. };
  7. function Child(name, age) {
  8.     Parent.call(this, name); // 继承属性
  9.     this.age = age;
  10. }
  11. // 使用 Object.create 创建新的原型对象,让 Child 继承 Parent 的方法
  12. Child.prototype = Object.create(Parent.prototype);
  13. // 修正 constructor 指向,否则 Child.prototype.constructor 会指向 Parent
  14. Child.prototype.constructor = Child;
  15. Child.prototype.speak = function() {
  16.     console.log("Child is talking.");
  17. };
  18. const d = new Child("Child1", 18);
  19. d.makeSound(); // Parent are saying.
  20. d.speak(); // Child is talking.
复制代码
步骤分析:

  • Object.create(Parent.prototype)
    创建一个新对象,其原型指向 Parent.prototype,确保子类原型不污染父类。
  • 修复 constructor 指向
    若不修复,Child.prototype.constructor 将指向 Parent,导致实例的 constructor 错误。
  • 构造函数借用 (Parent.call)
    在子类构造函数中调用父类构造函数,初始化实例属性。
ES6 class 语法的 prototype

ES6 的 class 是原型的语法糖,本质仍基于原型链:
  1. class Person {
  2.     constructor(name) {
  3.         this.name = name;
  4.     }
  5.    
  6.     // 方法自动添加到 Person.prototype
  7.     sayHello() {
  8.         console.log(`Hello, my name is ${this.name}`);
  9.     }
  10. }
  11. const p = new Person("Tom");
  12. p.sayHello(); // Hello, my name is Tom
  13. // 静态方法添加到构造函数本身
  14. Person.staticMethod = function() {
  15.     console.log("This is a static method.");
  16. };
  17. console.log(Object.getPrototypeOf(p) === Person.prototype); // true
复制代码
在这个例子中:

  • sayHello 方法实际存储在 Person.prototype。
  • static 关键字定义的方法属于构造函数本身,而非原型。
prototype验证

构造函数与原型的关系
  1. // 验证 Object 是 Function 的实例
  2. console.log(Object instanceof Function); // 输出: true
  3. // 验证 Function 继承自 Object.prototype
  4. console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype); // 输出: true
  5. // 自定义函数和对象
  6. function A() {}
  7. const a = new A();
  8. // 验证自定义函数是 Function 的实例
  9. console.log(A instanceof Function); // 输出: true
  10. // 验证自定义对象的原型是自定义函数的 prototype
  11. console.log(Object.getPrototypeOf(a) === A.prototype); // 输出: true
  12. // 验证自定义函数的 prototype 的原型是 Object.prototype
  13. console.log(Object.getPrototypeOf(A.prototype) === Object.prototype); // 输出: true
  14. // 原型链的终点
  15. console.log(Object.prototype.__proto__); // null
复制代码
prototype图形展示
  1. // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
  2. +----------------+  constructor  +---------------------+
  3. |   Function     | <------------ |  Function.prototype |
  4. | (Function 本身) | ------------> |    (Function 原型)   |
  5. +----------------+   prototype   +---------------------+
  6.                      __proto__       ^     |
  7.                                      |     |
  8.        |------------------------------     | __proto__
  9.        |          __proto__                |
  10.        |                                   v
  11. +----------------+  constructor +------------------+               +--------+
  12. |    Function    | <----------  |  Object.prototype | --------->   |  终点   |
  13. | (Object 函数)   | ---------->  |  (所有对象的基类)   |  __proto__   |  null  |
  14. +----------------+  prototype   +------------------+               +--------+
  15.                                           ^
  16.                                           |
  17.                                           | __proto__
  18.                                           |
  19. +----------------+  constructor +----------------------+
  20. | function Foo() |  <---------  |   Foo.prototype      |
  21. |  (自定义函数)    |  ----------> |  (自定义函数原型对象)   |
  22. +----------------+   prototype  +----------------------+
  23.                                           ^
  24.                                           |
  25.                                           |
  26. +----------------+                        |
  27. |   new Foo()    | -----------------------|
  28. |  (函数实例对象)  |      __proto__
  29. +----------------+           
复制代码
实例化对象步骤(new 关键字的执行过程)

实例化 const newFoo = new Foo(); 的步骤
  1. function Foo() {}
  2. const newFoo = new Foo();
复制代码
1. 创建一个新对象
JavaScript 先创建一个新的空对象 newFoo。
  1. const newFoo = {};
复制代码
2. 设置新对象的原型
newFoo.__proto__ 被设置为 Foo.prototype,即 newFoo 继承了 Foo.prototype 的属性和方法。
  1. newFoo.__proto__ = Foo.prototype;
复制代码
3. 执行构造函数,并绑定 this
  1. const result = Foo.apply(newFoo, arguments);
复制代码
调用 Foo 构造函数,并将 newFoo 作为 this 传入。
若 Foo 显式返回一个对象,则 new 操作符返回该对象;否则返回 newFoo。
4. 返回对象
若构造函数返回对象,则返回该对象。否则返回新创建的 obj。
  1. return typeof result === "object" && result !== null ? result : newFoo;
复制代码
原型链分析

基于上面 const newFoo = new Foo(); 进行分析。
原型链指向
  1. newFoo.__proto__ === Foo.prototype   // ✅ `newFoo` 的原型是 `Foo.prototype`
  2. Foo.prototype.__proto__ === Object.prototype   // ✅ `Foo.prototype` 的原型是 `Object.prototype`
  3. Object.prototype.__proto__ === null   // ✅ `Object.prototype` 的原型是 `null`(即原型链的终点)
复制代码
构造器关系
  1. newFoo.constructor === Foo.prototype.constructor   // ✅ `newFoo` 的构造函数是 `Foo`
  2. Foo.prototype.constructor === Foo   // ✅ `Foo.prototype` 的 `constructor` 指向 `Foo` 本身
  3. Foo.prototype.constructor.prototype === Foo.prototype   // ✅ `Foo.prototype.constructor` 的 `prototype` 仍然是 `Foo.prototype`
复制代码
说明:

  • 当我们创建一个新对象时,它的 constructor 属性通常来源于它的原型(即 Foo.prototype.constructor)。
  • 使用 Object.create 或修改原型时,有可能需要手动修正 constructor 指向。
Function 和 Object 互相指向
  1. Foo.prototype.__proto__.constructor.__proto__ === Function.prototype   // ✅ `Object` 构造函数的 `__proto__` 指向 `Function.prototype`
  2. Function.prototype === Object.__proto__   // ✅ `Function.prototype` 就是 `Object` 的 `__proto__`
  3. Function.prototype.__proto__.__proto__ === null   // ✅ `Function.prototype.__proto__` 是 `Object.prototype`,再往上是 `null`
复制代码
构造器和原型链的循环指向
  1. Foo.prototype.constructor.prototype.constructor === Foo   // ✅ 循环指向 `Foo`
  2. Foo.prototype.constructor.prototype.constructor.prototype === Foo.prototype   // ✅ 再次循环指向 `Foo.prototype`
  3. Foo.prototype.constructor === Foo   // ✅ `Foo.prototype.constructor` 仍然指向 `Foo`
复制代码
Object 和 Function 之间的关系
  1. Object.prototype.constructor === Object   // ✅ `Object.prototype` 的 `constructor` 是 `Object`
  2. Object.prototype.constructor.__proto__ === Function.prototype   // ✅ `Object` 构造函数本身是 `Function` 的一个实例
  3. Function.constructor.__proto__ === Function.prototype   // ✅ `Function` 构造函数的 `__proto__` 也是 `Function.prototype`
  4. Function.prototype.__proto__ === Object.prototype   // ✅ `Function.prototype` 继承自 `Object.prototype`
  5. Function.__proto__.__proto__ === Object.prototype   // ✅ `Function.__proto__` 继承自 `Function.prototype`,最终指向 `Object.prototype`
  6. Object.prototype.__proto__ === null   // ✅ `Object.prototype` 是原型链终点
复制代码
原型使用的注意事项


  • 避免直接修改内置原型
    如 Array.prototype.myMethod = ... 可能导致兼容性问题。
  • 原型属性的共享特性
    引用类型(如数组)的属性可能被所有实例意外修改:
  1. function MyClass() {}
  2. MyClass.prototype.data = [];
  3. const a = new MyClass();
  4. a.data.push(1); // 所有实例的 data 都会变化
复制代码

  • 性能优化
    将方法定义在原型上,而非构造函数内,减少内存占用。
总结


  • prototype 属性

  • 每个 JavaScript 函数 都有一个 prototype 属性(除了箭头函数)。
  • prototype 是一个对象,所有由该函数创建的实例都会共享 prototype 上的方法。
  • __proto__ 指向该对象的原型(即构造函数的 prototype),形成原型链。
  • 通过 Object.create() 进行原型继承,ES6 class 语法是 prototype 的语法糖。
  • 原型链终点 为 Object.prototype,其 proto 为 null。


  • new 关键字的作用

  • 创建一个新对象 newFoo
  • 设置 newFoo.__proto__ = Foo.prototype
  • 执行 Foo 并绑定 this
  • 返回 newFoo 或构造函数返回的对象


  • 构造函数、原型和 Object 的关系

  • Foo.prototype 继承自 Object.prototype
  • Object.prototype 是所有对象的原型链终点
  • Object 和 Function 互相指向,Object 也是 Function 的一个实例
  • Function.prototype.__proto__ === Object.prototype,最终 Function 也继承自 Object
更多链接:
https://github.com/microwind/design-patterns

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册