Javascript 节点JS:访问子类';s函数

Javascript 节点JS:访问子类';s函数,javascript,node.js,inheritance,compoundjs,Javascript,Node.js,Inheritance,Compoundjs,假设我有两个类,Base和Child。Base是子对象将从中继承的基类。下面是将此可视化的代码: Base.js function Base(init) { } function log() { console.log('here'); } Base.prototype.log = log; module.exports = Base; var Base = require('../Base.js'); var Child = module.exports = function C

假设我有两个类,
Base
Child
。Base是子对象将从中继承的基类。下面是将此可视化的代码:

Base.js

function Base(init) {

}

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

module.exports = Base;
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;
//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);
    }
};
//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.');
    }
};
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.
Child.js

function Base(init) {

}

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

module.exports = Base;
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;
//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);
    }
};
//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.');
    }
};
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.

我非常想做的是像
this.log()
这样的事情,甚至
log()
也不错。我意识到我可以在继承的类中设置一个变量,但是对于继承
Base
的每个类,我都必须这样做,这绝对不是理想的。所以我的问题是,我可以做类似于
this.log()
的事情,而不必在继承的类中设置变量吗?我有什么误解吗?

更新答案:

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

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

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

var c = new Child();
c.test();
var c = new Child();
var f = c.test;
f();
…然后在调用中,
this
将是子实例,它将(间接)继承
Parent.prototype
对象及其
log
属性

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

var c = new Child();
c.test();
var c = new Child();
var f = c.test;
f();
如果这样做,则在函数调用中,
将是全局对象(或
未定义
如果处于严格模式),而不是
子实例。这是因为在JavaScript中,
This
主要由函数的调用方式来设置,这样调用函数并不会将
This
设置为您想要的

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

someFunctionThatUsesACallback(c.test);
…表示代码回拨不会为您设置

如果需要这样做,
函数#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
做了什么,但是正确设置
子项
父项
之间的关系并不复杂:

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
属性分配给实例,并且希望使用
Parent
版本的
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);
通过始终使用该模式,我避免了在
子代码中写入
父代码
(我使用
Super
arg),因此如果需要重新设置
子代码
,我只需更改传递到函数中的内容

因为这相当难看(例如,在
Child
的顶部不清楚它来自
Parent
,因为
Parent
位于底部)并且涉及样板代码,我觉得不需要每次都重新编写,所以我为它编写了一个简单的助手脚本,我称之为:

var Child = Lineage.define(Parent, function(p, pp) {
    p.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };
});
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});
};
请注意,
relege
Child
Parent
原型作为参数传递,使它们的使用更加简洁(因为您可以选择这些参数名称,所以可以使用任何适合您的术语-我使用
p
作为正在创建的“类”的原型[
Child
,以及父对象原型的
pp
,等等。)

node.js中的标准函数(我个人认为)是非常糟糕的代码。相反,我更喜欢使用它来创建类:

// 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();
    };
});

此外,它只有20行代码,可以在任何地方使用。

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

但是,在复合中,您可以创建no_eval控制器,这意味着它们看起来像典型的原型函数…因此您可以基本上创建一个mixin,或者我可以要求我的mixin并将其用作类…如下所示:

var Child = Lineage.define(Parent, function(p, pp) {
    p.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };
});
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});
};

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

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

parent.js

function Base(init) {

}

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

module.exports = Base;
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;
//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);
    }
};
//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.');
    }
};
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.
child.js

function Base(init) {

}

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

module.exports = Base;
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;
//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);
    }
};
//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.');
    }
};
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.
test.js

function Base(init) {

}

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

module.exports = Base;
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;
//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);
    }
};
//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.');
    }
};
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.

不要两者都失败,因为调用
log
时,
this
的值错误?是,
this.log()
只是失败并使服务器崩溃,但我把它放进去是为了显示我在尝试和想要什么。所以这告诉我,我的实际问题是不可能……在每个类的函数中,我都必须以某种奇怪的方式访问日志函数。因此,我认为我最初将变量指定为日志的方法是最简单的方法。Don别误会,这是一个很好的答案,b