JavaScript中的显式原型与隐式原型:深入理解原型链

发布于:2025-06-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、核心概念解析

二、原型链运作机制

三、构造函数的原型三角关系 

四、ES6类语法糖的本质 

五、实现原型继承的三种方式

六、关键注意事项

七、原型链应用场景

八、总结核心要点


一、核心概念解析

  1. 显式原型 (prototype)

    • 函数特有的属性

    • 当函数作为构造函数使用时,其实例会继承该函数的prototype

    • 本质是一个包含constructor属性和共享方法的普通对象

      function Person(name) {
        this.name = name;
      }
      
      // 向显式原型添加方法
      Person.prototype.sayHello = function() {
        console.log(`Hello, I'm ${this.name}`);
      };
      
      // 验证类型
      console.log(typeof Person.prototype); // "object"
      console.log(Person.prototype.constructor === Person); // true

       

  2. 隐式原型 (proto)

    • 每个对象都拥有的属性

    • 指向创建该对象时使用的构造函数的prototype

    • 形成原型链的关键连接点(ES6后建议使用Object.getPrototypeOf()获取)

      const john = new Person("John");
      
      // 对象通过__proto__访问构造函数的原型
      console.log(john.__proto__ === Person.prototype); // true
      
      // 推荐的标准获取方式
      console.log(Object.getPrototypeOf(john) === Person.prototype); // true

二、原型链运作机制

[实例对象] john
    │
    ├── __proto__ → [Person.prototype]
    │        │
    │        ├── sayHello() 
    │        │
    │        └── __proto__ → [Object.prototype]
    │                 │
    │                 ├── toString()
    │                 ├── valueOf()
    │                 │
    │                 └── __proto__ → null
    │
    └── name: "John"

 

原型链查找规则

  1. 访问对象属性时,先在自身查找

  2. 找不到则通过__proto__向上级原型查找

  3. 直到Object.prototype(所有对象的基类)

  4. 最终到达null停止查找

    console.log(john.toString()); // 来自Object.prototype
    console.log(john.sayHello()); // 来自Person.prototype
    
    // 验证原型链关系
    console.log(john instanceof Person); // true
    console.log(john instanceof Object); // true

三、构造函数的原型三角关系 

function Animal(type) {
  this.type = type;
}

const dog = new Animal("哺乳动物");

// 经典三角关系:
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true
console.log(Object.getPrototypeOf(Animal) === Function.prototype); // true

四、ES6类语法糖的本质 

class Vehicle {
  constructor(wheels) {
    this.wheels = wheels;
  }
  
  drive() {
    console.log(`Driving with ${this.wheels} wheels`);
  }
}

// 类本质仍是构造函数
console.log(typeof Vehicle); // "function"

const car = new Vehicle(4);

// 验证原型关系
console.log(car.__proto__ === Vehicle.prototype); // true
console.log(Vehicle.prototype.__proto__ === Object.prototype); // true

五、实现原型继承的三种方式

  1. 组合继承(最经典方式):

    function Parent(name) {
      this.name = name;
    }
    
    Parent.prototype.speak = function() {
      console.log(`I'm ${this.name}`);
    };
    
    function Child(name, age) {
      Parent.call(this, name); // 继承属性
      this.age = age;
    }
    
    // 建立原型链
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    
    // 添加子类方法
    Child.prototype.cry = function() {
      console.log("Waahh!");
    };

  2. ES6类继承

    class Employee {
      constructor(name) {
        this.name = name;
      }
      
      work() {
        console.log(`${this.name} is working`);
      }
    }
    
    class Manager extends Employee {
      constructor(name, teamSize) {
        super(name);
        this.teamSize = teamSize;
      }
      
      manage() {
        console.log(`Managing ${this.teamSize} people`);
      }
    }

  3. Object.create实现纯对象继承: 

    const parent = {
      greet() {
        console.log("Hello from parent!");
      }
    };
    
    const child = Object.create(parent, {
      cry: {
        value: function() {
          console.log("Waaaah!");
        }
      }
    });

六、关键注意事项

  1. 原型污染风险

    // 修改内置原型需谨慎
    Array.prototype.sum = function() {
      return this.reduce((a, b) => a + b, 0);
    };
    
    const nums = [1, 2, 3];
    console.log(nums.sum()); // 6 - 但可能引发冲突

  2. 性能优化技巧

    // 在构造函数中声明方法(适用于不共享的独特方法)
    function Player(id) {
      this.id = id;
      
      // 每个实例都有独立方法副本
      this.getID = function() {
        return this.id;
      };
    }
    
    // 在原型上声明方法(节省内存)
    Player.prototype.getScore = function() {
      return this.score;
    };

  3. 现代替代方案

  • 使用Object.getPrototypeOf()替代__proto__

  • 使用Object.setPrototypeOf()设置原型

  • ES6的ReflectAPI提供更多元操作

 

七、原型链应用场景

  1. 实现混入模式(Mixins)

    const canEat = {
      eat() {
        console.log(`${this.name} is eating`);
      }
    };
    
    const canSleep = {
      sleep() {
        console.log(`${this.name} is sleeping`);
      }
    };
    
    function assignPrototypes(target, ...sources) {
      Object.assign(target.prototype, ...sources);
    }
    
    function Cat(name) {
      this.name = name;
    }
    
    assignPrototypes(Cat, canEat, canSleep);
    
    const garfield = new Cat("Garfield");
    garfield.eat(); // "Garfield is eating"

八、总结核心要点

  1. 显式原型(prototype):函数的属性,用于实现方法共享

  2. 隐式原型(proto):对象的内部链接,指向其构造函数的prototype

  3. 原型链:通过__proto__连接形成的继承链

  4. 最佳实践

  • 方法定义在prototype上节省内存

  • 属性定义在构造函数中保持独立

  • 避免直接修改内置对象原型

  • 优先使用ES6类语法提高可读性

 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到