Removing properties which are not owned or inherited - javascript

This is my code.
function Material() {
this.id = null;
}
function Box() {
// Inherit Material props
Material.call(this)
// Define own props
this.width = null;
this.height = null;
this.weight = null;
}
var box = new Box();
// Setting values to Box props
box.id = 12;
box.width = 250;
box.height = 350;
box.weight = '10kg';
// Not its property
box.color = 'red';
// TODO: Passing box variable via function which will return new object of Box without color property
console.log(box);
// {
// 'id': 12,
// 'width': 250,
// 'height': 350,
// 'weight': '10kg',
// }
So basically at some point I want to get rid of color property that got set on box object (which is not the property of Box Class).
I don't want to individually delete such properties since there can be many. I have no way to find out which properties to delete. But I just know which properties to keep (Its own & inherited).
I am using lodash here. I tried _.forIn alongside _.defaults but of no help.
Thanks

try these:
var diff = _.difference(_.keys(box), _.keys(new Box()));
var clear = _.omit(box, diff);
console.log(clear);
clear - is a copy of box object with initial collection of properties

You can create a dummy Box object, loop through your object's properties and check, whether or not dummy object contains it:
function Material() {
this.id = null;
}
function Box() {
// Inherit Material props
Material.call(this)
// Define own props
this.width = null;
this.height = null;
this.weight = null;
}
function removeAdditionalProperties(obj) {
var dummy = new Box();
for (var key in obj) {
if (!dummy.hasOwnProperty(key)) {
delete obj[key]; // Or whatever you want to do with that prop
}
}
}
var box = new Box();
// Setting values to Box props
box.id = 12;
box.width = 250;
box.height = 350;
box.weight = '10kg';
// Not its property
box.color = 'red';
removeAdditionalProperties(box);
console.log(box);

Related

How to invoke a function when a class is instantiated

