从继承的角度理解 JS 的原型链
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 ,转载请注明出处