【原生JS教程】第 13 课:原型与继承机制

第 13 课:原型与继承机制

📌 引言

JavaScript 是一门基于原型的语言,与其他面向对象语言(如 Java、C++)的类继承机制不同。理解 JavaScript 的原型和继承机制是掌握这门语言的关键。原型系统使得对象可以共享属性和方法,同时实现代码复用和继承。

通过本节课的学习,你将掌握:

  • JavaScript 原型的概念和工作机制
  • 原型链的构成和查找机制
  • 构造函数与原型的关系
  • ES5 中的继承实现方式
  • ES6 class 语法和 extends 关键字
  • 原型继承在实际开发中的应用

📚 本节内容概要

✅ 1. 原型基础概念

1.1 什么是原型

在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]](可通过 __proto__ 访问),指向它的原型对象。原型对象也可以有自己的原型,这样就形成了一条原型链。

const obj = {};
console.log(obj.__proto__); // Object.prototype
console.log(obj.__proto__ === Object.prototype); // true
1.2 prototype 属性

只有函数才有 prototype 属性,它指向一个对象,这个对象会成为该函数创建的实例的原型。

function Person() {}

console.log(typeof Person.prototype); // "object"
console.log(Person.prototype.constructor === Person); // true
1.3 __proto__prototype 的区别
function Animal() {}

const dog = new Animal();

console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.__proto__ === Function.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true

✅ 2. 原型链机制

2.1 原型链的概念

当访问一个对象的属性时,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达原型链的顶端(null)。

function Parent() {
  this.parentProp = "父级属性";
}

Parent.prototype.getParentProp = function() {
  return this.parentProp;
};

function Child() {
  this.childProp = "子级属性";
}

// 建立原型链
Child.prototype = new Parent();
Child.prototype.constructor = Child;

Child.prototype.getChildProp = function() {
  return this.childProp;
};

const child = new Child();
console.log(child.getParentProp()); // "父级属性"
console.log(child.getChildProp()); // "子级属性"
2.2 原型链查找过程
const obj = {
  a: 1
};

console.log(obj.a); // 1 (自身属性)
console.log(obj.toString); // function toString() (来自 Object.prototype)
// 查找顺序: obj -> obj.__proto__(Object.prototype) -> obj.__proto__.__proto__(null)

✅ 3. 构造函数与原型

3.1 构造函数创建对象
function Person(name, age) {
  this.name = name;
  this.age = age;
}

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

const person1 = new Person("Alice", 25);
const person2 = new Person("Bob", 30);

console.log(person1.sayHello()); // "Hello, I'm Alice"
console.log(person2.sayHello()); // "Hello, I'm Bob"
console.log(person1.sayHello === person2.sayHello); // true (共享方法)
3.2 instanceof 操作符
console.log(person1 instanceof Person); // true
console.log(Person instanceof Function); // true
console.log(Person instanceof Object); // true
console.log(Function instanceof Object); // true

✅ 4. ES5 继承实现

4.1 原型链继承
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(`${this.name} makes a sound`);
};

function Dog(name, breed) {
  this.name = name;
  this.breed = breed;
}

// 继承 Animal
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(`${this.name} barks`);
};

const dog = new Dog("旺财", "金毛");
dog.speak(); // "旺财 makes a sound"
dog.bark(); // "旺财 barks"
4.2 构造函数继承(借用构造函数)
function Animal(name) {
  this.name = name;
  this.colors = ["red", "blue"];
}

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

const dog1 = new Dog("旺财");
const dog2 = new Dog("小黑");

dog1.colors.push("green");
console.log(dog1.colors); // ["red", "blue", "green"]
console.log(dog2.colors); // ["red", "blue"] (不共享引用类型属性)
4.3 组合继承
function Animal(name) {
  this.name = name;
  this.colors = ["red", "blue"];
}

Animal.prototype.speak = function() {
  console.log(`${this.name} makes a sound`);
};

function Dog(name, breed) {
  // 继承属性
  Animal.call(this, name);
  this.breed = breed;
}

// 继承方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(`${this.name} barks`);
};

const dog1 = new Dog("旺财", "金毛");
const dog2 = new Dog("小黑", "哈士奇");

dog1.colors.push("green");
console.log(dog1.colors); // ["red", "blue", "green"]
console.log(dog2.colors); // ["red", "blue"]

✅ 5. ES6 Class 与 extends

5.1 基本 class 语法
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  sayHello() {
    return `Hello, I'm ${this.name}`;
  }
  
  // 静态方法
  static getSpecies() {
    return "Homo sapiens";
  }
}

const person = new Person("Alice", 25);
console.log(person.sayHello()); // "Hello, I'm Alice"
console.log(Person.getSpecies()); // "Homo sapiens"
5.2 extends 实现继承
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  
  speak() {
    super.speak(); // 调用父类方法
    console.log(`${this.name} barks`);
  }
  
  getInfo() {
    return `${this.name} is a ${this.breed}`;
  }
}

const dog = new Dog("旺财", "金毛");
dog.speak(); 
// "旺财 makes a sound"
// "旺财 barks"

console.log(dog.getInfo()); // "旺财 is a 金毛"
5.3 getter 和 setter
class Rectangle {
  constructor(width, height) {
    this._width = width;
    this._height = height;
  }
  
  get area() {
    return this._width * this._height;
  }
  
  set width(value) {
    if (value <= 0) {
      throw new Error("宽度必须大于0");
    }
    this._width = value;
  }
  
  get width() {
    return this._width;
  }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 50
rect.width = 20;
console.log(rect.area); // 100

🧠 本节重难点分析

🔑 重点内容

知识点说明
原型概念理解 [[Prototype]]__proto__prototype 的关系
原型链掌握属性查找机制
构造函数理解构造函数如何创建实例
ES6 Class掌握 class 语法和 extends 关键字
继承实现理解不同继承方式的优缺点

🧩 难点内容

知识点说明
原型链查找机制理解属性如何沿着原型链查找
组合继承理解借用构造函数和原型链继承的结合
super 关键字理解在 constructor 和方法中使用 super 的区别
静态方法与实例方法理解两者的区别和使用场景
继承中的 this 指向理解继承中 this 的绑定机制

📌 第 13 课 10 大高频面试题

1. 什么是原型链?请举例说明。

答案要点

function Parent() {}
function Child() {}

Child.prototype = new Parent();
const child = new Child();
// child.__proto__ -> Child.prototype -> Parent.prototype -> Object.prototype -> null

2. __proto__prototype 有什么区别?

答案要点

  • prototype 是函数的属性,指向原型对象
  • __proto__ 是对象的属性,指向其原型

3. 如何实现继承?

答案要点

// ES5 组合继承
function Parent(name) { this.name = name; }
Parent.prototype.sayName = function() { return this.name; };

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

// ES6 Class
class Parent { constructor(name) { this.name = name; } }
class Child extends Parent {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
}

4. 为什么需要调用 super()

答案要点

在子类构造函数中必须先调用 super() 才能使用 this,因为需要先初始化父类的部分。

5. 下面代码输出什么?

function Foo() {}
const f = new Foo();
console.log(f.__proto__ === Foo.prototype);
console.log(Foo.__proto__ === Function.prototype);

答案要点

都输出 true。

6. 如何判断对象是否具有某个属性?

答案要点

obj.hasOwnProperty('prop'); // 只检查自身属性
'prop' in obj; // 检查自身和原型链上的属性

7. 什么是静态方法?

答案要点

属于类本身而不是实例的方法,只能通过类名调用。

class MyClass {
  static myStaticMethod() { return "static"; }
}
MyClass.myStaticMethod(); // "static"

8. ES5 和 ES6 继承的区别?

答案要点

  • ES5 通过原型链实现继承
  • ES6 使用 class 和 extends 语法糖,更直观

9. getter 和 setter 的作用?

答案要点

提供对属性访问和设置的控制:

class MyClass {
  get prop() { return this._prop; }
  set prop(value) { this._prop = value; }
}

10. instanceof 的工作原理?

答案要点

检查构造函数的 prototype 属性是否出现在实例的原型链上。


🧾 总结

本节课我们深入学习了 JavaScript 的原型系统和继承机制。从原型基础概念到原型链机制,再到 ES5 和 ES6 的继承实现方式,全面掌握了 JavaScript 面向对象编程的核心知识。

通过本节课的学习,你已经具备以下能力:

  • 理解原型和原型链的概念及工作机制
  • 掌握构造函数创建对象的方法
  • 实现 ES5 中的各种继承模式
  • 使用 ES6 class 和 extends 语法
  • 理解静态方法、getter/setter 的使用
  • 解决继承中常见的问题

📅 下一节课安排

第 14 课:ES6+ 新特性

我们将学习 ES6 及后续版本引入的重要新特性,包括 let/const、解构赋值、模板字符串、默认参数、展开运算符、模块化等。这些特性大大提升了 JavaScript 的开发体验和代码质量。

📌 预习建议

  • 了解 ES6 相对于 ES5 的主要改进
  • 尝试使用 let 和 const 声明变量
  • 查阅 MDN 关于解构赋值的文档
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈前端老曹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值