I have two classes, one of which is a subclass of the other. What I want to do is have one of the functions, step, invoked when the subclass is instantiated. This will cause the subclass to blink. I can see how to do it with functional instantiation but not with pseudoclassical. I've attached the jquery and javascript. Basically, when the button is clicked, I want the blinky dancer to blink, this should happen via the step function. Maybe I'm calling it incorrectly in the dancer superclass?
$(document).ready(function() {
window.dancers = [];
$('.addDancerButton').on('click', function(event) {
var dancerMakerFunctionName = $(this).data('dancer-maker-function-name');
console.log(dancerMakerFunctionName);
var dancerMakerFunction = window[dancerMakerFunctionName];
var dancer = new dancerMakerFunction(
$("body").height() * Math.random(),
$("body").width() * Math.random(),
Math.random() * 1000
);
$('body').append(dancer.$node);
});
});
var makeBlinkyDancer = function(top, left, timeBetweenSteps) {
makeDancer.call(this, top, left, timeBetweenSteps);
};
makeBlinkyDancer.prototype = Object.create(makeDancer.prototype);
makeBlinkyDancer.prototype.constructor = makeBlinkyDancer;
var oldStep = makeBlinkyDancer.prototype.step;
makeBlinkyDancer.prototype.step = function() {
oldStep();
this.$node.toggle();
};
var makeDancer = function(top, left, timeBetweenSteps) {
this.$node = $('<span class="dancer"></span>');
};
makeDancer.prototype.step = function() {
setTimeout(step.bind(this), timeBetweenSteps);
};
makeDancer.prototype.setPosition = function (top, left) {
var styleSettings = {
top: top,
left: left
};
this.$node.css(styleSettings);
};
// makeDancer.prototype.setPosition();
You can call the method when the subclass is instantiated by calling init in the constructor. In the example and demo I called this method makeBlink rather than step or oldStep. Hope that makes it clear.
// the subclass BlinkyDancer
var BlinkyDancer = function (top, left, height, width, timeBetweenSteps) {
// ... get props from parent class
// make blink when sublcass instantiated
this.init = this.makeBlink();
};
Full Demo/Solution
Demo: https://codesandbox.io/s/call-method-when-class-instantiated-mnesxt
HTML
<body>
<div id="app">
<button id="make-blink">Make Blink</button>
<button id="stop-blink">Stop Blink</button>
</div>
</body>
JS
import $ from "jquery";
var Dancer = function (top, left, height, width) {
this.$node = $('<div class="dancer"></div>');
this.top = top;
this.left = left;
this.height = height;
this.width = width;
this.bg = "blue";
};
Dancer.prototype.putDancerOnScreen = function () {
this.$node.height(this.height).width(this.width);
this.$node.css("background-color", "blue");
this.$node.css("position", "absolute");
$("body").append(this.$node);
};
Dancer.prototype.setPosition = function () {
this.$node.css("top", this.top);
this.$node.css("left", this.left);
};
var myDancer = new Dancer(
Math.floor(Math.random() * 500),
Math.floor(Math.random() * 500),
50,
50
);
myDancer.putDancerOnScreen();
myDancer.setPosition();
// the subclass BlinkyDancer
var BlinkyDancer = function (top, left, height, width, timeBetweenSteps) {
Dancer.call(this, top, left, height, width); // get the props set up by Dancer
this.timeBetweenSteps = timeBetweenSteps;
this.blinkerId = null;
// make blink when sublcass instantiated
this.init = this.makeBlink();
};
// set inheritance and Dancer constructor function
BlinkyDancer.prototype = Object.create(Dancer.prototype);
BlinkyDancer.prototype.constructor = Dancer;
// add subclass methods
BlinkyDancer.prototype.makeBlink = function () {
if (this.blinkerId !== null) {
return;
}
let count = 0;
let blinkerId = setInterval(() => {
// do whatever thing you want to indicate blinking/dancing
if (count % 2 === 0) {
this.$node.css("background-color", "red");
} else {
this.$node.css("background-color", "blue");
}
count++;
}, this.timeBetweenSteps);
this.blinkerId = blinkerId;
console.log("blinkder id set: ", this.blinkerId);
};
BlinkyDancer.prototype.stopBlink = function () {
if (this.blinkerId === null) {
// already blinking
return;
}
if (this.blinkerId !== null) {
clearInterval(this.blinkerId);
console.log("blink id cleared: ", this.blinkerId);
this.blinkerId = null;
}
};
// instantiate a new subclass
let myBlinkyDancer = new BlinkyDancer(
Math.floor(Math.random() * 500),
Math.floor(Math.random() * 500),
50,
50,
25
);
// use parent class methods to put the element on screen
myBlinkyDancer.putDancerOnScreen();
myBlinkyDancer.setPosition();
const makeBlinkButton = document.getElementById("make-blink");
const stopBlinkButton = document.getElementById("stop-blink");
makeBlinkButton.addEventListener("click", function () {
myBlinkyDancer.makeBlink();
});
stopBlinkButton.addEventListener("click", function () {
myBlinkyDancer.stopBlink();
});
Explanation
The Dancer class takes some props related to positioning and dimensions. It gets a blue background and has two methods for adding the element to the DOM and setting the position on screen.
BlinkyDancer is a subclass that inherits all the props of Dancer as well as two new props and two new methods.
What I want to do is have one of the functions, step, invoked when the
subclass is instantiated.
When a new BlinkyDancer is instantiated, we call makeBlink right away with init so the element starts blinking.
// make blink when sublcass instantiated
this.init = this.makeBlink();
I used an alternating background color to demonstrate the blinking effect.
The makeBlink and stopBlink methods work in tandem with the new props timeBetweenSteps and blinkerId.
when the button is clicked, I want the blinky dancer to blink, this
should happen via the step function.
Buttons are wired up to trigger starting and stopping the blinking effect. I've replaced step in your example with simple and declarative methods for making the blink and stopping it.
makeBlinkButton.addEventListener("click", function () {
myBlinkyDancer.makeBlink();
});
The blinking effect uses setInterval to toggle the background color using the timeBetweenSteps prop as the interval duration. startBlink starts the interval and sets blinkerId to the corresponding interval ID. To stop the blinking effect, clearInterval is passed the blinkerId to clear it (stopping the blinking effect) before resetting it to null.
BlinkyDancer.prototype.makeBlink = function () {
if (this.blinkerId !== null) {
return;
}
let count = 0;
let blinkerId = setInterval(() => {
// do whatever thing you want to indicate blinking/dancing
if (count % 2 === 0) {
this.$node.css("background-color", "red");
} else {
this.$node.css("background-color", "blue");
}
count++;
}, this.timeBetweenSteps);
this.blinkerId = blinkerId;
console.log("blinkder id set: ", this.blinkerId);
};
BlinkyDancer.prototype.stopBlink = function () {
if (this.blinkerId === null) {
// already blinking
return;
}
if (this.blinkerId !== null) {
clearInterval(this.blinkerId);
console.log("blink id cleared", this.blinkerId);
this.blinkerId = null;
}
};
Maybe I'm calling [step] incorrectly in the dancer superclass?
It looks like you were on the right track but missing parens to call the method. In your example code, rather than:
makeBlinkyDancer.prototype.step;
You would do:
makeBlinkyDancer.prototype.step();

