【问题标题】:Node JS: Accessing inherited function in subclass's functionNode JS:在子类的函数中访问继承的函数
【发布时间】:2014-04-11 16:25:22
【问题描述】:

假设我有两个班级,BaseChild。 Base 是 Child 将继承的基类。这是可视化的代码:

Base.js

function Base(init) {

}

function log() {
  console.log('here');
}
Base.prototype.log = log;

module.exports = Base;

Child.js

var Base = require('../Base.js');

var Child = module.exports = function Child(init) {
  Base.call(this, init);
};

require('util').inherits(Child, Base);

function test() {
  this.log();  // doesn't work
  Base.prototype.log();  // Works but is quite ugly
  Child.super_.prototype.log();  // Works but is even uglier
}

Child.prototype.test = test;

我绝对想做的是像this.log() 甚至log() 这样的东西会很好。我意识到我可以在我的继承类中设置一个变量,但是我必须为每个继承 Base 的类都这样做,这绝对不理想。所以我的问题是,我可以在不必在继承类中设置变量的情况下做类似this.log() 的事情吗?我是不是误会了什么?

【问题讨论】:

  • 不要两个都失败,因为this 在调用log 期间有错误的值?
  • 是的,this.log() 只是失败并导致服务器崩溃,但我将其放入以显示我正在尝试和想要的内容。

标签: javascript node.js inheritance compoundjs


【解决方案1】:

更新答案

从您下面的评论中,回复我的声明 this.log() 应该可以工作:

嗯,就是这样。当我在 Child 的测试函数中时,this 是一个空对象,所以我假设我没有得到正确的范围。

您还没有显示您是如何调用test 的,但我怀疑这就是问题所在。如果您通过 Child 实例调用它:

var c = new Child();
c.test();

...然后在调用中,this 将是子实例,它将(间接)继承 Parent.prototype 对象及其 log 属性。

但是如何称呼它很重要。这行不通,例如:

var c = new Child();
var f = c.test;
f();

如果你这样做,在对函数的调用中,this 将是全局对象(如果你处于严格模式,undefined),而不是Child 实例。这是因为在 JavaScript 中,this 主要由函数的调用方式设置,而这样调用它并不会将 this 设置为您想要的。

这对于回调很重要,因为将c.test 作为回调传递:

someFunctionThatUsesACallback(c.test);

...表示回调代码不会为您设置this

如果您需要这样做,Function#bind 将提供帮助:

var f = c.test.bind(c); // Returns a version of c.test that, when called,
                        // will have `this` set to `c`
f();                    // Works, `this` is the `Child` instance

同样:

someFunctionThatUsesACallback(c.test.bind(c));

更多(在我的博客上):


原答案

如果您正确设置原型层次结构,并且Child.prototype 上没有log(并且您没有在实例上放置log 属性),那么您应该能够使用@987654348 @ 正好。如果不能,则层次结构设置不正确。

我不知道util.inherits 是做什么的,但是正确设置ChildParent 之间的关系并不复杂:

function Parent() {
}
Parent.prototype.log = function() {
    console.log("log called");
};

function Child () {
    Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // This line is largely optional, but a good idea

// Usage
var c = new Child();
c.log(); // "log called"

但是如果你在你的Child.prototype 中覆盖log 或者为实例分配一个log 属性,并且你想使用Parentlog 版本,那么你当然不能只使用this.log(),因为该属性不再引用 Parent.prototype.log

当你需要调用某事物的父版本时(我称它们为“超级调用”,我认为这不是原始版本),你必须做更多的工作:

我通常通过将父构造函数传递给我用来构建子构造的函数来设置这样的层次结构,例如:

var Child = (function(Super) {
    var pp = Super.prototype;

    function Child() {
    }
    Child.prototype = Object.create(pp);

    Child.prototype.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };

    return Child;
})(Parent);

通过始终使用该模式,我避免了在 Child 代码中编写 Parent(我使用 Super arg 代替),所以如果我需要变基 Child,我只需更改我传递的内容进入函数。

因为这相当难看(例如,在 Child 的顶部不清楚它是从 Parent 派生的,因为 Parent 在底部)并且涉及样板代码,我觉得没有必要编写再一次,我为它写了一个简单的帮助脚本,我称之为Lineage,它看起来像这样:

var Child = Lineage.define(Parent, function(p, pp) {
    p.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };
});

请注意,LineageChildParent 原型作为参数传递,使其使用起来更加简洁(而且由于您可以选择这些参数名称,因此您可以使用任何适合您的术语——我使用p作为正在创建的“类”的原型[上面的Child],使用pp作为父原型等)。

