Question Regarding some Haverbeke "Eloquent JavaScript" Constructor Code - javascript

I am having a bit of difficulty understanding a few lines of code from page 108 of Marijn Haverbeke's "Eloquent JavaScript" book.
Namely, in the example below, I don't understand what "element = (x, y) => undefined" in the constructor parameter list is doing, when the Matrix object is instantiated with "let matrix = new Matrix(3, 3, (x, y) => value ${x}, ${y});"
Can some step-by-step it for me? It seems like we are passing a function to the constructor, but I don't get why the constructor parameter list is setup the way it is with another arrow function.
var Matrix = class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width;
this.height = height;
this.content = [];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y);
}
}
}
get(x, y) {
return this.content[y * this.width + x];
}
set(x, y, value) {
this.content[y * this.width + x] = value;
}
}
var MatrixIterator = class MatrixIterator {
constructor(matrix) {
this.x = 0;
this.y = 0;
this.matrix = matrix;
}
next() {
if (this.y == this.matrix.height) return {done: true};
let value = {x: this.x,
y: this.y,
value: this.matrix.get(this.x, this.y)};
this.x++;
if (this.x == this.matrix.width) {
this.x = 0;
this.y++;
}
return {value, done: false};
}
}
Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
};
let matrix = new Matrix(3, 3, (x, y) => `value ${x}, ${y}`);
for (let {x, y, value} of matrix) {
console.log(x, y, value);
}

Ok, I forgot in ES6+ an equal sign after a parameter is a default value if the parameter is missing or undefined. The way he did this is just a little awkward.

Related

What is y * width + x from matrix class two-dimensional array

I'm kinda confused of how this statement works this.content[y * width + x] = element(x, y));
class Matrix {
constructor(width, height, element = (x, y) => undefined) { this.width = width; this.height = height; this.content = [];
//console.log(this.content);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y);
console.log(this.content[y * width + x] = element(x, y));
}
}
}
get(x, y) { return this.content[y * this.width + x]; } set(x, y, value) { this.content[y * this.width + x] = value; } }
class MatrixIterator { constructor(matrix) { this.x = 0; this.y = 0; this.matrix = matrix; } next() { if (this.y == this.matrix.height) return {done: true}; let value = {x: this.x, y: this.y, value: this.matrix.get(this.x, this.y)};
this.x++; if (this.x == this.matrix.width) { this.x = 0; this.y++; } return {value, done: false}; } }
Matrix.prototype[Symbol.iterator] = function() { return new MatrixIterator(this); };
//We can now loop over a matrix with for/of.
let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);
console.log(matrix);
for (let {x, y, value} of matrix) { console.log(x, y, value);
}
// → 0 0 value 0,0
// → 1 0 value 1,0
// → 0 1 value 0,1
// → 1 1 value 1,1
To identify each element of a matrix you need to use an index obviously , here the writer of the book you where you found this, chose this approach to do it :
y * this.width + x
This allows you to access a determined element of your Matrix while identifient all elements and guarantees that nothing will overlap .
Alternatively you can use :
[y + '_' + x] this does exactly the same purpus of y * this.width + x ,
but converting to string and concatenation uses loops for something that could be done by simply 2 operations ! y * this.width and result + x .
Here is a rewrite of your code with my way of indexing :
class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width;
this.height = height;
this.content = {};//you need this for string keys !
//console.log(this.content);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y +'_'+ x] = element(x, y);
console.log((this.content[y +'_'+ x] = element(x, y)));
}
}
}
get(x, y) {
return this.content[y +'_'+ x];
}
set(x, y, value) {
this.content[y +'_'+ x] = value;
}
}
class MatrixIterator {
constructor(matrix) {
this.x = 0;
this.y = 0;
this.matrix = matrix;
}
next() {
if (this.y == this.matrix.height) return { done: true };
let value = {
x: this.x,
y: this.y,
value: this.matrix.get(this.x, this.y),
};
this.x++;
if (this.x == this.matrix.width) {
this.x = 0;
this.y++;
}
return { value, done: false };
}
}
//We can now loop over a matrix with for/of.
let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);
console.log('Matrix formed is : ',matrix);

