一、原型(Prototype)

1. 概念

在 JavaScript 中,几乎所有对象都有一个 内部属性 [[Prototype]](在大部分浏览器中可以通过 __proto__ 访问),它指向另一个对象,这个对象就是 原型

原型的作用主要有两个:

  1. 属性继承:对象可以访问原型上的属性和方法。
  2. 方法共享:多个对象可以共享同一个方法,而不必在每个对象中重复定义。

2. 构造函数与原型

在 JavaScript 中,构造函数创建对象时,会自动把对象的原型指向构造函数的 prototype

function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};
const alice = new Person('Alice');
alice.sayHello(); // Hello, I'm Alice
// 查看原型
console.log(alice.__proto__ === Person.prototype); // true

✅ 这里有几个关键点:

  • Person.prototype 是原型对象。
  • alice.__proto__ 指向 Person.prototype
  • 原型上定义的方法 sayHello 被实例 alice 共享,而不是每个实例都复制一份。

3. 内置对象的原型

JavaScript 中的数组、函数等内置对象也有原型:

const arr = [1, 2, 3];
console.log(arr.__proto__ === Array.prototype); // true
console.log(arr.__proto__.__proto__ === Object.prototype); // true

这里我们就看到 原型链 的雏形。


二、原型链(Prototype Chain)

1. 概念

原型链指的是对象通过 __proto__ 一层层向上查找属性的链式结构,直到找到 nullObject.prototype.__proto__ === null)。

当访问对象的属性或方法时,JS 引擎的查找顺序是:

  1. 先在对象自身属性中查找。
  2. 如果找不到,则去对象的原型中查找。
  3. 如果原型还有原型,则继续向上查找。
  4. 直到原型为 null,仍找不到则返回 undefined

2. 示例

function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

const bob = new Person('Bob');
bob.sayHello(); // Hello, I'm Bob
// 原型链结构
console.log(bob.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

访问 bob.sayHello() 时:

  1. bob 对象自身没有 sayHello 属性。
  2. JS 去 bob.__proto__(即 Person.prototype)查找,找到了方法。
  3. 执行方法。

如果你访问 bob.toString()

  1. bob 自身没有 toString
  2. Person.prototype 也没有。
  3. JS 去 Person.prototype.__proto__(即 Object.prototype)找,找到了 toString 方法。

✅ 这就是原型链的查找过程。


3. 注意事项

  1. 原型链是动态的:
Person.prototype.sayAge = function() { console.log('Age unknown'); };
bob.sayAge(); // Age unknown
  • bob 已经存在,但仍可以访问新加的原型方法。
  1. 避免原型污染
    • 不要在原型上直接放可变的引用类型(如数组、对象),会被所有实例共享。
function Person() {}
Person.prototype.hobbies = [];
const p1 = new Person();
const p2 = new Person();
p1.hobbies.push('reading');
console.log(p2.hobbies); // ['reading'],被共享了

三、原型与类的关系

ES6 的 class 本质上是语法糖,底层仍然用原型实现:

class Person {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const cindy = new Person('Cindy');
console.log(cindy.__proto__ === Person.prototype); // true

所以理解原型链对理解 ES6 类的继承也非常重要。


四、总结

概念 描述
原型 (prototype) 对象的隐式父对象,用于属性继承和方法共享。
原型链 (prototype chain) 对象通过原型层层向上查找属性的链式结构,最终以 null 结束。
构造函数 & 原型 通过 new 创建对象,实例的 __proto__ 指向构造函数的 prototype
ES6 类 底层通过原型实现继承,语法更清晰。

好的,我们来系统讲解 JavaScript 的继承,从基础概念到实现方式,再结合原型链讲解。


五、继承的概念

继承指的是一个对象或类 获取另一个对象或类的属性和方法 的能力。
在 JavaScript 中,继承主要通过 原型链 实现,也可以通过 ES6 class对象组合实现。


六、原型链继承(最基础的继承方式)

1️⃣ 例子

function Person(name) {
  this.name = name;
  this.skills = ['JS', 'HTML'];
}

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

function Student(name, grade) {
  this.grade = grade;
  Person.call(this, name); // 调用父构造函数,继承实例属性
}

// 继承方法(原型链继承)
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function() {
  console.log(`${this.name} is studying in grade ${this.grade}`);
};

const stu1 = new Student('张三', 10);
stu1.sayHello();  // Hello, I'm 张三
stu1.study();     // 张三 is studying in grade 10

2️⃣ 原理

  1. Student.prototype = Object.create(Person.prototype)
    • 创建一个新对象,新对象的原型指向 Person.prototype
    • Student 的实例可以访问 Person 的方法(继承方法)
  2. Person.call(this, name)
    • 调用父构造函数,继承实例属性(每个实例有独立副本)

✅ 这样做就解决了 原型链继承共享引用属性的问题(如数组、对象会被共享)。


七、ES6 class 继承

ES6 提供了更简洁的继承语法:

class Person {
  constructor(name) {
    this.name = name;
    this.skills = ['JS', 'HTML'];
  }

  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);  // 调用父类构造函数
    this.grade = grade;
  }

  study() {
    console.log(`${this.name} is studying in grade ${this.grade}`);
  }
}

const stu2 = new Student('李四', 11);
stu2.sayHello();  // Hello, I'm 李四
stu2.study();     // 李四 is studying in grade 11
  • extends 实现原型链继承
  • super() 调用父类构造函数,继承实例属性

✅ ES6 的继承底层依然是 原型链 + 构造函数调用,只是语法更清晰。


八、对象继承(Object.create)

有时不想用构造函数,可以直接用对象继承:

const person = {
  name: '张三',
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const student = Object.create(person);
student.grade = 10;
student.study = function() {
  console.log(`${this.name} is studying in grade ${this.grade}`);
};

student.sayHello(); // Hello, I'm 张三
student.study();    // 张三 is studying in grade 10

Object.create(person) 创建了一个对象,原型指向 person,实现了 原型继承


九、继承方式总结

方式 实现特点 注意点
原型链继承 子类原型指向父类实例或父类原型,方法继承共享 实例属性共享引用类型,需配合构造函数继承解决
构造函数继承(借用构造函数) 在子构造函数中调用父构造函数,继承实例属性 方法无法继承,需要原型链补充方法
组合继承(最常用) 原型链继承 + 构造函数继承,兼顾方法和属性 经典方案
ES6 class 继承 extends + super(),语法糖封装组合继承 更直观,底层仍是原型链 + 构造函数
对象继承(Object.create) 创建对象,原型指向另一个对象 简单对象继承,适合轻量对象

十、 JavaScript 继承中实例属性、原型方法和原型链层级 的关系。

下面是一个示例,以 Person 和 Student 为例:

层级 对象/原型 属性/方法示例 说明
实例 stu (Student 实例) name, grade 调用 super() 继承父类实例属性,子类自己的属性也在这里
子类原型 Student.prototype study() 子类特有方法定义在这里,实例通过原型链访问
父类原型 Person.prototype sayHello() 父类方法,实例通过原型链继承访问
Object 原型 Object.prototype toString(), hasOwnProperty() 所有对象共享的内置方法
原型链终点 null - 原型链结束,没有父对象

解释

  1. 实例属性
    • 通过 super() 调用父类构造函数继承,或子类自己定义。
    • 每个实例都有自己的独立副本。
  2. 子类原型方法
    • 定义在 Student.prototype 上。
    • 所有 Student 实例共享。
  3. 父类原型方法
    • 定义在 Person.prototype 上。
    • 通过原型链访问,实现方法继承。
  4. Object.prototype 方法
    • 所有对象最终都可以访问。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