Praca z prototype - zaduma nad obiektowością JS

0

Witam bawię się JS tzn. staram się zrozumieć obiektowość ale daleko z tym do Javy czy C#. Mam pytanie czy to udany program napisany zgodnie z tym z paradygmatem, jak mogę poprawić kod by stosować lepsze praktyki:

// Circle //
    function Circle(radius) {
        this.radius = radius;
    };
    Circle.prototype.calcField = function() {
        return Math.PI * this.radius ** 2;
    }
    Circle.prototype.calcCircuit = function() {
        return 2 * Math.PI * this.radius;
    }
    var figure = new Circle(2);
    console.log(
        "Circle Field: " + figure.calcField() + 
        "\nCircle Circuit: " + figure.calcCircuit()
    );
// Circle //

// Cylinder //
    function Cylinder(radius, height) {
        Circle.call(this, radius);
        this.height = height;
    };
    Cylinder.prototype.calcCapacity = function() {
        return Circle.prototype.calcField.call(this) * this.height;
    }
    Cylinder.prototype.calcSphere = function() {
        return Circle.prototype.calcField.call(this) * 2 
        + (Circle.prototype.calcCircuit.call(this) * this.height);
    }
    var blockCylinder = new Cylinder(2, 10);
    console.log(
        "Cylinder Capacity: " + blockCylinder.calcCapacity() + 
        "\nCylinder Sphere: " + blockCylinder.calcSphere()
    );
// Cylinder //

// Cone //
    function Cone(radius, height) {
        Cylinder.call(this, this);
        this.radius = radius;
        this.height = height;
    }
    Cone.prototype.calcCapacity = function() {
        return Cylinder.prototype.calcCapacity.call(this) / 3;
    }
    Cone.prototype.calcSphere = function() {
        return Circle.prototype.calcField.call(this) + 
        + Math.PI * this.radius 
        * Math.sqrt(this.height ** 2 + this.radius ** 2);
    }
    var blockCone = new Cone(2, 10);
    console.log(
        "Cone Capacity: " + blockCone.calcCapacity() + 
        "\nCone Sphere: " + blockCone.calcSphere()
    );
// Cone //

Będzie wdzięczny za sugestie. Program liczy pole powierzchni i obwód koła, pola powierzchni całkowitej a także objętość stożka i walca.

6

Za wiele się tutaj nie dzieje, w każdym razie jakaś tam obiektowość jest zachowana. Co do samego kodu to taka mała uwaga:

        return Math.PI * this.radius ** 2;

używasz tutaj operatora **, który jest częścią standardu ES7. Skoro korzystasz z tak nowych feature'ów, to zamiast pisać siermiężnie klasy za pomocą funkcji-konstruktorów i prototype, możesz użyć słówka kluczowego class wprowadzonego w ES6. Co prawda efekt da to praktycznie taki sam (class jest tylko lukrem składniowym) to jednak sprawi że kod będzie znacznie czytelniejszy.

2

raczej powinno być area, a nie field.
i pewnie bardziej circumference albo perimeter niż circuit
i raczej volume niż capacity
i pole powierzchni to surface area, a nie sphere.

I co to jest blockCylinder? Dlaczego tak nazwana zmienna?

0
LukeJL napisał(a):

raczej powinno być area, a nie field.
i pewnie bardziej circumference albo perimeter niż circuit
i raczej volume niż capacity
i pole powierzchni to surface area, a nie sphere.

I co to jest blockCylinder? Dlaczego tak nazwana zmienna?

Zgadzam się nazwy zmiennych są bardzo ważne o czym nie każdy pamięta. Na usprawiedliwienie powiem tylko, że nie korzystałem nigdy z tego obszaru słownictwa dziękuje za sugestie widać "Czysty Kod" został przerobiony :)

0
złoty napisał(a):

Za wiele się tutaj nie dzieje, w każdym razie jakaś tam obiektowość jest zachowana. Co do samego kodu to taka mała uwaga:

        return Math.PI * this.radius ** 2;

używasz tutaj operatora **, który jest częścią standardu ES7. Skoro korzystasz z tak nowych feature'ów, to zamiast pisać siermiężnie klasy za pomocą funkcji-konstruktorów i prototype, możesz użyć słówka kluczowego class wprowadzonego w ES6. Co prawda efekt da to praktycznie taki sam (class jest tylko lukrem składniowym) to jednak sprawi że kod będzie znacznie czytelniejszy.

