【问题标题】:How to "subclass" a node.js module如何“子类化” node.js 模块
【发布时间】:2026-02-21 01:35:01
【问题描述】:

多年来,我主要是一名 Perl 编码员,但也有 C++ 背景,所以我来自“经典”OO 背景,现在正在学习 node.js。我刚刚通读了The Principles of Object-Oriented JavaScript,它很好地向像我这样具有古典思想的人解释了 JS 的 OO 概念。但是我还有一个问题,特别是与 node.js 和继承有关。如果我仍在使用“经典”词汇来解释我的问题,请原谅我。

假设我有一个模块 lib/foo.js:

function foo() {
    console.log('Foo was called');
}

module.exports.foo = foo;

我想在另一个模块 lib/bar.js 中“继承”它:

var foo = require('foo.js');

// Do some magic here with *.prototype, maybe?

function bar() {
    console.log('Bar was called');
}

module.exports.bar = bar;

这样我的主脚本可以做到这一点:

var bar = require('lib/bar.js');

bar.foo(); // Output "Foo was called"
bar.bar(); // Output "Bar was called"

这甚至可能吗?如果是这样,我错过了什么?

或者这是一种反模式?还是根本不可能?我应该怎么做?

【问题讨论】:

    标签: node.js inheritance node-modules


    【解决方案1】:

    我是这样做的,覆盖request module 中的一种方法。 警告:许多节点模块的扩展设计不佳,包括请求,因为它们在构造函数中做了太多的事情。不仅仅是一个庞大的参数选项,而是启动 IO、连接等。例如,请求将 http 连接(最终)作为构造函数的一部分。没有明确的.call().goDoIt() 方法。

    在我的示例中,我想使用查询字符串而不是 qs 来格式化表单。我的模块被巧妙地命名为“MyRequest”。在名为 myrequest.js 的单独文件中,您有:

    var Request = require('request/request.js');
    var querystring = require('querystring');
    
    MyRequest.prototype = Object.create(Request.prototype);
    MyRequest.prototype.constructor = MyRequest;
    
    // jury rig the constructor to do "just enough".  Don't parse all the gazillion options
    // In my case, all I wanted to patch was for a POST request 
        function MyRequest(options, callbackfn) {
            "use strict";
            if (callbackfn)
               options.callback = callbackfn;
            options.method = options.method || 'POST';          // used currently only for posts
            Request.prototype.constructor.call(this, options);  
               // ^^^  this will trigger everything, including the actual http request (icky)
               // so at this point you can't change anything more
        }
    
        // override form method to use querystring to do the stringify
        MyRequest.prototype.form = function (form) {
         "use strict";
            if (form) {
                this.setHeader('content-type', 'application/x-www-form-urlencoded; charset=utf-8');
                this.body = querystring.stringify(form).toString('utf8');
      // note that this.body and this.setHeader are fields/methods inherited from Request, not added in MyRequest.
                return this;
            }
    
            else
               return Request.prototype.form.apply(this, arguments);
        };
    

    然后,在您的应用程序中,而不是

    var Request = require("request");
    Request(url, function(err, resp, body)
      {
          // do something here
       });
    

    你去

    var MyRequest = require("lib/myrequest");
    MyRequest(url, function(err, resp, body)
      {
          // do that same something here
       });
    

    我不是 JavaScript 专家,所以可能有更好的方法...

    【讨论】:

    • 这是否包含在 node.js 模块中?又名lib/myrequest.js 之类的?或者您粘贴的代码是否存在于您的主应用程序中?
    • 我认为这是在正确的轨道上,并提供了一些很好的线索,但对我来说并不完全有效。
    • 我猜“request”模块返回的是对象构造函数,而不是一个实际的对象,这就是为什么你的代码适合你,但不适合我。我不得不使用var foo = require('foo.js'); Bar.prototype = Object.create( foo.__proto__ ),但经过反复试验后,我得到了与您大致相同的结果。谢谢!
    • @Flimzy 正确。我经常希望 JS/node 文档能更清楚地说明什么是类、什么是构造函数/工厂以及什么是对象的实例。
    【解决方案2】:

    作为参考,我对示例代码问题提出的具体解决方案如下:

    lib/foo.js:

    var Foo = function() {}
    
    Foo.prototype.foo = function() {
        console.log('Foo was called!');
    };
    
    module.exports = new Foo;
    

    lib/bar.js:

    var foo = require('./foo.js');
    
    var Bar = function() {}
    
    Bar.prototype = Object.create(foo.__proto__);
    Bar.prototype.constructor = Foo;
    
    Bar.prototype.bar = function() {
        console.log('Bar was called!');
    };
    
    module.exports = new Bar;
    

    然后在我的测试脚本中:

    var bar = require('lib/bar.js');
    
    bar.foo(); // Output "Foo was called"
    bar.bar(); // Output "Bar was called"
    

    【讨论】: