Node JS:在子类的函数中访问继承的函数

时间:2014-04-11 16:25:22

标签: javascript node.js inheritance compoundjs

假设我有两个课程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()这样的事情,而不必在继承的类中设置变量吗?我误解了什么吗?

4 个答案:

答案 0 :(得分: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属性),那么您应该能够使用this.log();就好了。如果不能,则表示层次结构尚未正确设置。

我不知道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"

但是,如果您在log中覆盖Child.prototype或为实例分配log属性,并且您想使用Parent的{​​{1}}版本,当然你不能只使用log,因为该属性不再引用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); 代码中编写Parent(我使用Child arg代替),所以如果我需要重新定义{{1}我只是改变了传递给函数的内容。

因为那是相当丑陋的(例如,Super顶部不清楚它来自Child,因为Child位于底部)并涉及样板代码我不知道我觉得每次都需要再写一次,我为它编写了一个简单的帮助脚本,我打电话给Lineage,这看起来像这样:

Parent

请注意Parent作为参数传递var Child = Lineage.define(Parent, function(p, pp) { p.doSomething = function() { // Call `log` with appropriate `this` pp.log.call(this); }; }); Lineage原型,使其简洁易用(并且因为您可以选择这些名称,您可以使用任何术语适合您 - 我使用Child作为正在创建的“类”的原型[上面的{Parent],以及父类原型的p等。) p>

答案 1 :(得分:0)

来自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行代码,可以在任何地方使用。

答案 2 :(得分:0)

就像一个FYI,我最终把它放在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});
};

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

答案 3 :(得分:-1)

我在网上看到的每个答案看起来都很复杂,或者依赖于外部库。假设您使用自定义类型设计模式(与传统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.