0

假设我有两个班级,Base并且Child. 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

4 回答 4

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,但正确设置Child和之间的关系Parent并不复杂:

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的版本log,那么您当然不能只使用this.log(),因为该属性不再引用Parent.prototype.log

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

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

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编写(我使用Superarg 代替),所以如果我需要 rebase 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);
    };
});

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

于 2014-04-11T16:30:06.327 回答
0

inheritsnode.js的标准函数(在我看来)是非常糟糕的代码。相反,我更喜欢用来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 行代码,可以在任何地方使用。

于 2014-04-11T16:36:21.827 回答
0

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

但是,在 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});
};

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

于 2014-08-28T23:36:47.420 回答
-1

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

父.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.');
    }
};

测试.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.
于 2016-02-07T11:44:20.063 回答