javascript class method call from inside another method, results in "moveToCell is not a function" error

I have this class:
class AGV {
constructor(id, x, y, agvcolor, matrixTable) {
this.id = id;
this.x = x;
this.y = y;
this.color = agvcolor;
this.xOffset = 1;
this.yOffset = 1;
this.matrix = matrixTable;
}
setX(newX) {
this.x = newX;
}
setY(newY) {
this.y = newY;
}
lastCoordinates() {
return JSON.stringify({
'x': this.x,
'y': this.y
});
}
spawn() {
var xCoord = this.x - this.xOffset;
var yCoord = this.y - this.yOffset;
var cell = this.matrix.childNodes[1].children[yCoord].children[xCoord];
cell.style.background = this.color;
cell.innerHTML = this.id;
}
moveToCell(x, y) {
console.log("rolling to x: " + x + " y: " + y);
this.clearPreviousCell(this.x, this.y);
// offsets are needed to convert array index to actual position
var xCoord = x - xOffset;
var yCoord = y - yOffset;
var cell = this.matrix.childNodes[1].children[yCoord].children[xCoord];
cell.style.background = this.color;
cell.innerHTML = this.id;
}
clearPreviousCell(x, y) {
var xCoord = x - xOffset;
var yCoord = y - yOffset;
var cell = this.matrix.childNodes[1].children[yCoord].children[xCoord];
cell.style.background = "#fff";
cell.innerHTML = "";
}
runTask(x, y, z) {
console.log("Running task. Target: X: " + x + " Y: " + y);
if (this.x < x) {
//while (this.x < x) {
this.x += 1;
setTimeout(function() {
moveToCell(this.x, y);
}, 800);
//}
}
}
}
And whenever I call moveToCell() function inside function runTask(), I get an error stating that the "moveToCell" function is not defined and as so, I cannot update the object's position in the map.
I've also tried this.moveToCell() insted, but results in the same problem. I don't understand what is wrong here.
Can anyone please post any thoughts on this? Thanks in advance
You need to specify the instance to call the method on, which should be this.
Also, this is not saved in normal closures. Use an arrow function to keep the context.
runTask(x, y, z) {
console.log("Running task. Target: X: " + x + " Y: " + y);
if (this.x < x) {
//while (this.x < x) {
this.x += 1;
setTimeout(() => this.moveToCell(this.x, y), 800);
//}
}
}
Since it's inside the class, you need to use the this operator. Change it to:
this.moveToCell(this.x, y);
As you're using it within setTimeout(function () { ... }), you need to cache this:
this.x += 1;
_this = this;
setTimeout(function() {
_this.moveToCell(this.x, y);
}, 800);

JavaScript Cannot Set Property '0' of undefined (2-dimensional array)

I know a bunch of people have already asked this, but nothing I have tried has worked. I have been stuck for days.
I suspect it may just be because the thing I am using (code.org) is a pile of flaming garbage, and it uses ES5. If you notice anything wrong, though, please let me know!
function Vector2(x, y)
{
this.x = x;
this.y = y;
return this;
}
function Pixel(x, y, darkness)
{
this.position = Vector2(x, y);
this.darkness = darkness;
this.rgbDarkness = this.darkness * 255;
return this;
}
function DataTexture2D(offsetX, offsetY, width, height)
{
this.offset = Vector2(offsetX, offsetY);
this.size = Vector2(width, height);
this.pixels = [];
for (var x = 0; x < width; x++)
{
this.pixels[x] = [];
for (var y = 0; y < height; y++)
{
this.pixels[x][y] = Pixel(x + this.offset.x, y + this.offset.y, 0.5);
}
}
return this;
}
var test = DataTexture2D(100, 50, 200, 300);
console.log(test.offset.x);

