2014-04-11 85 views
0

假設我有兩個類BaseChild。 Base是Child將繼承的基類。下面是這個可視化代碼:節點JS:在子類的函數中訪問繼承函數

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()而不必在繼承類中設置一個變量?我誤解了什麼嗎?

+0

不是那些做都失敗,因爲'this'在調用'log時有錯誤的值'? –

+0

是的,'this.log()'只是失敗並且崩潰了服務器,但我把它放在了我想要的東西上。 – incutonez

回答

0

就像一個參考,我最終把它放在Node爲您創建的global變量中。我意識到這是不好的做法,但它是一個需要被任何類,控制器等使用的日誌記錄機制,所以我不認爲這是一個可怕的解決方案。

然而,複合,您可以創建no_eval控制器,這意味着它們看起來像典型的原型功能...所以你基本上可以創建一個混入,或者我可以要求我的混入和使用它像一個類...是這樣的:

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

所以有選擇...只需要找到你認爲是最好的方法。

1

更新答

從下面的評論,回覆我說statment應this.log()工作:

嘛,所以這就是事情。當我在兒童的測試功能,this是一個空的對象,所以我假設在某個地方我沒有得到適當的範圍。

你沒有顯示你是如何打電話test,但我懷疑這是問題所在。只要你通過Child實例調用它:

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

...然後內通話,this將子實例,這將繼承(間接)與其log屬性Parent.prototype對象。

但你怎麼稱呼它是重要的。這是行不通的,例如:

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" 

但是,如果你重寫你的Child.prototypelog或分配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); 

通過經常使用這種模式,我避免了不得不寫ParentChild代碼中(我用的是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); 
    }; 
}); 

注意Lineage通行證在ChildParent原型既作爲參數,使之簡潔使用它們(因爲你得到挑選這些argumetn名稱,您可以使用任何適用於您的術語  —我使用p作爲創建的「類」的原型[Child在上面],和pp爲父母的原型等)。

+0

所以這告訴我的是我的實際問題是不可能的......在每個班級的功能中,我必須以某種奇怪的方式訪問日誌功能。因此,我認爲將var指定爲日誌的最初方法是最簡單的路線。不要誤解我的意思,這是一個很好的答案,但這不是我真正想要的。 – incutonez

+0

@incutonez:再一次,除非你重寫'log',然後用正確的層次結構(這很容易做到)'this.log()'應該可以工作。如果您在'Child'級重寫'log'並且想要使用'Parent'版本,則只需執行其他操作。如果這就是你需要做的,並且把'log'分配給一個變量,然後用正確的'this'值來調用它,那麼你必須這樣做'yourVariable.call(this);'所以這基本上和'pp.log.call(this);'FWIW,ES6會使* lot *變得更容易,但即使你用和諧標誌運行Node,它也沒有。 –

+0

那麼,這就是事情......我正在嘗試做前者(不是壓倒性的登錄兒童)。當我在Child的測試函數中,'this'是一個空對象,所以我假設在某個地方我沒有得到適當的範圍。不幸的是,大部分對我來說是隱藏的,因爲我使用CompoundJS作爲我的MVC框架。我還假設,在ES5中如何設置繼承,它不會像我期待的那樣工作。關於和諧,是的,我耐心地等待它出來......我聽說班級會更容易。 – incutonez

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行代碼,並可以隨處使用。

+0

這實際上是一個非常有趣的方法,更多的是我正在尋找的內容。我可能會嘗試將其適應於我的解決方案。謝謝。 – incutonez

+0

誰向我致敬 - 謹慎解釋爲什麼? –

-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.