NodeJS'de JavaScript OOP: nasıl?


118

Java'da olduğu gibi klasik OOP'ye alışkınım.

NodeJS kullanarak JavaScript'te OOP yapmak için en iyi uygulamalar nelerdir?

Her Sınıf bir module.export?

Sınıflar nasıl oluşturulur?

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

vs. (doğru olduğundan bile emin değilim)

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

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

vs.

this.Class = function() {}

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

...

Miras nasıl işler?

NodeJS'de OOP uygulamak için belirli modüller var mı?

OOP'ye benzeyen şeyler yaratmanın binlerce farklı yolunu buluyorum .. ama en çok kullanılan / pratik / temiz yolun ne olduğu konusunda hiçbir fikrim yok.

Bonus soru : MongooseJS ile kullanılması önerilen "OOP stili" nedir? (MongooseJS belgesi bir Sınıf ve bir örnek olarak kullanılan bir model olarak görülebilir mi?)

DÜZENLE

İşte JsFiddle'da bir örnek, lütfen geri bildirim sağlayın.

//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'de OO JS hakkında gerçekten spesifik hiçbir şey yoktur. Sadece OO JS var. Sorunuz Java OOP tekniklerini JS'ye çevirmekle ilgili ki bu doğru değil . JS'nin prototip tabanlı modelinin nasıl çalıştığını ve bunu kendi yararınıza nasıl kullanabileceğinizi öğrenmek için aynı zamanı / enerjiyi harcamanızın daha iyi olacağını düşünüyorum
Elias Van Ootegem

1
Ayrıca, JavaScript'te sınıflarınız yoktur. İşlevlerle sınıf benzeri davranışlar oluşturabilirsiniz, ancak bu genellikle iyi bir fikir değildir.
m_vdbeek

1
@AwakeZoldiek "Yerli özellik" olmamasıyla neyi kastediyorsunuz?
Esailija

4
@fusio Genel olarak prototip kalıtımıyla, nesneler / örnekler diğer nesnelerden / örneklerden miras alır. Yani, soyut tanımlarla çalışmadığınız için sınıflar kullanılmaz. Yani miras bir prototypezincir yoluyla yapılır . Ve hayır, nesne " özel " üyeleri desteklemez . Node.js'deki modüller / komut dosyaları kapanışlar olarak uygulanmasına rağmen, bunu yalnızca kapatmalar sunabilir.
Jonathan Lonowski

1
@Esailija Aslında kapanışların özel üyeler yaratabileceğini önermek istemedim. Sadece kapanışların ve kapalı değişkenlerin JavaScript'te alabileceğiniz kadar yakın olduğunu öneriyordum . Ancak, diğer kısım için: Bahsettiğim tek " uygulama ", bazı globallerin her komut dosyası için benzersiz olarak tanımlandığı bir kapanış içinde değerlendirilen Düğüm modüllerini dikkate aldı .
Jonathan Lonowski

Yanıtlar:


116

Bu, kutudan çıkan bir örnek. Daha az "hacky" istiyorsanız, miras kitaplığı veya benzeri bir şey kullanmalısınız.

Bir animal.js dosyasında şunu yazarsınız:

var method = Animal.prototype;

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

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

module.exports = Animal;

Başka bir dosyada kullanmak için:

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

var john = new Animal(3);

Bir "alt sınıf" istiyorsanız, mouse.js'nin içinde:

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;

Ayrıca dikey miras yerine "Yöntem ödünç alma" seçeneğini de düşünebilirsiniz. Yöntemini sınıfınızda kullanmak için bir "sınıftan" miras almanıza gerek yoktur. Örneğin:

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

 }

 method.add = Array.prototype.push;

 ...

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

kullanmak Animal.prototype.getAge= function(){}ve this.getAge = function(){}içine eklemek arasındaki fark function Animal() {}nedir? Alt sınıf biraz hilekar görünüyor .. "kalıtım" kitaplığı inheritsile @badsyntax tarafından önerildiği gibi bir şeyi mi kastediyorsunuz?
fusio

4
@fusio evet, öyle bir şey yapabilirsiniz inherits(Mouse, Animal);ki, kalıtım kurulumunu biraz temizler. Aradaki fark, bir işlevi paylaşmak yerine her örneklenen nesne için yeni işlev kimliği oluşturuyor olmanızdır. 10 fareniz varsa, 10 işlev kimliği oluşturdunuz (bunun nedeni farenin bir yöntemi olması, 10 yöntem olması durumunda 10 fare 100 işlev kimliği oluşturması, sunucunuzun CPU'sunun çoğunu hızla GC: P üzerinde boşa harcamasıdır) , onları hiçbir şey için kullanmayacak olsanız bile. Dil şu anda bunu optimize etmek için yeterli ifade gücüne sahip değil.
Esailija

Woah. Teşekkürler :) Bu oldukça basit görünüyor, ayrıca JS'yi Java ile karşılaştırdıkları Details_of_the_Object_Model'i buldum . Yine de, miras almak için basitçe yaparlar: Mouse.prototype = new Animal().. sizin örneğinizle nasıl karşılaştırılır? (örneğin nedir Object.create()?)
fusio

