【问题标题】:JavaScript OOP in NodeJS: how?NodeJS 中的 JavaScript OOP:如何?
【发布时间】:2013-08-13 19:53:51
【问题描述】:

我习惯了 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)

【问题讨论】:

  • node.js 中没有关于 OO JS 的真正具体。只有OO JS。您的问题是关于 translating Java OOP 技术到 JS,这只是 不正确。我认为你最好花同样的时间/精力来学习 JS 的基于原型的模型是如何工作的,以及如何利用它来发挥你的优势
  • 另外,JavaScript 中没有类。您可以使用函数创建类似类的行为,但这通常不是一个好主意。
  • @AwakeZoldiek 你说它不是“原生功能”是什么意思?
  • @fusio 通常使用原型继承,对象/实例继承自其他对象/实例。因此,不使用类是因为您没有使用抽象定义。因此,继承是通过prototype chain 完成的。而且,不,对象不支持“private”成员。只有 closures 可以提供,尽管 Node.js 中的模块/脚本是作为闭包实现的。
  • @Esailija 我实际上并不是要建议闭包可以创建私有成员。只是建议闭包和封闭变量在 JavaScript 中是as close as you can get。但是,对于另一部分:我提到的唯一“实现”考虑了 Node 模块,这些模块在闭包中进行评估,其中一些 globals 被定义为每个脚本唯一的。

标签: javascript node.js oop inheritance mongoose


【解决方案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,没有转译。该示例取自官方文档中的多个示例。

【讨论】:

    【解决方案2】:

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

    The Definitive Guide to Object-Oriented JavaScript

    从头看到尾!!

    基本上,Javascript 是一种Prototype-based 语言,它与 Java、C++、C# 和其他流行朋友中的类完全不同。 该视频比这里的任何答案都更好地解释了核心概念。

    在 ES6(2015 年发布)中,我们有一个“class”关键字,它允许我们像使用 Java、C++、C#、Swift 等一样使用 Javascript“类”。

    视频中的屏幕截图展示了如何编写和实例化 Javascript 类/子类:

    【讨论】:

    • 感谢您为 ES6 提供了答案。谢谢!不幸的是,我没有观看 27 分钟视频的数据。我将继续寻找书面指导。
    • 感谢您的视频。我帮我解决了很多关于 javascript 的问题。
    【解决方案3】:

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

    您可以查看 JavaScript 类MDN link to JS classes 在 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();
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        这是一个开箱即用的示例。如果你想要更少的“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(){} 和在function Animal() {} 中简单地添加this.getAge = function(){} 有什么区别?子类似乎有点hacky ..“继承”库的意思是@badsyntax 建议的inherits 之类的东西?
        • @fusio 是的,您可以执行类似inherits(Mouse, Animal); 之类的操作来清理继承设置。不同之处在于您正在为每个实例化对象创建新的函数标识,而不是共享一个函数。如果你有 10 个鼠标,你就创建了 10 个函数标识(那只是因为鼠标有一个方法,如果它有 10 个方法,10 个鼠标会创建 100 个函数标识,你的服务器会很快将大部分 CPU 浪费在 GC 上:P) ,即使您不会将它们用于任何事情。该语言目前没有足够的表达能力来优化它。
        • 哇。谢谢 :) 这看起来很简单,我还找到了Details_of_the_Object_Model,他们将 JS 与 Java 进行了比较。尽管如此,继承他们只是这样做:Mouse.prototype = new Animal().. 与您的示例相比如何? (例如,Object.create() 是什么?)
        • @fusio Object.create 不调用构造函数...如果构造函数有副作用或类似的(它可以做任何普通函数可以做的事情,不像Java),那么调用它是不可取的设置继承链时。
        • 我仍然认为对于开始使用 JavaScript 的人来说,破解这样的解决方案并不是一个好的解决方案。有很多怪癖和陷阱不容易调试,因此不建议这样做。
        【解决方案6】:

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

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

        【讨论】:

        • 这是关于核心 NodeJS 环境的最有用的答案。
        • 现在看起来已弃用。来自答案链接:注意:不鼓励使用 util.inherits()。请使用 ES6 类和扩展关键字来获得语言级别的继承支持。另请注意,这两种样式在语义上是不兼容的。
        猜你喜欢
        • 1970-01-01
        • 2012-03-13
        • 1970-01-01
        • 1970-01-01
        • 2014-06-24
        • 2012-01-22
        • 1970-01-01
        • 2016-06-25
        • 2019-01-31
        相关资源
        最近更新 更多