NodeJS中的JavaScript OOP:如何?


117

我已经习惯了Java中的经典OOP。

使用NodeJS在JavaScript中进行OOP的最佳实践是什么?

每个类都是一个带有module.export?的文件。

如何建立课程?

this.Class = function() {
    //constructor?
    var privateField = ""
    this.publicField = ""
    var privateMethod = function() {}
    this.publicMethod = function() {} 
}

与(我什至不确定它是正确的)

this.Class = {
    privateField: ""
    , privateMethod: function() {}

    , return {
        publicField: ""
        publicMethod: function() {}
    }
}

this.Class = function() {}

this.Class.prototype.method = function(){}

...

继承将如何工作?

是否有用于在NodeJS中实现OOP的特定模块?

我正在发现一千种创建类似于OOP的东西的方法。但是我不知道什么是最常用/最实用/最简洁的方法。

额外的问题:与MongooseJS一起使用时,建议的“ OOP样式”是什么?(能否将MongooseJS文档视为类,而将模型用作实例?)

编辑

这是JsFiddle中的示例,请提供反馈。

//http://javascriptissexy.com/oop-in-javascript-what-you-need-to-know/
function inheritPrototype(childObject, parentObject) {
    var copyOfParent = Object.create(parentObject.prototype)
    copyOfParent.constructor = childObject
    childObject.prototype = copyOfParent
}

//example
function Canvas (id) {
    this.id = id
    this.shapes = {} //instead of array?
    console.log("Canvas constructor called "+id)
}
Canvas.prototype = {
    constructor: Canvas
    , getId: function() {
        return this.id
    }
    , getShape: function(shapeId) {
        return this.shapes[shapeId]
    }
    , getShapes: function() {
        return this.shapes
    }
    , addShape: function (shape)  {
        this.shapes[shape.getId()] = shape
    }
    , removeShape: function (shapeId)  {
        var shape = this.shapes[shapeId]
        if (shape)
            delete this.shapes[shapeId]
        return shape
    }
}

function Shape(id) {
    this.id = id
    this.size = { width: 0, height: 0 }
    console.log("Shape constructor called "+id)
}
Shape.prototype = {
    constructor: Shape
    , getId: function() {
        return this.id
    }
    , getSize: function() {
        return this.size
    }
    , setSize: function (size)  {
        this.size = size
    }
}

//inheritance
function Square(id, otherSuff) {
    Shape.call(this, id) //same as Shape.prototype.constructor.apply( this, arguments ); ?
    this.stuff = otherSuff
    console.log("Square constructor called "+id)
}
inheritPrototype(Square, Shape)
Square.prototype.getSize = function() { //override
    return this.size.width
}

function ComplexShape(id) {
    Shape.call(this, id)
    this.frame = null
    console.log("ComplexShape constructor called "+id)
}
inheritPrototype(ComplexShape, Shape)
ComplexShape.prototype.getFrame = function() {
    return this.frame
}
ComplexShape.prototype.setFrame = function(frame) {
    this.frame = frame
}

function Frame(id) {
    this.id = id
    this.length = 0
}
Frame.prototype = {
    constructor: Frame
    , getId: function() {
        return this.id
    }
    , getLength: function() {
        return this.length
    }
    , setLength: function (length)  {
        this.length = length
    }
}

/////run
var aCanvas = new Canvas("c1")
var anotherCanvas = new Canvas("c2")
console.log("aCanvas: "+ aCanvas.getId())

var aSquare = new Square("s1", {})
aSquare.setSize({ width: 100, height: 100})
console.log("square overridden size: "+aSquare.getSize())

var aComplexShape = new ComplexShape("supercomplex")
var aFrame = new Frame("f1")
aComplexShape.setFrame(aFrame)
console.log(aComplexShape.getFrame())

aCanvas.addShape(aSquare)
aCanvas.addShape(aComplexShape)
console.log("Shapes in aCanvas: "+Object.keys(aCanvas.getShapes()).length)

anotherCanvas.addShape(aCanvas.removeShape("supercomplex"))
console.log("Shapes in aCanvas: "+Object.keys(aCanvas.getShapes()).length)
console.log("Shapes in anotherCanvas: "+Object.keys(anotherCanvas.getShapes()).length)

