带你区分原型对象与对象原型,不要再傻傻分不清了

610 阅读4分钟

原型对象

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象,我们叫这个对象为原型对象。这个对象包含了该类型所有实例共享的属性和方法。

function Person(name) {
    this.name = name;
}
Person.prototype.sayHello = function () {
    console.log("你好,我是" + this.name)
}
Person.prototype.sayBye = function () {
    console.log('再见!');
}
const zs = new Person('张三');
zs.sayHello();
zs.sayBye();
console.log(Person.prototype)
  • Person.prototype 是构造函数的原型对象
  • 所有通过new Person()创建的实例都会继承这个原型对象

让我们再创建一个构造函数来验证一下,就喊张三的好朋友李四来帮忙吧!

const ls = new Person('李四');
ls.sayHello();
ls.sayBye();
console.log(zs.__proto__ === ls.__proto__); // true 

看吧!通过构造函数创建的实例会继承这个原型对象的

下面让我们再来介绍一下原型对象的constructor属性

image.png

每个原型对象都有一个constructor属性,它的作用是指向该原型对象的构造函数
我们可以简单理解为:指向它的父对象
知道了它的作用,那它有什么用处?别急,让我带你看看下面的这段代码

function Person(name) {
    this.name = name;
}
Person.prototype = {
    
}
console.log(Person.prototype.constructor) //Person

奇怪,constructor属性不是指向父对象Person吗?怎么会指向Object?
这是因为这种写法相当于给原型对象采取对象属性赋值,但这样会覆盖构造函数原型对象原本的内容,所以修改后的constructor属性就不指向当前的构造函数了。这个时候,就需要constructor闪亮登场了!

function Person(name) {
    this.name = name;
}
Person.prototype = {
    constructor: Person
}
console.log(Person.prototype.constructor) //Object

返回控制台一看,constructor属性又重新指向当前构造函数了。
通过对上面代码的分析:我们发现构造函数还可以创建实例对象,构造函数还有有一个原型对象,原型对象里可以放置一些公共的属性和方法,但是为什么实例对象可以访问原型对象里的属性或方法?这就牵扯到我们今天的另一个主题了对象原型

对象原型

每个对象实例都有一个内部属性[[Prototype]](在浏览器中可通过__proto__访问),我们称之为对象原型

还是用上面的那段代码

console.log(Person.prototype)
console.log(zs.__proto__)
console.log(zs.__proto__ === Person.prototype) //true
// 现代JavaScript推荐使用
console.log(Object.getPrototypeOf(zs) === Person.prototype); // true

让我们看看控制台打印的信息

屏幕截图 2025-05-11 232749.png
哇哦!一模一样!学到了学到了。 之所以实例对象可以访问原型对象里的属性和方法,就是因为有原型对象的存在。
简单理解就是:原型对象指向对象原型
关键点

  • __proto__是实例访问原型链的入口
  • Object.getPrototypeOf()是标准访问方式

注意:__proto__对象原型里也有一个constructor,指向创建该实例对象的构造函数
下面让我们来梳理一下这三者之间的关系

image.png

原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,这种原型对象的链状关系就叫做原型链。
直接上图

image.png

关键点

  • 实例通过__proto__访问原型链。
  • 原型链的查找从实例对象自身开始,通过 __proto__ 逐级向上访问,终点是Object.prototype(其__proto__null

从上面的图片中我们可以总结一下原型链的查找规则:

1.当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

2.如果没有就查找它的原型(也就是 proto 指向的 prototype 原型对象)。

3.如果还没有就查找原型对象的原型(Object 的原型对象)。

4.依此类推一直找到 Object 为止(null)。

5.proto 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

6.可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

扩展

ES6的class语法本质仍是基于原型链的语法糖:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  speak() {
    console.log(`${this.name} barks`);
  }
}

const rex = new Dog('Rex');

// 验证关系
console.log(rex.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true

结语

感谢大家的观看,如果对您有帮助的话,请在评论区随便说两句吧,这对作者非常重要。也欢迎大家点赞关注支持,我会在后面继续更新的。