@fusio Object.create yapıcıyı çağırmaz ... eğer kurucunun yan etkileri varsa (Java'dan farklı olarak normal bir işlevin yapabileceği her şeyi yapabilir), daha sonra miras zincirini kurarken onu çağırmak istenmez.
Esailija

3
Hala JavaScript kullanmaya başlayan biri için bunun gibi bir çözümü hacklemenin iyi bir çözüm olmadığını düşünüyorum. Hata ayıklaması kolay olmayan, tavsiye edilmemesi gereken pek çok tuhaflık ve tuzak var.
m_vdbeek

43

Node.js topluluğu olarak, JavaScript ECMA-262 spesifikasyonundaki yeni özelliklerin Node.js geliştiricilerine zamanında getirilmesini sağlar.

JavaScript sınıflarına göz atabilirsiniz . JS sınıflarına MDN bağlantısı ECMAScript 6 JavaScript sınıfları tanıtıldı, bu yöntem Javascript'te OOP kavramlarını modellemek için daha kolay bir yol sağlar.

Not : JS sınıfları yalnızca katı modda çalışacaktır .

Aşağıda, Node.js'de yazılmış bir sınıf iskeleti, kalıtım bulunmaktadır (Kullanılan Node.js Sürüm v5.0.0 )

Sınıf bildirimleri:

'use strict'; 
class Animal{

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

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

var a1 = new Animal('Dog');

Kalıtım:

'use strict';
class Base{

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

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

var childObj = new Child();

14

inheritsStandart utilmodülle birlikte gelen yardımcıyı kullanmanızı öneririm : http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor

Bağlantılı sayfada nasıl kullanılacağına dair bir örnek var.


Bu, çekirdek NodeJS ortamı ile ilgili en yararlı cevaptır.
Philzen

1
Artık kullanımdan kaldırılmış görünüyor. Yanıt bağlantısından: Not: util.inherits () işlevinin kullanılması önerilmez. Lütfen ES6 sınıfını kullanın ve dil düzeyinde kalıtım desteği almak için anahtar kelimeleri genişletin. Ayrıca iki stilin anlamsal olarak uyumsuz olduğuna dikkat edin.
Frosty Z

11

Bu internetteki Nesne Tabanlı JavaScript hakkında en iyi video:

Nesne Tabanlı JavaScript için Kesin Kılavuz

Baştan sona izleyin !!

Temel olarak Javascript, Java, C ++, C # ve diğer popüler arkadaşlardaki sınıflardan oldukça farklı olan Prototip tabanlı bir dildir. Video, temel kavramları buradaki herhangi bir cevaptan çok daha iyi açıklıyor.

ES6 ile (2015'te yayınlandı), Java, C ++, C #, Swift, vb. İle yaptığımız gibi Javascript "sınıflarını" kullanmamıza izin veren bir "sınıf" anahtar kelimesine sahibiz.

Bir Javascript sınıfının / alt sınıfının nasıl yazılacağını ve başlatılacağını gösteren videodan ekran görüntüsü: görüntü açıklamasını buraya girin


ES6'ya cevap verdiğiniz için teşekkür ederim. Teşekkür ederim! Maalesef 27 dakikalık bir videoyu izleyecek veriye sahip değilim. Yazılı rehberlik arayışıma devam edeceğim.
tim.rohrer

Video için teşekkürler. JavaScript ile ilgili birçok soruyu temizlememe yardımcı oldum.
Kishore Devaraj

4

Javascript topluluğunda pek çok kişi, prototip modelin yerel olarak katı ve sağlam bir OOP yapmaya izin vermediği için OOP'nin kullanılmaması gerektiğini savunuyor. Ancak, OOP'nin bir dil meselesi olduğunu düşünmüyorum, daha çok bir mimari meselesi.

Javascript / Node'da gerçekten güçlü bir OOP kullanmak istiyorsanız, tam yığın açık kaynak çerçevesi Danf'e göz atabilirsiniz . Güçlü bir OOP kodu için gerekli tüm özellikleri sağlar (sınıflar, arayüzler, miras, bağımlılık enjeksiyonu, ...). Aynı sınıfları hem sunucu (düğüm) hem de istemci (tarayıcı) tarafında kullanmanıza da izin verir. Üstelik kendi danf modüllerinizi kodlayabilir ve Npm sayesinde herkesle paylaşabilirsiniz.


-1

Kendi başınıza çalışıyorsanız ve Java veya C # veya C ++ 'da bulabileceğiniz gibi OOP'ye en yakın şeyi istiyorsanız, javascript kitaplığı CrxOop'a bakın. CrxOop, Java geliştiricilerine biraz tanıdık gelen sözdizimi sağlar.

Dikkatli olun, Java'nın OOP'si Javascript'te bulunan ile aynı değildir. Java ile aynı davranışı elde etmek için CrxOop'un yapılarını değil CrxOop sınıflarını kullanın ve tüm yöntemlerinizin sanal olduğundan emin olun. Sözdizimine bir örnek,

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

Kod saf bir javascript, başka bir dosyalama yok. Örnek, resmi belgelerden bir dizi örnekten alınmıştır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.