JavaScript: 原型与原型链

55 阅读3分钟

介绍

JavaScript是一种面向对象的原型语言,这意味着对象可以直接继承其他对象。这与Java等经典语言形成对比,在Java中,子类继承自超类,对象是类的实例。

什么是原型?

  • 每个 JavaScript 对象都有一个特殊的隐藏属性 [[Prototype]],用于指向它的原型对象。
  • prototype 是函数对象的一个属性,用来定义由这个构造函数创建的对象的原型

什么是原型链?

  • 原型链是多个对象通过 [[Prototype]] 链接形成的结构。
  • 当访问一个对象的属性时,JavaScript 引擎会先在对象自身查找,如果没有找到,就沿着原型链向上查找,直到找到 null(原型链的终点)

原型的作用

  • 实现继承
    • 子对象可以通过原型链共享父对象的属性和方法。
    • 减少内存消耗,所有实例共享一个方法。
  • 动态扩展对象功能
    • 可以在原型上添加方法,所有继承该原型的对象自动拥有这个方法。

不理解的话,可以看看下面这个比喻

  • 假设你去图书馆(对象),需要查一本书(属性)。
  • 你先在自己包里(对象自身)找,没找到就去图书馆的一层(原型)找,再没找到就去二层(更高层原型),直到顶层(Object.prototype)。
  • 如果顶层都没有,那只能返回“没有”(undefined)。

进一步理解

__proto__和prototype关系

  • __proto__和constructor是对象独有的
  • prototype属性是函数独有的

在 js 中我们是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性值,这个属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。

当我们使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。

当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是我们新建的对象为什么能够使用 toString() 等方法的原因。

  • 原型(prototype): 一个简单的对象,用于实现对象的 属性继承。可以简单的理解成对象的爹。
  • 构造函数: 可以通过new来 新建一个对象 的函数。
  • 实例: 通过构造函数和new创建出来的对象,便是实例。 实例通过__proto__指向原型,通过constructor指向构造函数。

原型、实例与构造函数三者的关系

● 实例.proto === 原型

● 原型.constructor === 构造函数

● 构造函数.prototype === 原型

function Constructor() {}
const obj = new Constructor();

console.log(obj.__proto__ === Constructor.prototype); // true
// 原型对象(prototype)上有一个默认的 constructor 属性,指向它所属的构造函数。
console.log(Constructor.prototype.constructor === Constructor); // true
console.log(Constructor.prototype === obj.__proto__); // true

例子

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

Animal.prototype.eat = function () {
	console.log("Eating...");
};

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

// Dog 的原型是 Animal 的实例
Dog.prototype = new Animal();

Dog.prototype.bark = function () {
	console.log("Woof!");
};

const dog = new Dog("Buddy");
console.log(dog.type); // 输出:Animal (从 Animal 的原型链继承)
dog.eat(); // 输出:Eating... (继承自 Animal.prototype)
dog.bark(); // 输出:Woof!

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