Javascript 在派生类而不是类本身上添加新方法的好处是什么?
出于个人学习目的,我试图了解Winston的包装设计结构及其每个模块背后的目的,但我无法理解这一点 在Winstons软件包中,模块中有一个核心Javascript 在派生类而不是类本身上添加新方法的好处是什么?,javascript,node.js,winston,Javascript,Node.js,Winston,出于个人学习目的,我试图了解Winston的包装设计结构及其每个模块背后的目的,但我无法理解这一点 在Winstons软件包中,模块中有一个核心Logger类,它实现了记录器的主要功能,并提供了一些公共方法,如Logger.log方法。它还实现了内部使用的转换流方法 然后在模块中有一个名为DerivedLogger的派生类,它扩展了Logger类,似乎它的唯一目的是向Logger原型添加优化级别的方法。然后在模块底部的工厂函数中实例化并导出该DerivedLogger类 我的问题是,为什么需要D
Logger
类,它实现了记录器的主要功能,并提供了一些公共方法,如Logger.log
方法。它还实现了内部使用的转换流方法
然后在模块中有一个名为DerivedLogger
的派生类,它扩展了Logger类,似乎它的唯一目的是向Logger原型添加优化级别的方法。然后在模块底部的工厂函数中实例化并导出该DerivedLogger
类
我的问题是,为什么需要DerivedLogger
类?如果在记录器
类原型本身上添加这些级别方法,然后让工厂函数直接实例化记录器
类,那么性能会有任何差异吗?我能想到的唯一原因是,DerivedLogger
类的添加可能只是为了模块化目的?有人能帮我理解原因吗
谢谢 这张很有趣,谢谢你指出
简而言之:它与代码结构无关,它是一种性能优化。评论说: 创建一个新的类派生记录器,其级别可以附加到的原型。这是一个众所周知的V8优化,可以提高原型函数的性能 就个人而言,我认为这需要引用(在代码评审中我不会接受)。幸运的是,我认为我找到了作者所说的“优化”: Mathias(一位在V8上工作的谷歌工程师)谈到了通过正确使用
原型来加速JavaScript执行。这篇文章有很多细节,如果你正在学习,它确实值得一读
Winston的优化可以归结为:
可以在元素.prototype
上找到getAttribute()
方法。这意味着每次调用anchor.getAttribute()
,JavaScript引擎都需要
- 检查
getAttribute
是否不在锚对象本身上
- 检查直接原型是否为
htmlanchoreElement.prototype
- 断言那里没有
getAttribute
- 检查下一个原型是否为
HTMLElement.prototype
- 断言那里没有
getAttribute
- 最终检查下一个原型是否为
元素。原型
- 并且
getAttribute
就在那里
总共是7张支票!由于这类代码在web上非常常见,引擎会应用一些技巧来减少原型上的属性加载所需的检查次数
这大致适用于温斯顿,如下所示:
- 类上的方法在所述类的
原型
-对象上定义
- 每次对实例调用方法时,引擎都需要找到被调用方法所附加的
原型
- 在这样做的过程中,它为您正在调用方法的实例类获取原型,并在其
prototype
- 如果它找不到它(例如,因为该方法是继承的),它将沿着
prototype
-链走到下一个类并在那里查找
- 这将一直持续到找到方法(并按子顺序执行)或到达
原型链的末尾(并引发错误)
通过在构造函数中运行\u setupLevels()
,级别方法将直接附加到特定记录器实现实例的原型。这意味着类层次结构可以任意增长:原型链查找只需一步即可找到方法
下面是另一个(简化)示例:
如果在实例化test
后设置断点,我们会看到以下内容:
如您所见,testDirect
-方法直接连接到test
,而testInherit
是多级的
我个人认为这是不好的做法:
- 现在可能是这样,但将来可能不是这样。如果V8在内部对此进行了优化,那么当前的“优化”速度可能会显著降低
- 如果没有分析和源代码,这是一个优化的说法是没有价值的
- 理解起来很复杂(见这个问题)
- 这样做实际上会影响性能。链接文章的结论是:
不要弄乱原型
至于模块化:对于所有扩展都有一个清晰的基类,这是值得一提的
在一种比JavaScript更严格的语言中,这样的类可以提供仅用于扩展的特定方法,这些方法在消费者的公共API中是隐藏的。然而,在这种特殊情况下,Logger
本身就可以了。你的回答让我很高兴问了这个问题,而不是忽略它!谢谢很高兴你能证实这一点;)我喜欢你对原型链遍历的可视化。
class A {
constructor() {
this.setup();
}
testInherit() {
console.log("Inherited method called");
}
setup() {
this["testDirect"] = () => console.log("Directly attached method called");
}
}
class B extends A {
constructor() {
super();
}
}
const test = new B();
test.testInherit();
test.testDirect();