I'm a new dev coming from a Ruby background. Recently I've been working very hard in JS and I'm having some issues with class inheritance in the new ES 6. I feel like it may be an issue with my understanding of JS or conflating it to much to Ruby. I've been trying to translate a Ruby Project into JS to practice, but I'm now failing a feature test.
Failing Feature test when trying to initialize two instances of a class
const STATS = { str:1, dex:1, int:1 }
class Example {
constructor(race, clas) {
this.race = race,
this.clas = clas,
this.stats = this.add(STATS)
}
add(stats) {
if(this.race != 'empty'){
stats.str += this.race.str
stats.dex += this.race.dex
stats.int += this.race.int
}
if(this.clas != 'empty') {
stats.str += this.clas.str
stats.dex += this.clas.dex
stats.int += this.clas.int
}
return stats
}
}
var a = new Example({str: 1, dex:0, int:0}, 'empty');
var b = new Example('empty', {str: 0, dex:0, int:1});
console.log('Should be str:2 dex:1 int:1');
console.log(a.stats);
console.log('Should be str:1 dex:1 int:2');
console.log(b.stats);
My class has functions that change the state when constructed, but the issue is any time a new Class is called it retains the changes from the previous variable. It is only an issue on my feature test as it is the only time that class is called twice.
This is the link to my feature test https://github.com/RyanWolfen7/jsRPG/blob/master/cypress/integration/featureTest/characterFeature_test.js
And this is the class thats failing the tests
https://github.com/RyanWolfen7/jsRPG/blob/master/models/characters/character.js
Honestly I'm probably going to scrap my project and start fresh anyways, but I would like to understand what my issue is. I was taking an OOD approach to JS and translating my ruby project https://github.com/RyanWolfen7/ruby_rpg to JS. I'm not sure if its because I wrote the test wrong or a deep misunderstanding of how es-6 works.
Things I have tried:
Creating a new object
Assigning a newly created object to new class
It's not an inheritance issue. In fact, it has nothing to do with OO at all. What you are seeing is the result of the fact that most things in javascript are references (pointers) but you are writing code as if STATS is a value.
In your function add you do this:
add(stats) {
if(this.race != 'empty'){
// modifying passed in object, not creating new object
stats.str += this.race.str
stats.dex += this.race.dex
stats.int += this.race.int
}
if(this.clas != 'empty') {
// modifying passed in object, not creating new object
stats.str += this.clas.str
stats.dex += this.clas.dex
stats.int += this.clas.int
}
return stats
}
So no matter how many times you call add() and from whichever instance of Example you call it from you are only accessing and overwriting the single shared STATS object.
To create new copies of STATS on each function call you need to copy it to a new object. The fastest old-school way to do this is to serialise the object to a string then convert the string back to an object:
add (input) {
var stats = JSON.parse(JSON.stringify(input));
// ...
}
This feels ugly but multiple benchmarks really did show it to be the fastest method.
Modern javascript can do this using Object.assign:
add (input) {
var stats = Object.assign({},input);
// ...
}
However, I don't know if it is faster. You will have to benchmark it yourself. You can google the phrase "js clone object" for more info.
Related
Edit: the code below was made up on the spot to show how I was going about what I was doing. It definietely won't run, it is missing a lot of things.
Here is a working example in codepen: https://codepen.io/goducks/pen/XvgpYW
much shorter example: https://codepen.io/goducks/pen/ymXMyB
When creating a function that is using call or apply, the this value stays null when using getPerson. however, when I use apply or call with getPerson it returns the correct person.
Please critique, I am really starting to learn more and more. I am in the middle of a project section so it might be hard to change all the code, but my next project could implement this better.
call and apply are setting to the window and not the object.
I will provide code that is much simpler with the same concept of what I am talking about.
function createPerson(){
this.manager = null;
this.teamManager = null;
this.setTeamManager = function(val){
this.teamManager = val;
}
this.setManager = function(val){
console.log('setting manager to',val);
this.teamManager = val;
}
this.getTeamManager = function(){
console.log('setting team manager to',val);
return this.teamManager ;
}
this.getManager = function(){
return this.manager;
}
this.appendSelect = function(elem){
var that = this;
createOtherSelects(that,elem);
}
//some functions that create selects with managers etc
//now assume there are other selects that will filter down the teams,
//so we might have a function that creates on change events
function createOtherSelects(that){
//code that creates locations, depending on location chosen will
//filter the managers
$('#location').on('change',function(){
//do some stuff
//... then call create management
createManagement(that,elem);
});
}
function createManagement(that,elem){
var currentLocation = that.location; //works
var area = that.area;//works ... assume these are set above
//code that returns a filter and unique set of managers back
that.teamManager = [...new Set(
data.map(person=>{
if(person.area==area &&
person.currentLocation==currentLocation
){
return person;
}
})
)].filter(d=>{if(d){return d}});
if(elem.length>0){
var selectNames = ['selectManager','selectTeamManager'];
var fcns = [that.setManager,that.setTeamManager];
for(var i = 0; i < selectNames.length;i++){
//do stuff
if(certainCriteriaMet){
// filter items
if(filteredManager == 1){
fcns[i].call(null,currentManager);//
}
}
}
}
}
}
var xx = new createPerson()
In console I see setting manager and setting team manager to with the correct values.
however when I call xx in console, I see everything else set except for
xx.teamManager and xx.manager
instead it is applying to the window, so if I type teamManager in the console, it will return with the correct person.
If I straight up say
that.setManager('Steve')
or even it works just fine.
xx.setManager('steve')
the this value in setManager is somehow changing from the current instance of the object to this window. I don't know why, and I would like to learn how to use apply and call using that for future reference.
I think the issue is with your following code
fcns[i].call(null,currentManager)
If you are not supplying "this" to call, it will be replaced with global object in non-strict mode.
fcns[i].call(that,currentManager)
See mdn article here
From your codepen example, you need to change that line
fcnset[0].apply(that,[randomName]);
The first argument of the apply method is the context, if you are not giving it the context of your method it's using the global context be default. That's why you end up mutating the window object, and not the one you want !
In Screeps, I this code doesn't work:
var sources = creep.room.find(Game.FIND_SOURCES_ACTIVE);
It says this:
Cannot read property 'find' of undefined
I have been looking around and cannot find ANY other way to find sources.
Also I've noticed that most of other peoples code doesn't work and even the tutorial's code no longer works when put into the real game.
I can't be completely sure about your issue since I don't have your complete code to go off of but one issue could be that creepis not defined.
You need somewhere in your code to define creep such as a for loop to loop over each of your creeps in the game or room.
var roleMiner = require('role.miner') // role.miner being the module name for miner actions
for(var name in Game.creeps) {
var creep = Game.creeps[name];
//
// do whatever you wish with the current selected creep.
//
// most of the time you will call a module similar to what the tutorials suggest and put your actions for it in there
//
if(creep.memory.role == 'miner'){
roleMiner.run(creep); // passes the current selected creep to the run function in the module
}
}
So, in your roleMiner module you would have something that defines your miners actions.
var roleMiner = {
run: function(creep) {
// this one returns an array of the sources that are in the room with the creep
var sourcesRoom = creep.room.find(FIND_SOURCES);
// this one returns the source object which is closest to the creeps positon
var sourcesClose = creep.pos.findClosestByRange(FIND_SOURCES);
}
}
module.exports = roleMiner;
Hope this helps.
Screeps have some ... mechanism when sharing your data between each game tick.
If you store any thing in global Memory object, your data will lose all its prototype.
to restore your prototype use Object.setPrototypeOf(creep,Creep.prototype) or create new Creep object from your creep id.
I think what you are looking for is:
var sources = creep.pos.findClosestByRange(Game.SOURCES);
or
var sources = creep.pos.findClosestByPath(Game.SOURCES);
im a new player, not sure my code is efficient, i think the find method will be like this:
var sources = creep.room.find(FIND_SOURCES_ACTIVE)
creep will going to the active resource to harvester.
In the following code example I want to somehow create a var myThingName via a functions arguments so I can avoid having to build global var names outside of the Module.
I have tinkered with window['myvarname'] = "yada"; But that's a global and feels like a complete hack.
In short, I am assuming I need to have a way to make many vars like : myThingName_1 myThingName_2 etc...
Background: I am trying to build a Canvas Element constructor to put any number of individual Canvas elements onto the screen. I am using various libraries like Paper.js Kinetic.js etc. When trying to streamline the production I built a Module wrapper but am now stuck at how to create unique var names for the libraries KLASS constructor for their various implementations of stage or scope.
Perhaps I am doing things totally incorrectly from the start.
But at the moment I am stumped as to how this goes about or what the name of the pattern I am looking.
var MagicThingModule = {
createThing : function(myThingNameAsString){
var myThingName = myThingNameAsString;
myThingName = new KLASS.Shape({ // code });
},
init : function(myThingNameAsString){
createThing(myThingNameAsString);
}
}
MagicThingModule.init("sendNameForThing_1");
How about:
var MagicThingModule = {
allMyThings: {},
createThing : function(myThingNameAsString){
var myThingName = myThingNameAsString;
myThingName = new KLASS.Shape({ // code });
this.allMyThings[myThingNameAsString] = myThingName;
},
// use me to et your thing back...
getMyThing: function(myThingName){
return this.allMyThings[myThingName];
},
init : function(myThingNameAsString){
createThing(myThingNameAsString);
}
}
and you can later reference it by:
var myThing = MagicThingModule.getMyThing("sendNameForThing_1");
this would be my first question ever on stackoverflow, hope this goes well.
I've been working on a game (using corona SDK) and I used Node.js to write a small little server to handle some chat messages between my clients, no problems there.
Now I'm working on expanding this little server to do some more, and what I was thinking to do is create an external file (module) that will hold an object that has all the functions and variables I would need to represent a Room in my games "Lobby", where 2 people can go into to play one against the other, and each time I have 2 players ready to play, I would create a copy of this empty room for them, and then initialize the game in that room.
So I have an array in my main project file, where each cell is a room, and my plan was to import my module into that array, and then I can init the game in that specific "room", the players would play, the game will go on, and all would be well... but... my code in main.js:
var new_game_obj = require('./room.js');
games[room_id] = new_game_obj();
games[room_id].users = [user1_name,user2_name];
Now, in my room.js, I have something of the sort:
var game_logistics = {};
game_logistics.users = new Array();
game_logistics.return_users_count = function(){
return game_logistics.users.length;
}
module.exports = function() {
return game_logistics;
}
So far so good, and this work just fine, I can simply go:
games[room_id].return_users_count()
And I will get 0, or 1, or 2, depending of course how many users have joined this room.
The problems starts once I open a new room, since Node.js will instance the module I've created and not make a copy of it, if I now create a new room, even if I eliminated and/or deleted the old room, it will have all information from the old room which I've already updated, and not a new clean room. Example:
var new_game_obj = require('./room.js');
games["room_1"] = new_game_obj();
games["room_2"] = new_game_obj();
games["room_1"].users = ["yuval","lahav"];
_log(games["room_1"].return_user_count()); //outputs 2...
_log(games["room_2"].return_user_count()); //outputs 2...
Even doing this:
var new_game_obj = require('./room.js');
games["room_1"] = new_game_obj();
var new_game_obj2 = require('./room.js');
games["room_2"] = new_game_obj2();
games["room_1"].users = ["yuval","lahav"];
_log(games["room_1"].return_user_count()); //outputs 2...
_log(games["room_2"].return_user_count()); //outputs 2...
Gives the same result, it is all the same instance of the same module in all the "copies" I make of it.
So my question as simple as that, how do I create a "clean" copy of my original module instead of just instancing it over and over again and actually have just one messy room in the end?
What you're doing is this (replacing your require() call with what gets returned);
var new_game_obj = function() {
return game_logistics;
}
So, every time you call new_game_obj, you return the same instance of game_logistics.
Instead, you need to make new_game_obj return a new instance of game_logistics;
// room.js
function Game_Logistics() {
this.users = [];
this.return_users_count = function(){
return this.users.length;
};
}
module.exports = function() {
return new Game_Logistics();
}
This is quite a shift in mentality. You'll see that we're using new on Game_Logistics in module.exports to return a new instance of Game_Logistics each time it's called.
You'll also see that inside Game_Logistics, this is being used everywhere rather than Game_Logistics; this is to make sure we're referencing the correct instance of Game_Logistics rather than the constructor function.
I've also capitalized your game_logistics function to adhere to the widely-followed naming convention that constructor functions should be capitalized (more info).
Taking advantage of the prototype chain in JavaScript is recommended when you're working with multiple instances of functions. You can peruse various articles on "javascript prototypical inheritance* (e.g. this one), but for now, the above will accomplish what you need.
I want to create an object that can parse a certain filetype. I've looked at some of the files in the File API and I want my object to work about the same. So basically, what I want is this:
A function, called CustomFileParser. I want to be able to use it as the following:
var customFileParser = new CustomFileParser();
customFileParser.parsed = paresed;
customFileParser.progress = progress;
customFileParser.parse(file);
function parsed(event){
//The file is loaded, you can do stuff with it here.
}
function progess(event){
//The file load has progressed, you can do stuff with it here.
}
So I was thinking on how to define this object, but I'm not sure how to define these events and how I should do this.
function customFileParser(){
this.parse = function(){
//Do stuff here and trigger event when it's done...
}
}
However, I'm not sure how to define these events, and how I can do this. Anyone can give me a hand?
Javscript is prototype-based OOP language, not class-based like most other popular languages. Therefore, the OOP constructs are a bit different from what you might be used to. You should ignore most websites that try to implement class-based inheritance in JS, since that's not how the language is meant to be used.
The reason people are doing it because they are used to the class-based system and are usually not even aware that are alternatives to that, so instead of trying to learn the correct way, they try to implement the way that they are more familiar with, which usually results in loads and loads of hacks or external libraries that are essentially unnecessary.
Just use the prototype.
function CustomFileParser(onParsed, onProgress) {
// constructor
this.onParsed = onParsed;
this.onProgress = onProgress;
};
CustomFileParser.prototype.parse = function(file) {
// parse the file here
var event = { foo: 'bar' };
this.onProgress(event);
// finish parsing
this.onParsed(event);
};
And you can use it like so
function parsed(event) {
alert(event);
}
function progress(event) {
alert(event);
}
var customFileParser = new CustomFileParser(parsed, progress);
var file = ''; // pseudo-file
customFileParser.parse(file);
From what it sounds to me i think you need your program to look like this
function customFileParser( onparse , progress){
this.onparse = onparse;
this.progressStatus = 0;
this.progress = progress;
this.parser = function (chunk)
}
this.parse = function(){
// Do stuff of parsing
// Determine how much data is it
// Now make a function that parses a bit of data in every run
// Keep on calling the function till the data is getting parsed
// THat function should also increase the percentage it think this can be done via setTimeout.
// After every run of the semi parser function call the progress via something like
this.parser();
if(progressStatus <100){
this.progress(this.progressStatus);
}else{
this.parsed();
}
}
}
and u can create instance of that object like
var dark = new customFileParser( function () { // this tells what to
do what parsed is complete } , function (status) { // this tells what
to do with the progress status } ) ;
using the method i suggested. you can actually define different methods for all the instances of the object you have !