JS基础三座大山之一-原型和原型链

先来看一个现象:为什么arr可以访问到数组的方法?

原型现象

进入正题

1. 构造函数

// 构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 生成实例
const p = new Person('zhangsan', 18);

如上述代码所示,JS通过构造函数来生成实例。但是又出现了一个新的问题,在构造函数中通过this赋值的属性或者方法,是每个实例的实例属性以及实例方法,无法共享公共属性。所以又设计出了一个原型对象,来存储这个构造函数的公共属性以及方法

2. 原型对象 prototype

JS的每个函数在创建的时候,都会生成一个属性prototype,这个属性指向一个对象,这个对象就是此函数的原型对象。该原型对象中有个属性为constructor,指向该函数。这样原型对象和它的函数之间就产生了联系即:Constructor.prototype.constructor === Constructor

原型prototype

现在我们创建一个People类(类也是函数)

class People {
    constructor(name,age){
        this.name = name;
    }
    sayHi(){
        console.log(`Hello,我是父类: ${this.name}` )
    }

    sleep(){
        console.log(`Hello,我是父类:我正在睡觉` )
    }
}

const Chili = new People("Chili","20");

code1

原型对象的作用:

  • 存放一些属性和方法
  • 在js中实现继承

此时,完善一下我们的Preson构造函数:

// 构造函数
function Preson(name, age) {
    this.name = name;
    this.age = age;
}
// 所有实例共享的公共方法
Preson.prototype.say = function (word) {
    console.log(`${this.name}说:${word}`);
}

const p1 = new Preson('张三', 18); // 创建一个Person实例对象
p1.hasOwnProperty('say') // false 说明不是定义在其本身上的 hasOwnPrototype 判断是否是自己的属性
p1.say('hello world'); // 调用公共方法 打印:张三说:hello world

这里就要思考了,为什么我们构造的p1这个实例对象,它可以调用到Person这个构造函数的原型对象上的方法呢?明明只有在构造函数内部通过this来赋值的属性或者方法才会被实例所继承,为什么在构造函数的原型对象上定义的say方法也能通过实例来调用到呢?

在上述现象为什么arr可以访问到数组的方法,是因为arr是Array的实例,实例时可以访问到Array的公共方法,所以arr可以使用Array的方法,那么实例arr为什么就可以使用原型身上的方法呢?这里就引出了__propto__和原型链的概念。

3. __propto__隐式原型

  • __propto__:每个对象都有__propto__属性
  • 作用:这个属性指向他的原型对象prototype属性

每个通过构造函数创建出来的实例对象,其本身有个属性__proto__,这个属性会指向该实例对象的构造函数的原型对象,这么说好像有点绕,我们看下图

原型__proto__

看图也有点绕?简化上图得到下图:

构造函数原型

还是看看上面创建的People类

class People {
    constructor(name,age){
        this.name = name;
    }
    sayHi(){
        console.log(`Hello,我是父类: ${this.name}` )
    }

    sleep(){
        console.log(`Hello,我是父类:我正在睡觉` )
    }
}

const Chili = new People("Chili","20");

code2

4. 原型链

现在我们知道了,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会通过它的__proto__隐式属性,找到它的构造函数的原型对象,如果还没有找到就会再在其构造函数的prototype的__proto__中查找(因为prototype也是个对象,每个对象都有__propto__属性),这样一层一层向上查找就会形成一个链式结构,我们称为原型链。

原型链

注意注意⚠

如果通过p1实例对象的__proto__属性赋值,则会改变其构造函数的原型对象,从而被所有实例所共享。

// 构造函数
    function Preson(name, age) {
      this.name = name;
      this.age = age;
    }
    // 所有实例共享的公共方法
    Preson.prototype.say = function (word) {
      console.log(`${this.name}说:${word}`);
    }

    const p1 = new Preson('张三', 18); // 创建一个Person实例对象
    const p2 = new Preson('李四', 20); // 新创建一个Proson实例对象
    p1.say('hello world'); // 调用公共方法
    p1.hasOwnProperty('say') // false 说明不是定义在其本身上的
    p1.__proto__.do = function () {
      console.log('往原型对象中添加方法');
    }
    p2.do(); // 打印出了-往原型对象中添加方法

