Pochodząc z Java do Angularjs (jestem nowy do wszystkich technologii związanych z JavaScript), próbuję "tłumaczyć" moje myśli. Napisałem obiekt niektóre testy w Jasmin. Niektóre testy działają, ale utrzymuje się w przypadku niepowodzenia tym, co wydawało mi się być problemem. Oto kod:

function Invoice() {

var invoicelines = new LinesHandler();

function LinesHandler() {

    var lines = [];

    function Line() {
        var quantity = 0;
        var description = '';
        var rate = 0;
        var total=0;

        return {
            getQuantity : function() {
                return this.quantity;
            },

            setQuantity : function(quantity) {
                this.quantity = quantity;
                this.refreshTotal();
            },

            getDescription : function() {
                return this.description;
            },

            setDescription : function(description) {
                this.description = description;
            },

            getRate : function() {
                return this.rate;
            },

            setRate : function(rate) {
                this.rate = rate;
                this.refreshTotal();
            },

            getTotal : function() {
                return this.total;
            },

            refreshTotal : function() {
                this.total = this.quantity * this.rate;
            }
        }
    }

    return {
        getLines : function () {
            return lines;
        },

        addLine : function(line) {
            lines.push(line);
        },

        removeLine : function() {},

        editLine : function() {},

        createNewLine : function() {
            return new Line();
        },

        getCount : function() {
            return lines.length;
        },

        getLine : function(i) {
            return lines[i];
        }
    }
}
return {
    createNewLine : function() {return invoicelines.createNewLine();},
    getLinesCount : function() {return invoicelines.getCount();},
    addLine : function(line) {invoicelines.addLine(line);},
    getLines : function() {return invoiceLines;},
    getLinesTotal : function() {
        var total = 0;
        for (line in invoiceLines) {
            total += line.getTotal;
        };
        return total;
    },
    getTaxesTotal: function() {}
};

}

I oto test, który zawodzi

it('Calculates invoice\'s total while adding lines', function() {
        var invoice = scope.invoice;

        for(var i = 1, j=10; i < 4; i++, j += 10){
            var line = invoice.createNewLine();
            line.setQuantity(j);
            line.setRate(j);
            invoice.addLine(line);
        }

        expect(invoice.getLinesTotal()).toBe(1400);
    });

Próbowałem połączyć inferyceliny bezpośrednio w innej funkcji, wypróbowałem z tą.Invoiceliny, wypróbowałem funkcję Getlines (), ale problem jest taki sam, wciąż wchodzę w Firebug coś w rodzaju:

ReferenceError: invoiceLines is not defined

Nie rozumiem problemu. Dlaczego inne funkcje są w stanie zobaczyć prywatnego członka, ale nie jest funkcją Getlinestotal? lub pętla?

Z góry dziękuję.

PS: Nie wahaj się krytykować kod, jestem pewien, że nie jest to najlepszy sposób na kod w JavaScript

1
user3013860 20 listopad 2013, 20:08

2 odpowiedzi

Najlepsza odpowiedź

Jeśli chodzi o Twój tytuł (jak zrobić dobrą enkapsulację w JavaScript), oto lepszy ogólny wzór do użycia podczas strukturyzacji obiektu zorientowanego JavaScript:

// Wrap a "class" module in an immediately invoked function expression.
var Parent = (function() {

  // Use a function declaration to create the class's constructor function.
  function Parent(param) {

    // Initialize instance properties from constructor args.
    this.param = param;

    // Initialize any other instance properties we need.
    this.initVar = "foo";
  }

  // Add instance methods to the class's prototype. These will not exist directly on the instance.
  // Instead, JS will look at the instances's prototype to find the value.
  // If you try to access a method (or property) that is not defined on this class prototype,
  // JS will keep looking up the prototype chain. The order here would go:
  //   instance -> instance.[[prototype]] (AKA Parent.prototype) -> Object.prototype -> null
  Parent.prototype.someMethod = function() {
    console.log("Cheese it!");
  };

  // Here we just make a simple method that logs an instance property to the console.
  Parent.prototype.someParentMethod = function() {
    console.log(this.param);
  };

  // Return our now-defined class
  return Parent;

// Immediately invoke the wrapping function expression, returning the Parent class.
}());

// Now lets make a class that inherits from Parent.
var Child = (function() {

  // Make the child constructor
  function Child() {
    // If we want we can call the Parent constructor, passing our Child instance as `this`
    Parent.call(this, "someParam");

    // Do any other child instance initialization
  }

  // Set the Child prototype to a new instance of Parent. For child, the prototype chain will look like:
  //   instance -> instance.[[prototype]] (AKA Child.prototype) -> instance.[[prototype]].[[prototype]] (AKA Parent.prototype) -> Object.prototype -> null
  Child.prototype = new Parent();

  // Point the constructor property to the Child constructor (currently points to Parent)
  Child.prototype.constructor = Child;

  // Override a parent method
  Child.prototype.someMethod = function() {
    console.log("Kill all humans!");
  };

  // Add a method to the Child prototype
  Child.prototype.someChildMethod = function() {
    console.log("Here be dragons");
  };

  return Child;
}());

var myParent = new Parent("foobar");
var myChild  = new Child();
myParent.someMethod();        // => "Cheese it!"
myChild.someMethod();         // => "Kill all humans!"
myParent.someParentMethod(); // => "foobar"
myChild.someParentMethod();  // => "someParam"
myChild.someChildMethod();    // => "Here be dragons"
myParent.someChildMethod();   // => TypeError: Object #<Parent> has no method 'someChildMethod'

Wiem, że może to nie odpowiedzieć bezpośrednio na twoje pytanie. Jednak jest to demonstracja tworzenia dobrze zamkniętych "klas" z dziedzictwem. System prototypu może zająć trochę do GRK - starałem się, aby komentarze były jak najbardziej jasne. Pomysł polega na tym, że przez dołączenie metod do prototypu są one określone tylko raz, a zatem zajmują dużo mniej pamięci. Jeśli metoda nie istnieje na samej instancji, będzie wyglądać w górę jego prototypowego łańcucha, aby sprawdzić, czy jest zdefiniowany w dowolnym miejscu, aż do ostatecznego łańcucha prototypu zostanie ostatecznie dostanie do null. Również przez manipulowanie prototypami klasy możemy osiągnąć wiele dziedzictwa poprzez kompozycję.

Mam nadzieję, że to pomoże. Daj mi znać, jeśli coś jest niejasne.

1
sbking 20 listopad 2013, 17:10
  1. Nie możesz utworzyć instancji LinesHandler przed jej zdefiniowaniem.
  2. Usuń "var" z definiowania invoicelines, aby zrobić go globalne (podobne do static w Javie)
  3. Domyślny zwrot "New Lineshandler" jest this, nie powinieneś zastępować return.
  4. Funkcja Line jest prywatna, zdefiniuj to jak this.line = function(){}
0
Grim 20 listopad 2013, 16:23