Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/38.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在派生类而不是类本身上添加新方法的好处是什么?_Javascript_Node.js_Winston - Fatal编程技术网

Javascript 在派生类而不是类本身上添加新方法的好处是什么?

Javascript 在派生类而不是类本身上添加新方法的好处是什么?,javascript,node.js,winston,Javascript,Node.js,Winston,出于个人学习目的,我试图了解Winston的包装设计结构及其每个模块背后的目的,但我无法理解这一点 在Winstons软件包中,模块中有一个核心Logger类,它实现了记录器的主要功能,并提供了一些公共方法,如Logger.log方法。它还实现了内部使用的转换流方法 然后在模块中有一个名为DerivedLogger的派生类,它扩展了Logger类,似乎它的唯一目的是向Logger原型添加优化级别的方法。然后在模块底部的工厂函数中实例化并导出该DerivedLogger类 我的问题是,为什么需要D

出于个人学习目的,我试图了解Winston的包装设计结构及其每个模块背后的目的,但我无法理解这一点

在Winstons软件包中,模块中有一个核心
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();