console.log(aSquare instanceof Shape)
console.log(aComplexShape instanceof Shape)

12
在node.js中,OO JS 并没有真正的特定之处。只有OO JS。您的问题是关于 Java OOP技术转换为JS的,这是不对的。我认为您最好花相同的时间/精力来学习JS的基于原型的模型如何工作以及如何利用它来发挥自己的优势
Elias Van Ootegem 2013年

1
另外,您没有JavaScript类。您可以使用函数创建类似类的行为,但这通常不是一个好主意。
m_vdbeek 2013年

1
@AwakeZoldiek这不是“本机功能”是什么意思?
Esailija

4
@fusio通常,通过原型继承,对象/实例将从其他对象/实例继承。因此,不使用类,因为您没有使用抽象定义。因此,继承是通过prototype完成的。而且,不,对象不支持“ 私有 ”成员。尽管Node.js中的模块/脚本被实现为闭包,但是只有闭包可以提供。
Jonathan Lonowski

1
@Esailija我并不是要暗示关闭可以创建私有成员。只是建议闭包和封闭变量尽可能接近 JavaScript。但是,另一方面,我提到的唯一的“ 实现 ”提到了Node模块,这些模块是在一个闭包中进行评估的,其中某些全局变量是每个脚本唯一定义的。
Jonathan Lonowski

Answers:


116

这是一个开箱即用的示例。如果您想减少“ hacky”,则应使用继承库或类似的库。

在文件animal.js中,您应该编写:

var method = Animal.prototype;

function Animal(age) {
    this._age = age;
}

method.getAge = function() {
    return this._age;
};

module.exports = Animal;

要在其他文件中使用它:

var Animal = require("./animal.js");

var john = new Animal(3);

如果要“子类”,请在mouse.js中:

var _super = require("./animal.js").prototype,
    method = Mouse.prototype = Object.create( _super );

method.constructor = Mouse;

function Mouse() {
    _super.constructor.apply( this, arguments );
}
//Pointless override to show super calls
//note that for performance (e.g. inlining the below is impossible)
//you should do
//method.$getAge = _super.getAge;
//and then use this.$getAge() instead of super()
method.getAge = function() {
    return _super.getAge.call(this);
};

module.exports = Mouse;

您也可以考虑“方法借用”而不是垂直继承。您无需从“类”继承即可在其类上使用其方法。例如:

 var method = List.prototype;
 function List() {

 }

 method.add = Array.prototype.push;

 ...

 var a = new List();
 a.add(3);
 console.log(a[0]) //3;

使用Animal.prototype.getAge= function(){}和仅添加this.getAge = function(){}内部有function Animal() {}什么区别?子类似乎有点不客气..使用“继承”库,您表示的是inherits@badsyntax建议的内容吗?
fusio

4
@fusio是的,您可以执行类似的操作inherits(Mouse, Animal);来清理继承设置。不同之处在于,您正在为每个实例化对象创建新的函数标识,而不是共享一个函数。如果您有10个鼠标,那么您已经创建了10个功能身份(这仅仅是因为鼠标只有一种方法,如果有10个方法,那么10个鼠标将创建100个功能身份,您的服务器会很快将其大部分CPU浪费在GC:P上) ,即使您不会将它们用于任何用途。该语言目前没有足够的表达能力来优化此功能。
Esailija

哇。谢谢:)这似乎很简单,我还找到了Details_of_the_Object_Model,他们在其中将JS与Java进行了比较。不过,要继承它们,它们只是这样做:Mouse.prototype = new Animal()..它与您的示例相比如何?(例如什么是Object.create()?)
fusio

@fusio Object.create不会调用构造函数...如果构造函数具有副作用(此类函数可以执行正常功能可以执行的任何操作,与Java不同,则该函数可以执行任何其他操作),那么在设置继承链时调用它是不可取的。
Esailija 2013年

3
对于仍然开始使用JavaScript的人,我仍然坚持认为,破解这样的解决方案并不是一个好的解决方案。有很多不容易调试的怪癖和陷阱,因此不建议这样做。
m_vdbeek 2013年

42

