javascript的原型、原型链

92 阅读5分钟

前言

想要更多理解 javascript 就离不开原型和原型链,javascript 的原型链实际上跟其他语言继承多态那套东西类似,只不过实现细节各有不同罢了,整体核心逻辑应该是很相似的(内部肯定有很多不同的操作,导致实际内容千差万别),ES6 的 class 也是根据原型和原型链出现的

每一个函数都有原型和原型链,因此原型、原型链也是函数的知识点,这下可能也更容易理解函数为什么也是对象了吧😂,因此早期 class 对象 基本上都是 function 函数 编写的,这要是有相关的代码,相信也能一看就明白了

class 我相信大家都了解,毕竟应该都学习过 class(类和对象)、继承、多态,可以参考一下他们,本篇就不多介绍他们了(如果有了解过相关继承链的,应该很容易理解)

下面就介绍一下原型和原型链吧,并且介绍之前先简单介绍一下函数的调用

原型(原型对象)

每一个 function 函数类型的数据都有一个 prototype 类型的属性,这个属性指向一个对象,那就是这个对象的原型对象,原型对象就是 function 函数 的公共对象

原型对象的 constructor 指向该 function 函数

之前看到过一个很好的图,如下所示

image.png

举个原型的代码例子

ps:根据函数创建的对象有个隐藏属性__proto__属性指向原型(不推荐使用,仅仅用来表示关系),上面的都是使用函数调用,这个是根据函数创建的对象不一样哈(理解为类和对象)

ps2:类似于 {} 实际上也是一个语法糖,相当于 new Object() 函数

function Person() {
    this.name = '哈哈'
    this.age = 0
}
//声明一个原型
Person.prototype.country = '中国'
Person.prototype.fullName = function () {
    return this.name + this.age
}

const person = new Person()
console.log(person);
console.log(person.__proto__); //对象有个隐藏属性__proto__属性指向原型
console.log(person.__proto__.constructor); //指向原对象function函数
console.log(person.fullName());

原型链

原型链可以理解为是基于原型的基础上形成的关系链条(继承),也就是可以理解为我们原型的 __proto__ 又指向哪里呢

原型的最终原型函数实际上就是 Object 对象的原型函数,并且该最终原型函数的 __proto__ 为 null (和一些语言逻辑不一样哈)

下面是以前看到的一个图的逻辑

image.png

基础原型链就这么简单,是么?

类本身的原型、一切皆对象,函数原型对应的原型也是对象的原型

前面提到的 function 函数类型 的 原型函数 我们理解了,其对象的 __proto__ 是我们的原型函数,那么类本身(不是他们创建的对象)的 __proto__ 是什么呢,看着他们都像是Function函数的原型函数,可以理解为其他所有类本身都是由 Function 创建出来的对象

如下所示,可以看出,其他类都是由 Function 类创建出来的对象

//Person类本身的构造方法就是Function,剩下的都是
console.log(Person.constructor === Function); // true
console.log(Object.constructor === Function); // true
console.log(Function.constructor === Function); // true

//并且他们(类本身)的原型函数都是 Function 的原型函数
console.log(Person.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true

得出了以下结论:

  • 在js中,所有函数都可以看做是 Function 的实例,也就是的类本身(不是他们创建的对象)是由 Function 创建而来的
  • 而 Person 类本身 和 Object 类本身都是函数,所以它们的构造函数就是 Function
  • Function 本身也是函数,所以 Function 也是自己的实例,function 也是对象
  • 众所周知 javascript 一切皆对象,函数原型对应的原型也是对象的原型

这种情况下,似乎某种情况下形成了一个奇妙的闭环,之前看到了这张图(加入了类的 __proto__function),很符合

image.png

扩展(函数的调用)

了解了原型、原型链,我们简单了解一下函数调用

正常函数的调用相信大家都明白,直接在某个类中 this.函数名() 即可调用该函数,一般理解为对象调用了其里面某个函数

实际上对象的调用和我们直观的调用逻辑是有点不一样的,而是函数的执行使用到了某个对象,就像我们常见的 call 方法 function.call(对象,参数),根据传入的对象,确定我们的函数内更新的参数,实际上后续操作的内容就是传入该对象的内存空间

ps:最外层的函数对应的对象可以理解为是 window 对象

每个创建声明的 function 都会形成一个固定的结构,包括函数、属性(实际也是组合函数),这个东西实际上就是类本身,类拥有 prototype 原型函数,并且原型链的存在也就是继承的东西,里面存在父类、祖父类等

调用一个类的函数,实际上就是通过该对象,定位到其所在类,然后再该类中查找该函数,不存在则去原型函数查找,原型找不动去原型的父类原型查找,一直查询到根类,找不到则会报错,找到了则将该对象传递给找到的函数,让对应函数操作该对象所在的内存空间

最后

ps__proto__ 这个一看就不是正规调用的,实际使用 Object.getPrototypeOf() 用来获取原型函数就行了