Type Error - Referencing Javascript Classes

Firstly a little elaboration of the project I'm working on. I have started building a 'map maker' for a 2d game I am working on. The project is just for fun and has proven so far to be a great way to learn new things.
I recently showed my working map maker code to a friend who suggested it would be much more re-usable if I restructured the project to be more OOR, which I am now attempting.
The problem I have is when I add a 'Guild' instance to my map, the first one works fine, but the second causes a type error that is giving me a headache!
I will post all of the relevant code from the different files below, but the overall structure is as follows:
Map.js = Map class file, container for setting the map overall size and iterating over (and placing) map objects.
MapObject.js = Class file for simple map objects such as walls, contains the position and icon properties.
Guild.js = Class file, extends MapObject.js, this is where my problem seems to be, adds an additional 'MapIcon' and will have other features such as levels and names etc.
map-maker.js = Main file for generating the map-maker page, utilises the above class files to create the map.
Below is the code used to create an instance of 'Guild' on my map:
map-maker.js (creating the map / map object / guild instances)
// Initialise a new instance of map class from Map.js using the user
provided values for #mapX and #mapY.
var currentMap = new Map(XY,40);
// Get the user box volume * map boxsize from Map.js
currentMap.calcDimensions();
// Create a Map Object and push it to the currentMap with its position.
function createMapObject(x,y,floor){
currentMap.objects.push(new MapObject(x,y,floor));
}
// Create a Guild Object (extension of Map Object) and push it to the currentMap with its position.
function createGuildObject(x,y,floor){
currentMap.objects.push(new Guild(x,y,floor));
}
....
case 13: // Enter Key (Submit)
unhighlightTools();
currentMap.drawMap();
if(currentFloor != null){
currentFloor.hasFloor = true;
if(currentFloor.tileName == "Guild"){
createGuildObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}else {
createMapObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}
console.log("Map object created at - X:"+currentFloor.position.x+" Y:"+currentFloor.position.y);
}
currentFloor = [];
highlightTools();
break;
}
Guild.js (constructor and assigning map icon)
class Guild extends MapObject {
constructor(x,y,floor) {
super(x,y,floor);
this.levels = [];
}
mapIcon() {
this.mapIcon = new Image();
this.mapIcon.src = "../images/mapSprites/obj-1.png"
return this.mapIcon;
}
}
MapObject.js (position setup and constructor)
class MapObject {
constructor(x,y,floor) {
this.position = {x, y};
this.icon = this.wallFloorIcons(floor);
}
wallFloorIcons(floor) {
this.img = new Image();
this.name = "";
this.name += (floor.wallNorth) ? 'n' : '';
this.name += (floor.wallEast) ? 'e' : '';
this.name += (floor.wallSouth) ? 's' : '';
this.name += (floor.wallWest) ? 'w' : '';
this.name = 'wall-'+this.name+'.png';
if(this.name == 'wall-.png'){
this.img.src = "../images/mapSprites/floor.png";
}else {
this.img.src = "../images/mapSprites/"+this.name;
}
return this.img;
}
getIcon() {
return this.img;
}
}
Map.js (processing the objects at a given location and drawing the canvas)
class Map {
// Map Width / Height and number of boxes. Used to generate map and zoom level.
constructor(wh, boxSize) {
this.size = wh;
this.width = wh[0];
this.height = wh[1];
this.boxSize = boxSize;
this.objects = [];
this.boxes = wh[0];
}
// Calculates the width and height * boxSize for rendering the canvas.
calcDimensions(){
this.realX = Math.floor(this.width * this.boxSize);
this.realY = Math.floor(this.height * this.boxSize);
this.realX = parseInt(this.realX,10);
this.realY = parseInt(this.realY,10);
this.realXY = [
this.realX,
this.realY
];
return this.realXY;
}
// Draws the canvas, grid and adds the objects that belong to the map.
drawMap(){
var self = this;
self.canvas = document.getElementById("canvas");
self.c = self.canvas.getContext("2d");
self.background = new Image();
self.background.src = "../images/mapSprites/oldPaperTexture.jpg";
// Make sure the image is loaded first otherwise nothing will draw.
self.background.onload = function(){
self.c.drawImage(self.background,0,0);
self.fillMap();
}
}
fillMap(){
var self = this;
self.c.lineWidth = 1;
self.c.strokeStyle = 'black';
self.c.fillStyle = "rgba(255, 255, 255, 0.2)";
for (var row = 0; row < self.boxes; row++) {
for (var column = 0; column < self.boxes; column++) {
var x = column * self.boxSize;
var y = row * self.boxSize;
self.c.beginPath();
self.c.rect(x, y, self.boxSize, self.boxSize);
self.c.stroke();
self.c.closePath();
for (var i=0; i<self.objects.length; i++) {
var floor = self.objects[i];
if (floor.position.x == column && floor.position.y == row) {
if (self.objectsAtPosition({x:floor.position.x, y:floor.position.y}) != null) {
var mapObjects = self.objectsAtPosition({x:floor.position.x, y:floor.position.y})
for (var mapObject of mapObjects) {
this.c.drawImage(mapObject.getIcon(), x, y, self.boxSize, self.boxSize);
console.log(mapObject);
if(mapObject instanceof Guild){
console.log(mapObject);
this.c.drawImage(mapObject.mapIcon(), x, y, self.boxSize, self.boxSize);
}
}
}
}
}
}
}
}
deleteObject(pos){
this.objectsAtPosition(pos);
for( var i = 0; i < this.objects.length; i++){
if(this.objects[i] == this.objs){
delete this.objects[i];
this.objects.splice(i,1);
}
}
}
objectsAtPosition(position) {
var objs = [];
for (var o of this.objects) {
if (o.position.x == position.x && o.position.y == position.y) {
objs.push(o);
}
}
return objs;
}
}
When I run the code, this is my error:
Uncaught TypeError: mapObject.mapIcon is not a function
at Map.fillMap (Map.js:70)
at Image.self.background.onload (Map.js:39)
The error comes after I add 1 guild then try to add any other map object. Guild or otherwise.
Sorry if this question is a little vague, I'm still learning (as you can see :p).
Thanks for your time!
Earl Lemongrab
Got a solution from a friend in the end.
The issue was that I was reassigning this.mapIcon = new Image() so it exploded when it was called a second time.
Feel pretty silly for not spotting it.
Thanks for the help everyone.

