JavaScript原型、原型链

54 阅读2分钟

JavaScript原型、原型链

在 JavaScript 中,原型(Prototype)原型链(Prototype Chain) 是实现继承和共享属性和方法的机制。理解它们是掌握 JavaScript 面向对象编程的关键。

1. 原型(Prototype)

(1) 什么是原型?

  • 每个 JavaScript 对象(除 null 外)都有一个内部属性 [[Prototype]],指向其原型对象。
  • 原型对象也是一个普通对象,可以包含属性和方法。
  • 通过 __proto__(非标准)或 Object.getPrototypeOf() 访问原型。

(2) 原型的作用

  • 共享属性和方法:所有实例共享原型上的属性和方法,节省内存。
  • 实现继承:通过原型链访问父类的属性和方法。

(3) 示例

const person = {
  greet() {
    console.log(`你好,我是${this.name}`);
  }
};

const student = {
  name: '张三',
  __proto__: person // 设置 student 的原型为 person
};

student.greet(); // 输出:你好,我是张三

2. 原型链(Prototype Chain)

(1) 什么是原型链?

  • 当访问对象的属性或方法时,如果对象本身没有,JavaScript 会沿着 [[Prototype]] 向上查找,直到找到或到达 null
  • 这种链式查找机制称为 原型链

(2) 原型链的终点

  • 所有对象的原型链最终指向 Object.prototype,而 Object.prototype 的原型是 null

(3) 示例

const animal = {
  eat() {
    console.log(`${this.name} 正在吃东西`);
  }
};

const dog = {
  name: '小黑',
  __proto__: animal
};

dog.eat(); // 输出:小黑 正在吃东西
console.log(dog.toString()); // 输出:[object Object](来自 Object.prototype)

3. 构造函数与原型

(1) 构造函数的作用

  • 构造函数用于创建对象实例。
  • 每个构造函数都有一个 prototype 属性,指向原型对象。
  • 实例的 __proto__ 指向构造函数的 prototype

(2) 示例

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`你好,我是${this.name}`);
};

const person1 = new Person('李四');
person1.greet(); // 输出:你好,我是李四
console.log(person1.__proto__ === Person.prototype); // true

4. 原型链的继承

(1) 实现继承

  • 通过设置子类构造函数的 prototype 为父类实例,实现原型链继承。
  • 问题:父类实例属性会被所有子类实例共享。

(2) 示例

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

Animal.prototype.eat = function() {
  console.log(`${this.name} 正在吃东西`);
};

function Dog(name, breed) {
  Animal.call(this, name); // 调用父类构造函数
  this.breed = breed;
}

// 设置 Dog.prototype 为 Animal 的实例
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复构造函数指向

const dog = new Dog('小黑', '拉布拉多');
dog.eat(); // 输出:小黑 正在吃东西

5. 原型链的查找机制

(1) 查找过程

  1. 检查对象自身是否有该属性或方法。
  2. 如果没有,沿着 __proto__ 查找原型对象。
  3. 重复步骤 2,直到找到或到达 null

(2) 示例

const obj = {};
console.log(obj.toString()); // 输出:[object Object]
// 查找过程:obj -> Object.prototype -> null

6. 原型链的注意事项

(1) 原型污染

  • 修改原型会影响所有实例。
    Array.prototype.push = function() {
      console.log('原型被修改了!');
    };
    const arr = [1, 2, 3];
    arr.push(4); // 输出:原型被修改了!
    

(2) 性能问题

  • 原型链过长会影响查找性能。

(3) 原型链的终点

  • 所有对象的原型链最终指向 Object.prototype,而 Object.prototype 的原型是 null

7. 原型链的现代替代方案

(1) ES6 类(Class)

  • 语法糖,本质仍基于原型链。
  • 提供更清晰的继承语法。
    class Animal {
      constructor(name) {
        this.name = name;
      }
      eat() {
        console.log(`${this.name} 正在吃东西`);
      }
    }
    
    class Dog extends Animal {
      constructor(name, breed) {
        super(name);
        this.breed = breed;
      }
    }
    
    const dog = new Dog('小黑', '拉布拉多');
    dog.eat(); // 输出:小黑 正在吃东西
    

(2) Object.create()

  • 直接创建对象并指定原型。
    const animal = {
      eat() {
        console.log(`${this.name} 正在吃东西`);
      }
    };
    
    const dog = Object.create(animal);
    dog.name = '小黑';
    dog.eat(); // 输出:小黑 正在吃东西
    

总结

概念描述核心作用
原型对象的[[Prototype]] 属性共享属性和方法
原型链通过[[Prototype]] 链式查找实现继承和属性查找
构造函数创建对象实例的函数定义实例属性和原型方法
ES6 类基于原型链的语法糖提供清晰的继承语法

理解原型和原型链是掌握 JavaScript 继承机制的基础,同时现代语法(如 class)提供了更简洁的实现方式。

更多vue相关插件及后台管理模板可访问vue admin reference,代码详情请访问github