Czy powinno się przypisywać wartości polom obiektu czy ustawiać je np. w inicie?

0

Oglądając tutorial zauważyłem, że autor robi coś takiego:

var foo = {
        width: null,
        height: null,
        grid: null,

        /**
         * Creates a grid filled with EMPTY values
         * @param {Int} c Number of columns in the grid
         * @param {Int} r Number of rows in the grid
         */
        init: function(c, r) {
            this.width = c;
            this.height = r;

            this.grid = []; // dlaczego nie zrobic tego wyzej? Oo

            for (var i = c.length - 1; i >= 0; i--) {
                this.grid.push([]);
                for (var i = r.length - 1; i >= 0; i--) {
                    this.grid.[x].push(EMPTY);
                }
            }
        },
}

W innym miejscu w kodzie:

queue: null,
last: null 

// pozniej...

queue = [] 
// pozniej tablica jest wypelniana...

this.last = queue[0]

dlaczego od razu nie mogl zrobic np.:

queue: [],
last: queue[0] || null
0

Ad 1:

this.grid = []; // dlaczego nie zrobic tego wyzej? Oo

Najwyraźniej autor chce mieć odróżniony stan niezainicjowania danego pola od tego czy jest puste.

Ad 2: jak sam zauważyłeś, później tablica jest wypełniana - zatem queue[0] przed wypełnieniem tablicy to nie jest to samo co queue[0] po wypełnieniu tablicy.

0

No tak, przecież to dynamicznie nie przypisuje wartości.. Super, dzięki za wyjaśnienie. A czy któraś z tych praktyk (Ad. 1) jest lepsza?

0

Zależy co chce się osiągnąć - można faktycznie chcieć rozróżnić stan pustej tablicy od niezainicjowanej, nie jest to nic niespotykanego.

0

No tak, przecież to dynamicznie nie przypisuje wartości.. Super, dzięki za wyjaśnienie. A czy któraś z tych praktyk (Ad. 1) jest lepsza?

To zależy. Wszystko ma swoje zalety i wady w zależności od tego, co się chce osiągnąć.

Poprogramuj trochę prototypowo w JS, to docenisz zalety inicjalizowania tablic w konstruktorze/inicjalizatorze, zamiast wrzucania ich luzem:

np. ten kod skończy się WTFem:

function Foo () {
   
}
Foo.prototype.test = {
    animals: []
};

var a = new Foo();
a.animals.push('kot');
alert(a.animals);  // kot
var b = new Foo();
b.animals.push('pies');
alert(b.animals); // WTF: "kot,pies" a nie "pies" jak można by się spodziewac

https://jsfiddle.net/j32wj5dr/

wszystko dlatego, że JavaScript kopiując tablice robi to przez referencję, więc referencja a.animals i b.animals wskazuje na tę samą tablicę. Tutaj więc faktycznie lepszy byłby sposób inicjalizowania tablicy dopiero w inicjalizatorze czy konstruktorze, np.:
https://jsfiddle.net/j32wj5dr/2/

Pytanie też co zrobimy z funkcją inicjalizującą, kiedy i w jakich okolicznościach zostanie wywołana (w podanym przez ciebie przykładzie widzę, że funkcja init służy również do tego, żeby programista mógł ustawić później parametry c oraz r. Może też dlatego w tym konkretnym przykładzie użyto funkcji inicjalizującej (defacto customowej, bo w czystym JavaScript nie ma czegoś takiego jak init, ktoś sobie tak umownie stworzył taką funkcję)

Z drugiej strony zapewne można byłoby wymyśleć jakiś przykład, który by pokazywał, że jednak przeciwna praktyka (tablice bezpośrednio w literałach obiektów) byłaby bardziej wskazana, np. z powodu większej czytelności. W sumie użycie funkcji init czy podobnej powoduje pewien rozrost kodu i nie zawsze jest to potrzebne.

0

Nie chce zakładać nowego tematu, dlatego zadam pytanie tutaj. Mam kilka obiektów.

  • SnakeGame

  • GameBoard

  • Grid

  • Snake

  • Worm

W obiekcie grid mam metodę draw:

Grid

draw: function(gameboard) {
        var cellValue;

        for (var x = 0; x < this.cols; x++) {
            for (var y = 0; y < this.rows; y++) {
                
                cellValue = this.get(x, y);

                if(this.isEmpty(x, y)) 
                    gameboard.ctx.fillStyle = "#FFF";
                else if (Snake.isPrototypeOf(cellValue)) 
                    gameboard.ctx.fillStyle = "#0FF";
                else if (Worm.isPrototypeOf(cellValue)) 
                    gameboard.ctx.fillStyle = "#F00";

                gameboard.ctx.fillRect(x * this.cellWidth, y * this.cellWidth, this.cellWidth, this.cellWidth);
            }
        }
    }

Jak widać potrzbuję tam dostępu do ctx, który moim zdaniem powinien siedzieć w GameBoard.

GameBoard

    /**
     * Creates a grid filled with EMPTY values
     * @param {Object} o options
     */
    init: function(o) {
        this.grid = Object.create(Grid);
        this.grid.create(o.cols, o.rows, o.cellWidth);
        
        this.width = this.grid.width;
        this.height = this.grid.height;

        this.canvas = document.createElement("canvas");
        this.canvas.width = this.width;
        this.canvas.height = this.height;
        
        this.ctx = this.canvas.getContext("2d");
        
        document.getElementsByTagName("body")[0].appendChild(this.canvas);
    },

    fill: function() {
        this.grid.draw(this);
    }

Funkcja init w głownym obiekcie SnakeGame:

    function init() {
		gameboard = Object.create(GameBoard);
	    snake = Object.create(Snake);
	    worm = Object.create(Worm);

	    gameboard.init(options.grid);
	    snake.init(options.snake);
	    worm.init();
	    
	    gameboard.grid.set(snake);
	    gameboard.grid.set(worm);

	    gameboard.fill();
    }

Zastanawiam się czy dobrą praktyką jest przekazanie w ten sposób jak w funkcji fill obiektu GameBoard, żeby mieć dostęp do ctx, czy po prostu przerzucić ten ctx do Grid...

Trochę się zakręciłem i nie wiem jak to rozwiązać z poziomu designu.

TLFC (fucking confused)
W jaki sposób można połączyć ze sobą powyższe obiekty, które wypisałem, żeby to miało ręce i nogi.

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