Explain how the property is being unpacked

Im not sure how the value is being unpacked in:
for (let {y,x, value} of matrix) {
console.log(x, y, value);
}
how does it know to pull it from the next().value.value of the Matrix.prototype[Symbol.iterator]?
class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width;
this.height = height;
this.content = [];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y);
}
}
}
get(x, y) {
return this.content[y * this.width + x];
}
set(x, y, value) {
this.content[y * this.width + x] = value;
}
}
let obj = new Matrix(2, 2, (x, y) => `value ${x},${y}`)
class MatrixIterator {
constructor(matrix) {
this.x = 0;
this.y = 0;
this.matrix = matrix;
}
next() {
if (this.y == this.matrix.height) return {done: true};
let value = {x: this.x,
y: this.y,
value: this.matrix.get(this.x, this.y)};
this.x++;
if (this.x == this.matrix.width) {
this.x = 0;
this.y++;
}
return {value, done: false};
}
}
Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
};
let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);
for (let {y,x, value} of matrix) {
console.log(x, y, value);
}
// → 0 0 value 0,0
// → 1 0 value 1,0
// → 0 1 value 0,1
// → 1 1 value 1,1
Because for (let obj of matrix) gives you the object you're building in this line let value = {x: this.x, y: this.y, value: this.matrix.get(this.x, this.y)};, into every loop iteration, then the destructuring syntax {y, x, value} extracts every field.

best design practice to separate common code?

I have a card class:
function Card() {
this.image = new Image();
this.x = 0;
this.y = 400;
this.initialX = 0;
this.initialY = 0;
this.scaleFactor = 4;
this.setImage = function(ii){
this.image.src = ii;
};
this.getWidth = function(){
if (this.image == null){
return 0;
}
return this.image.width / this.scaleFactor;
}
this.getHeight = function(){
if (this.image == null){
return 0;
}
return this.image.height / this.scaleFactor;
}
this.pointIsInCard = function(mx, my){
if (mx >= this.x && mx <= (this.x + this.getWidth()) && my >= this.y && my <= (this.y + this.getHeight()))
{
return true;
}
else{
return false;
}
};
};
I then have a deck class:
function Deck(x, y, w, h){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.cards = [];
}
I need to add a method in Deck class similar to pointIsInCard instead it will be called pointIsInDeck. The logic will be same i.e to check whether the passed in point falls in the boundary of the object. So seeing this duplication of code I wanted to know what is a good design practice to avoid this duplication? One option I thought of was to extract the method out and create a function for generic object with x, y, width, height but again from OOP principles I thought this method should belong to the class/object. I appreciate any help! Thanks!
A common approach for what you're doing is to attach a Rectangle or similar instance with that functionality to both of your objects, that is:
class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
containsPoint(x, y) {
return x >= this.x && x =< this.width &&
y >= this.y && y =< this.height;
}
}
Then just add it to Card and Deck:
function Card() {
this.rect = new Rectangle(/* Your card shape */);
// ...
}
function Deck() {
this.rect = new Rectangle(/* Your deck shape */);
// ...
}
And you can do:
card.rect.containsPoint();
deck.rect.containsPoint();
If these are classes related to drawing, they would both inherit from something like Rectangle, which they would both inherit this behaviour from.
If they are gameplay-related, I would prefer them each referencing a Rectangle (or its subclass) that they would delegate all UI-related tasks to; then reduce this to the previous paragraph's solution.
You can use Function.prototype.call() to set this at a function call
function Card() {
this.x = 1; this.y = 2;
};
function Deck() {
this.x = 10; this.y = 20;
}
function points(x, y) {
// do stuff
console.log(x, this.x, y, this.y); // `this`: `c` or `d`
}
var c = new Card();
var d = new Deck();
points.call(c, 3, 4); // `this`: `c` within `points` call
points.call(d, 100, 200); // `this`: `d` within `points` call

Categories