这样会污染原本的构造函数,所以,我们在开发的时候,要注意不要通过实例对象去改变其构造函数的原型对象,这样会对其他通过该构造函数生成的实例对象造成影响。

proto 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,然后再来为原型添加方法/属性。(摘自阮一峰的ES6入门)

再在其构造函数的prototype的__proto__中查找是什么意思?

补充知识:原型链的尽头

所有的原型对象的__proto__属性都是指向function Object的原型对象。 而function Object的原型对象在上图中我们可以得知是不存在__proto__这个属性的,它指向了null。我们就得知了原型链的尽头是null

什么? constructor 和 prototype 把你搞晕了?

5. constructor 与 prototype 的关系

  1. constructor 属性

位置:存在于类的原型对象(prototype)上(Person.prototype.constructor
指向:类(构造函数)本身
作用:标识创建该实例的构造函数

// 其实类就是函数 
// typeof Person 返回 'function'
class Person {
  constructor(name) {
    this.name = name;
  }
}

console.log(Person.prototype.constructor === Person); // true
  1. prototype 属性

位置:存在于类(构造函数)上(Person.prototype)
指向:该类的原型对象
作用:存储所有实例共享的方法和属性

  1. 实例的 __proto__ 属性

位置:存在于所有对象上
指向:构造函数的 prototype 对象
作用:提供原型链查找机制
实例的 constructor 属性继承自原型对象
person.__proto__ === Person.prototype

constructor和prototype的关系

6. 总结

原型: 函数都有 prototype属性,称之为原型,也称之为原型对象

  • 原型可以放一些属性和方法,共享给实例对象使用
  • 原型可以做继承

原型链:对象都有__proto__属性这个属性指向他的原型对象,原型对象也是对象,也有__proto__属性,指向原型对象的原型对象.这样一层一层形成的链式结构称之为原型链,最顶层找不到则返回null

原型系统关键点:

  1. prototype 属性:只有函数拥有,指向该函数的原型对象(实例对象是没有的)

  2. __proto__ 属性:所有对象都拥有,指向创建该对象的构造函数的原型

  3. 原型链:当访问对象属性时,JavaScript 会沿着 __proto__ 链向上查找

  4. constructor 属性:原型对象指向其构造函数的引用

原型系统核心概念详解:

  1. 构造函数 (Constructor)
  • 用于创建对象的函数

  • 通常以大写字母开头

  • 包含 prototype 属性(是一个对象),指向原型对象

  • 示例:function Person() {}

  1. 原型对象 (Prototype)
  • 每个函数都有一个原型对象

  • 包含共享的属性和方法

  • 包含 constructor 属性,指回构造函数

  • 示例:Person.prototype

  1. 实例对象 (Instance)
  • 通过构造函数创建的对象

  • 包含 __proto__ 属性,指向构造函数的原型

  • 示例:const person = new Person()

  1. 原型链 (Prototype Chain)
  • 对象属性的查找机制

  • 查找顺序:实例自身 → 原型对象 → 原型对象的原型 → ... → null

  • 原型链终点:Object.prototype.proto === null

  1. __proto__prototype 的关系
  • prototype 是函数特有的属性

  • __proto__ 是所有对象都有的属性

  • 关键关系:instance.__proto__ === Constructor.prototype

  • Constructor.prototype.constructor === Constructor

  1. 原型继承的优势
  • 内存效率:方法在原型上共享,不占用每个实例的内存

  • 动态性:修改原型会影响所有实例

  • 灵活性:可以在运行时修改原型

  • 模拟类:在ES6之前实现面向对象编程

posted @ 2025-08-15 15:08  cyy618  阅读(9)  评论(0)    收藏  举报