Does changing a constructor's property after its instances are created affect the instances? If yes, then how?

Suppose this is the constructor:
var Bike = function (speed) {
this.speed = speed;
};
These are the instances created:
var suzuki = new Bike(500);
var hayabusa = new Bike(550);
var honda = new Bike(400);
Now, what I want to do is to change the speed of all the bikes to 600.
This can be done by changing the property of each object one by one:
suzuki.speed = 600;
hayabusa.speed = 600;
honda.speed = 600;
Can't it be changed by just changing the property of the class?
Bike.speed = 600;
You can't do this. What you can do is change the prototype later. This will, however, only effect instances that don't have that property defined:
var Bike = function(speed){
if(speed) {
this.speed = speed;
}
};
Bike.prototype.speed = 100;
var suzuki = new Bike(500);
var hayabusa = new Bike();
var honda = new Bike();
console.log(suzuki.speed); //500
console.log(hayabusa.speed); //100
console.log(honda.speed); //100
Bike.prototype.speed = 200;
console.log(suzuki.speed); //500
console.log(hayabusa.speed); //200
console.log(honda.speed); //200
The only way to change all instances is actually to change all instances. But you could do something like saving the date when you set the property, and the date on the property of the prototype and define a getter that is doing what you want:
var Bike = function(speed){
Object.defineProperty(this, 'speed', {
get() {
if(this.speedMoment && this.speedMoment > Bike.speedMoment) {
return this._speed;
} else {
return Bike._speed;
}
},
set(val) {
Bike._speedMomentIter++;
this.speedMoment = Bike._speedMomentIter;
this._speed = val;
}
});
if(speed) {
this.speed = speed;
}
};
Bike._speedMomentIter = 0;
Object.defineProperty(Bike, 'speed', {
set(val) {
Bike._speedMomentIter++;
this.speedMoment = Bike._speedMomentIter;
this._speed = val;
}
});
Bike.speed = 100;
var suzuki = new Bike(500);
var hayabusa = new Bike(800);
var honda = new Bike();
console.log(suzuki.speed); //500
console.log(hayabusa.speed); //800
console.log(honda.speed); //100
Bike.speed = 200;
console.log(suzuki.speed); //200
console.log(hayabusa.speed); //200
console.log(honda.speed); //200
new Bike(number).speed is instance variable.
Bike.speed would be 'class' (object) variable.
Those 2 are completely different and don't affect each other.

JavaScript object inheritance issues