【讨论】:

  • 所以这告诉我的是我的实际问题是不可能的......在每个类的功能中,我必须以某种奇怪的方式访问日志功能。因此,我认为我最初将 var 分配为 log 的方法是最简单的方法。不要误会我的意思,这是一个很好的答案,但这不是我真正想要的。
  • @incutonez:同样,除非您覆盖log,否则使用正确的层次结构(这很容易做到)this.log() 应该可以工作。如果您在Child 级别覆盖log 并且您想使用Parent 版本,您只需要执行其他操作。如果这是您需要做的,并且您将log 分配给一个变量,那么要使用正确的this 值调用它,您必须这样做yourVariable.call(this); 所以这与pp.log.call(this); FWIW,ES6 基本相同将使 lots 变得更容易,但即使您使用 Harmony 标志运行 Node,它还没有它。
  • 嗯,就是这样...我正在尝试做前者(而不是覆盖 Child 的登录)。当我在 Child 的测试函数中时,this 是一个空对象,所以我假设我没有得到正确的范围。不幸的是,其中大部分对我来说是隐藏的,因为我使用 CompoundJS 作为我的 MVC 框架。我还假设在 ES5 中如何设置继承,它并没有像我期望的那样工作。至于Harmony,是的,我正在耐心等待它出来……我听说上课会容易得多。
  • @incutonez:啊,我想我知道发生了什么。问题不在于层次结构设置不正确,而是您调用test 的方式。我已经用详细信息更新了答案。
  • 我将不得不深入研究复合代码,因为框架是真正负责创建类和调用其函数的框架......我只设置了类的外观,但我相信您选择 bind 是正确的。
【解决方案2】:

来自 node.js 的标准 inherits 函数(在我看来)是非常糟糕的代码。相反,我更喜欢使用augment 来创建类:

// base.js

var augment = require("augment");

var Base = module.exports = augment(Object, function () {
    this.constructor = function (init) {

    };

    this.log = function () {
        console.log("here");
    };
});

// child.js

var augment = require("augment");
var Base = require("./base");

var Child = module.exports = augment(Base, function (base) {
    this.constructor = function (init) {
        base.constructor.call(this, init);
    };

    this.test = function () {
        this.log();
    };
});

另外augment.js只有20行代码,可以在任何地方使用。

【讨论】:

  • 这实际上是一种非常有趣的方法,而且更符合我正在寻找的方法。我可能会尝试使其适应我的解决方案。谢谢。
【解决方案3】:

作为仅供参考,我最终将其放入 Node 为您创建的 global 变量中。我意识到这是一种不好的做法,但它是一种日志机制,任何类、控制器等都需要使用它,所以我不认为这是一个糟糕的解决方案。

但是,在 Compound 中,您可以创建 no_eval 控制器,这意味着它们看起来像典型的原型函数......所以您基本上可以创建一个 mixin,或者我可以需要我的 mixin 并像一个类一样使用它......就像这样:

var ControllerMixin = require(process.cwd() + 'app/mixins/ControllerMixin.js');
var Log;

var LoggerController = module.exports = function LoggerController(init) {
  ControllerMixin.call(this, init);  // mixin approach
  Log = require(process.cwd() + 'app/utils/LoggerMixin.js')(init);  // class approach
};

LoggerController.prototype.index = function index(controller) {
  controller.logMessage('blah');  // using mixin
  Log.logError('hi');  // using class
  global.logWarning('yep');  // global approach
  return controller.send({success: true});
};

所以有很多选择……只需要找到你认为最好的方法。

【讨论】:

    【解决方案4】:

    我在网上看到的每个答案要么看起来很复杂,要么依赖于外部库。假设您使用与传统 OOP 非常相似的自定义类型设计模式,为什么不简单地归结为基础。

    parent.js

    //the main parent class
    // class Parent
    
    
    module.exports = Parent;
    
    function Parent(a) {
        if (!(this instanceof Parent)) {
            return new Parent(a);
        }
        this.a = a; //Some parent variable
    }
    
    Parent.prototype = {
    
        // Instantiate child:
        getChild: function() {
            var Child = require('./child');
            return new Child(this);
        },
    
        // Some parent function, print some text:
        printText: function(text) {
            console.log(text);
        }
    };
    

    child.js

    //Similar to class Child extends Parent
    
    
    module.exports = Child;
    
    function Child(parent) {
    
        this.parent = parent;
    }
    
    Child.prototype = {
    
        // Prints Parent Variable:
        printParentVariable: function() {
            console.log(this.parent.a);
        },
    
        // Calls Parent Function:
        callParentFunction: function() {
            this.parent.printText('Child calling parent function.');
        }
    };
    

    test.js

    var parent = require('./parent')('parent text'); //instantiate parent with some text
    
    var child = parent.getChild();       //create instance of a child
    
    //*** Child has full access to its parents variables and methods ***//
    
    console.log(child.parent.a);         //Print the parent text "parent text"
    child.printParentVariable();         //Child method which prints the parent variable "parent text", identical to line above.
    child.parent.printText('Child calling parent');      //Call parent method, to print provided text
    child.callParentFunction();          //Calls parent method, identical to above.
    

    【讨论】:

      猜你喜欢
      • 2019-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      • 2017-01-07
      相关资源
      最近更新 更多