面向对象编程--JS原型和原型链

原型(Prototype)与原型链(Prototype Chain)—— JavaScript 的核心机制​

JavaScript 是一门基于原型的语言,​​原型和原型链​​是它实现继承和属性查找的核心机制。理解它们,才能真正掌握 JavaScript 的面向对象编程。

1. 原型(Prototype)是什么?

​(1) 每个 JavaScript 对象都有一个原型(__proto__ 或 [[Prototype]])​

  • 对象通过 ​​原型链​​ 继承属性和方法。
  • 原型本身也是一个对象,因此它也有自己的原型,形成链条。

​(2) 如何查看对象的原型?​

  • obj.__proto__​(非标准,但广泛支持)
  • Object.getPrototypeOf(obj)​(标准方法)
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

(3) 函数的原型(prototype 属性)​

  • ​函数​​(如构造函数)有一个特殊的 prototype 属性,用于实现基于原型的继承。
  • new 操作符​​ 会将该函数的 prototype 赋值给新对象的 __proto__
function Person() {}
console.log(Person.prototype); // { constructor: Person }
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true

​2. 原型链(Prototype Chain)是什么?​

​(1) 原型链是 JavaScript 的继承机制​

  • 当访问一个对象的属性时,如果它自身没有该属性,JavaScript 会沿着 ​​原型链​​ 向上查找。
  • 查找顺序:对象自身 → 对象的 protoproto.proto→ ... →Object.prototypenull ​。

​(2) 原型链示例​

function Animal(name) {
  this.name = name;
}
Animal.prototype.eat = function() {
  console.log(`${this.name} is eating.`);
};

function Dog(name, breed) {
  Animal.call(this, name); // 调用父类构造函数
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype); // 继承 Animal 的原型
Dog.prototype.constructor = Dog; // 修复 constructor 指向
Dog.prototype.bark = function() {
  console.log(`${this.name} is barking.`);
};

const dog = new Dog("Buddy", "Golden Retriever");
dog.eat(); // 继承自 Animal
dog.bark(); // Dog 自身的方法

原型链关系​​:

dog (Dog 实例)
  ├── __proto__ → Dog.prototype
        ├── __proto__ → Animal.prototype
              ├── __proto__ → Object.prototype
                    ├── __proto__ → null

(3) Object.prototype 是原型链的顶端​

  • 所有对象最终都继承自 Object.prototype
  • Object.prototype.__proto__ 是 null,表示原型链的终点。
console.log(Object.prototype.__proto__); // null

3. 原型 vs 原型链​

概念说明
​原型(Prototype)​每个对象都有一个原型(__proto__),函数还有一个 prototype 属性。
​原型链(Prototype Chain)​对象通过 __proto__ 形成的链条,用于属性查找。

4. 原型链的应用​

​(1) 继承(ES5 的继承方式)​

function Parent() {
  this.parentProp = "Parent";
}
Parent.prototype.parentMethod = function() {
  console.log("Parent method");
};

function Child() {
  Parent.call(this); // 调用父类构造函数
  this.childProp = "Child";
}
Child.prototype = Object.create(Parent.prototype); // 继承父类原型
Child.prototype.constructor = Child; // 修复 constructor

const child = new Child();
child.parentMethod(); // 继承自 Parent

​(2) 方法共享​

  • 所有实例共享原型上的方法,节省内存。
function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  console.log(`Hello, ${this.name}!`);
};

const p1 = new Person("Alice");
const p2 = new Person("Bob");
p1.sayHello === p2.sayHello; // true(共享同一个方法)

​(3) 属性查找机制​

  • 当访问 obj.prop 时:
    1. 先在 obj 自身查找。
    2. 如果没有,沿着 __proto__ 向上查找。
    3. 直到 Object.prototype 或 null
const obj = {};
console.log(obj.toString()); // 来自 Object.prototype.toString

5. ES6 的 class 和原型链​

ES6 的 class 本质上是语法糖,底层仍然是基于原型的继承。

class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log(`${this.name} is eating.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  bark() {
    console.log(`${this.name} is barking.`);
  }
}

const dog = new Dog("Buddy", "Golden Retriever");
dog.eat(); // 继承自 Animal
dog.bark(); // Dog 自身的方法

​底层原型链​​:

dog (Dog 实例)
  ├── __proto__ → Dog.prototype
        ├── __proto__ → Animal.prototype
              ├── __proto__ → Object.prototype
                    ├── __proto__ → null

​6. 常见问题​

​(1) 为什么 obj.toString() 能调用,但 obj 没有 toString 方法?​

  • 因为 toString 定义在 Object.prototype 上,obj 通过原型链找到它。

​(2) 如何避免原型污染?​

  • 不要直接修改 Object.prototype,否则会影响所有对象。
  • 使用 Object.create(null) 创建无原型的对象(用于纯字典)。
const pureDict = Object.create(null);
pureDict.key = "value";
console.log(pureDict.toString); // undefined(不会继承 Object.prototype 的方法)

​(3) __proto__ 和 prototype 的区别?​

属性说明
obj.__proto__对象的原型(指向其构造函数的 prototype)。
func.prototype函数的原型(用于 new 创建实例时的原型)。

7. 总结​

  • ​原型​​:每个对象都有一个原型(__proto__),函数还有一个 prototype 属性。
  • ​原型链​​:对象通过 __proto__ 形成的链条,用于属性查找。
  • ​继承​​:ES5 通过 Parent.prototype 和 Object.create 实现,ES6 通过 class 和 extends 实现。
  • ​方法共享​​:原型上的方法被所有实例共享,节省内存。
  • ​属性查找​​:先在自身查找,再沿原型链向上查找。

​一句话​​:
JavaScript 的继承和属性查找机制完全依赖原型和原型链,理解它们是掌握 JS 面向对象编程的关键!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值