从继承的角度理解 JS 的原型链

2024-11-15
JavaScript
66

JS 原型链想必前端或多或少了解一些,今天我们从类和继承的角度推理一下,JS 原型链设计原理

类和继承

在将原型链之前,需要先理解编程中的类和继承是什么。

类是通过 Class 关键词定义的数据类型,和 Function 关键词定义函数一样。类的内部可以定义方法。

这里定义了一个类 Shap 和 内部方法 A

class Shap {
  A() {
    console.log('i am shape')
  }
}

通过 new Class,可以创建 一个 类的实例对象。类似js 的 new Function

如果一个新的类 Class Circle 想使用 Class Shap 的方法,这里可以用到继承。继承就是,我的类可以自动用你的方法。

class Circle extend Shap {
   B() {
    console.log('i am circle')    
   }                       
}

通过 new Circle 创建的对象自动会有 A 方法

JS 实现类和继承

在 java 中,类直接通过class就能定义出来。

但是 JavaScript 设计之初没有 class 关键词,也想实现类和继承的能力,来看看他是如何设计的。

利用 Function 关键词代替 Class 关键词定义类

// js 写法
function Shape () {}

// java
class Shape {}

哪些方法可以被继承

现在类有了,那哪些方法能被继承呢

可以给 Function 增加特征属性,告诉解释器,哪些方法可以被继承。这里可以有两种方案

// 方案一,属性增加前缀
Function._extendA = ...
Function._extendB = ...

// 方案二,在一个属性里统一处理
Function._extend = {}
Function._extend.A = ...
Function._extend.B = ...

js 作者选择了第二种方案,增加一个 **propotype** 属性来表示这些方法可以被继承

function Shape () {}

Shape.prototype.A = function () {
  console.log('i am shape')
}

原型链

实例对象怎么知道继承的谁

new Function 生成的实例对象,目前只有一些内置的属性,还不还不知道自己是谁创造出来的。

想要找到自己的parent,一般通用方案是设置一个属性,指向parent

curObj._parent = Shape

像上面代码,通过 _parent 属性,可以找到刚才的Shap方法。再调用 .prototype.A 就能调用继承的方法了。

curObj._parent.prototype.A // i am shape

然后,如果 _parent 指向Shape,那每次调用继承的方法时候就得写 .prototype

那就直接将这个属性绑定到 Function 的 prototype 属性上,省一步调用

curObj._parentProto = Shape.prototype

curObj._parentProto.A  // i am shape

最后,将 _parentProto 换成 __porto__ 就是js的方案

原型链怎么来的

在第二步中,prototype指向的是一个对象,对象肯定会有 __proto\__ 属性,这样就形成了一个链式调用

原型链

至此,我们已经推理出了 js 原型链的本质

总结

JS 原型链通过属性链式引用的方式解决了继承问题,但是这种方案增加了学习成本。还不如直接在设计之初增加 Class 关键词更加方便

原文请查看 https://webfem.com/post/js-proto ,转载请注明出处