Faktycznie można użyć konstrukcji ze słowem kluczowym class, chociaż zdaje sobie sprawę, że realnie w JS klas nie ma, gdyż nie istnieją typy :)

0

Wykonałem sobie ćwiczenie wprowadzając ES6:

class Circle {
        constructor(radius) {
            this.radius = radius;
        }
 };

class Cylinder extends Circle {
        constructor(radius, height) {
            super(radius);
            this.height = height;
        }
 };

class Cone extends Cylinder {
        constructor(radius, height) {
            super(radius, height);
        }
 };
0

Przerobiłem program na mniej podobny do obiektowego w Javie i C++

// Circle //
    var Circle = {
        init: function(radius) {
            this.radius = radius;
        },
        area: function() {
            return Math.PI * this.radius ** 2; 
        },
        circumference: function() {
            return 2 * Math.PI * this.radius;
        },
        show: function() {
            console.log(
                    "Circle Area: " + this.area() + 
                    "\nCircle Circumference: " + this.circumference()
            );
        }
    }
    var circle = Object.create(Circle);
    circle.init(2);
    circle.show();
// Circle //

// Cylinder //
    var Cylinder = Object.create(Circle);
    Cylinder.initCylinder = function(radius, height) {
        this.init(radius);
        this.height = height;
    }
    Cylinder.calcCylinderCapacity = function() {
        return this.area() * this.height;
    }
    Cylinder.calcCylinderSphere = function() {
        return (this.area() * this.height * 2) 
        + (this.circumference() * this.height);
    }
    Cylinder.showInfoCylinder = function() {
        console.log(
            "Cylinder Capacity: " + this.calcCylinderCapacity() + 
            "\nCylinder Sphere: " + this.calcCylinderSphere()
        );
    }
    var cylinder = Object.create(Cylinder);
    cylinder.initCylinder(2, 10);
    cylinder.showInfoCylinder();
// Cylinder //

// Cone //
    var Cone = Object.create(Cylinder);
    Cone.initCone = function(radius, height) {
        this.initCylinder(radius, height)
        this.height = height;
    }
    Cone.calcConeCapacity = function() {
        return this.calcCylinderCapacity() / 3;
    }
    Cone.calcConeSphere = function() {
        return this.area() 
        + Math.PI * this.radius 
        * Math.sqrt(this.height ** 2 + this.radius ** 2);
    }
    Cylinder.showInfoCone = function() {
        console.log(
            "Cone Capacity: " + this.calcConeCapacity() + 
            "\nCone Sphere: " + this.calcConeSphere()
        );
    }
    var cone = Object.create(Cone);
    cone.initCone(2, 10);
    cone.showInfoCone();
// Cone //
0

No fajnie, ogarniasz. Raczej nikt i tak nie używa javascriptu w ten sposób korzystając z prototype'ów, dziedziczenia i tak dalej. Trochę inne paradygmaty zazwyczaj są używane, w dodatku w większości to proste skrypty na stronę, pluginy lub ewentualnie kod serwerowy w nodejs który też zazwyczaj operuje na prostych, płaskich typach.

Pamiętaj że tego typu tworzenie obiektu:

 var Cylinder = Object.create(Circle);
    Cylinder.initCylinder = function(radius, height) {
        this.init(radius);
        this.height = height;
    }

jeśli nie korzystasz tutaj z prototypu (a powinieneś) to linijki kodu są wykonywane jedna po drugiej i za każdym razem tworzona jest funkcja co jest dość kosztowne. Spróbuj utworzyć tak milion instancji to zobaczysz że bez używania prototypu zajmie to nawet kilka minut, a z użyciem prototypu kilkaset milisekund.

Jeśli chcesz się pobawić obiektowością i "typami", polecam od razu przejście na TypeScript - tutaj możesz się pobawić https://www.typescriptlang.org/play/ i jednocześnie możesz też zobaczyć jak Twoja klasa zostanie transpilowana do javascript. Ustaw w "Config" -> "Target" żeby zobaczyć jak wygląda dziedziczenie czy enkapsulacja w czystym ES5.

1 użytkowników online, w tym zalogowanych: 0, gości: 1