Hi please be patient with me. I am a PHP developer entering the world of javaScript. Now I'm building a grid that will contain blocks of different size. I want a base block with all the methods on it and then other blocks that extend this (to use its methods) but with different properties. I have done a lot of reading but dont seem to be able to find a consistent method. I have put together some code i though would work but it doesn't. SmallBlock in the example has its own properties but none of Blocks properties
// Simple extend function to add for creating object inheritance
function extend(Child, Parent) {
var Temp = function(){
Temp.prototype = Parent.prototype;
Child.prototype = new Temp();
Child.prototype.constructor = new Child();
}
}
//__________________________________________________
// ## OBJECT DECLARATIONS ##
//__________________________________________________
//__________________________________________________
// ## BLOCK ##
// Base object representing a block/tile in the grid
var Block = function(){
// The X position block
this.positionX = 0;
// The Y position block
this.positionY = 0;
// The height of the block
this.height = 0;
// The width of the block
this.width = 0;
// The horizontal orientation of a block
this.blockHorizontalOrientation = null;
// The vertical orientation of a block
this.blockVerticalOrientation = null;
// Method to set the width of a block
this.setWidth = function(width){
this.width = width;
};
// Method to set the height of a block
this.setHeight = function(height){
this.height = height;
};
// Method to get the width of a block
this.getWidth = function(){
return this.width;
};
// Method to get the height of a block
this.getHeight = function(){
return this.height;
};
// Method to set the X position of a block (in the grid)
this.setPosX = function(posX){
this.positionX = posX;
};
// Method to set the Y position of the block (in the grid)
this.setPosY = function(posY){
this.positionY = posY;
};
// Method to get the Y position of the clock
this.getPosY = function(){
return this.positionY;
};
// Method to get the X position of the clock
this.getPosX = function(){
return this.positionX;
};
// Method to get the horizontal orientation
this.getHOrientation = function(){
return this.blockHorizontalOrientation;
};
// Method to get the vertical orientation
this.getVOrientation = function(){
return this.blockVerticalOrientation;
};
// Method to get the horizontal orientation
this.setHOrientation = function(hOren){
this.blockHorizontalOrientation = hOren;
};
// Method to get the vertical orientation
this.setVOrientation = function(vOren){
this.blockVerticalOrientation = vOren;
};
}
//__________________________________________________
// ## SMALL BLOCK ##
// object representing a small block/tile in the grid -- extends Block
var BlockSmall = function(){
// The X position block
this.positionX = 0;
// The Y position block
this.positionY = 0;
// The height of the block
this.height = 1;
// The width of the block
this.width = 1;
// The horizontal orientation of a block
this.blockHorizontalOrientation = null;
// The vertical orientation of a block
this.blockVerticalOrientation = null;
};
// EXTENDING BLOCK
extend(BlockSmall, Block);
Please can somebody review this code and help by advising what it is i am doing wrong. Again please be patient with me if I am miles out. This is all brand new concepts to me.
Well, this should work for inheriting:
function extend(Child, Parent) {
Child.prototype = new Parent();
Child.constructor = Child;
}
In your code, you just define a function inside extend that never gets called.

Canvas stroke text for every object in array but draw just one image

I am trying to draw small image on canvas and some text for every person which I have in array.
I am doing like this
function drawPersonsOnCanvas(canvas, persons) {
var workIcon = '../images/work.png';
var partyIcon = '../images/party.png';
var context = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
// randomizePositions just put every object of array on random position inside canvas
persons = randomizePositions(persons, width, height);
for (var i in persons) {
var person= persons[i];
person['image'] = new Image();
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
person['image'].onload = function() {
context.drawImage(person['image'], person['x'], person['y']);
};
context.strokeText('person ' + person['status'], person['x'], person['y']);
}
}
and I always get all texts on map on good positions and just one image and x and y are different for all ( I have 5 persons in array, I tried and without onload but it doesn't work). Does anyone know what to change so I get one picture for every person?
By the time each onload is executed, your person variable has already been reassigned to the next element in persons.
So you need to create a new person variable inside onload.
To do this, add an index to your person[‘image’] that points to the correct person.
…
person['image'].index=i;
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
…
Then inside onload you can recreate the correct person:
var person=persons[this.index];
So your reworked code looks like this...and a Fiddle: http://jsfiddle.net/m1erickson/xms9B/
function drawPersonsOnCanvas(canvas, persons) {
var workIcon = '../images/work.png';
var partyIcon = //'../images/party.png';
var context = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
// randomizePositions just put every object of array on random position inside canvas
persons = randomizePositions(persons, width, height);
for (var i in persons) {
var person= persons[i];
person['image'] = new Image();
person['image'].onload = function() {
var person=persons[this.index];
context.drawImage(person['image'], person['x'], person['y']);
context.strokeText('person ' + person['status'], person['x'], person['y']);
};
person['image'].index=i;
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
}
}

Categories

Resources