由于Node.js社区确保将JavaScript ECMA-262规范的新功能及时提供给Node.js开发人员。

您可以看一下JavaScript类MDN链接到JS类 在ECMAScript 6 JavaScript类中,此方法提供了在Javascript中对OOP概念建模的更简便方法。

注意:JS类只能在严格模式下工作

下面是用Node.js编写的类继承的一些骨架(二手Node.js版本v5.0.0

类声明:

'use strict'; 
class Animal{

 constructor(name){
    this.name = name ;
 }

 print(){
    console.log('Name is :'+ this.name);
 }
}

var a1 = new Animal('Dog');

继承:

'use strict';
class Base{

 constructor(){
 }
 // methods definitions go here
}

class Child extends Base{
 // methods definitions go here
 print(){ 
 }
}

var childObj = new Child();

14

我建议使用inherits标准util模块随附的帮助程序:http : //nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor

在链接页面上有一个如何使用它的示例。


对于核心NodeJS环境,这是最有用的答案。
Philzen 2015年

1
现在看起来已弃用。从答案链接:注:不鼓励使用util.inherits()。请使用ES6类并扩展关键字以获得语言级别继承支持。还要注意,这两种样式在语义上是不兼容的。
Frosty Z

10

这是互联网上有关面向对象JavaScript的最佳视频:

面向对象的JavaScript权威指南

从头到尾观看!!

基本上,Javascript是一种基于原型的语言,与Java,C ++,C#和其他流行朋友中的类完全不同。视频解释的核心概念远胜于此处的任何答案。

在ES6(2015年发布)中,我们有了一个“类”关键字,该关键字使我们可以像使用Java,C ++,C#,Swift等一样使用Javascript“类”。

视频中的屏幕快照,显示了如何编写和实例化Javascript类/子类: 在此处输入图片说明


感谢您提供了ES6的答案。谢谢!不幸的是,我没有数据可以观看27分钟的视频。我将继续寻找书面指导。
tim.rohrer

感谢您的视频。我帮助我清除了有关JavaScript的许多问题。
Kishore Devaraj

4

在Javascript社区中,许多人认为不应该使用OOP,因为原型模型不允许在本地进行严格而健壮的OOP。但是,我认为OOP不是语言问题,而是体系结构问题。

如果要在Javascript / Node中使用真正强大的OOP,可以查看完整的开源框架Danf。它提供了强大的OOP代码所需的所有功能(类,接口,继承,依赖项注入等)。它还允许您在服务器(节点)和客户端(浏览器)端使用相同的类。此外,由于Npm,您可以编写自己的danf模块并与任何人共享。


-1

如果您自己工作,并且想要与OOP最接近的东西(如Java,C#或C ++中的东西),请参阅javascript库CrxOop。CrxOop提供Java开发人员有点熟悉的语法。

请注意,Java的OOP与Javascript中的OOP不同。要获得与Java中相同的行为,请使用CrxOop的类,而不是CrxOop的结构,并确保所有方法都是虚拟的。语法的一个例子是

crx_registerClass("ExampleClass", 
{ 
    "VERBOSE": 1, 

    "public var publicVar": 5, 
    "private var privateVar": 7, 

    "public virtual function publicVirtualFunction": function(x) 
    { 
        this.publicVar1 = x;
        console.log("publicVirtualFunction"); 
    }, 

    "private virtual function privatePureVirtualFunction": 0, 

    "protected virtual final function protectedVirtualFinalFunction": function() 
    { 
        console.log("protectedVirtualFinalFunction"); 
    }
}); 

crx_registerClass("ExampleSubClass", 
{ 
    VERBOSE: 1, 
    EXTENDS: "ExampleClass", 

    "public var publicVar": 2, 

    "private virtual function privatePureVirtualFunction": function(x) 
    { 
        this.PARENT.CONSTRUCT(pA);
        console.log("ExampleSubClass::privatePureVirtualFunction"); 
    } 
}); 

var gExampleSubClass = crx_new("ExampleSubClass", 4);

console.log(gExampleSubClass.publicVar);
console.log(gExampleSubClass.CAST("ExampleClass").publicVar);

该代码是纯javascript,没有转码。该示例摘自官方文档中的许多示例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.