javascript / jquery - adding properties to an instantiated object - javascript

I am instantiating an object which builds an associative array. After it is instantiated, I want to add properties the object's members, but I'm getting an error when I attempt to do so.
So this is the object, which parses a bunch of xml and builds out the scenes and vehicles arrays:
var supertree = {
scenes: {},
vehicles: {},
init: function() {
this.parseScenes();
this.parseVehicles();
},
...
And when I instantiate the object:
supertree.init();
supertree.ready = function() {
assignSpritePos();
}
I can't add properties to it. This is what I'm trying to do:
function assignSpritePos(){
var sceneCount = 0;
var sceneLength = Object.keys(supertree.scenes).length;
for (i=0; i< sceneLength; i++){
//console.log(i);
supertree.scenes[i].index = i;
sceneCount++;
}
}
As you can see, all I'm really trying to do is store some permanent reference to its index in the overall object. How do I do this? The error I get is:
TypeError: 'undefined' is not an object (evaluating 'supertree.scenes[i].index = i')

Assigning properties to objects isn't recursive, i.e. it doesn't create objects automagically for you so you must assign each property individually.
Try this...
supertree.scenes[i] = { 'index': i };

You can't assign a property to an object that doesn't exist yet. Use this:
supertree.scenes[i] = {};
supertree.scenes[i].index = i;

Related

JavaScript Exception: is not a function

I've created two object prototypes in JavaScript. These are the object constructor functions. You can see that the Board object contains an array of PostIt objects:
function PostIt(id, msg, img, pos) {
this.id = id;
this.msg = msg;
this.img = img;
this.pos = pos.slice(); // copy of array
}
function Board() {
this.postIts = []; // Empty array of PostIt objects
}
Every prototype has its methods (getters/setters) and also Board has methods to push and pop PostIt objects from the array.
I've also implemented a mechanism in Board to save & retrieve the array by means of localStorage:
Board.prototype.save = function () {
// Clean localstorage first
localStorage.clear();
if (this.getNumPostIts() > 0) { // It array is not empty
localStorage.setItem('myboard', JSON.stringify(this.postIts));
}
};
// Retrieve array of postit objects from local storage
Board.prototype.load = function () {
this.postIts = JSON.parse((localStorage.getItem('myboard')));
};
The PostIt objects are retrieved correctly, but I'm not sure about what happens when I assign them to the this.postIts array of the Board object:
this.postIts = JSON.parse((localStorage.getItem('myboard')));
Once I've done this on loading the site, when I try to call a method for any of the postit objects in the array, I get an exception. For example, the following code after loading the document
myBoard = new Board();
if (localStorage.myboard) { // Local Storage has data
// Retrieve data from local storate and dump it to myBoard
myBoard.load();
// Display Id for every postit
for (var i=0; i < myBoard.getNumPostIts(); i++) {
var pId = myBoard.getPostItByArrayIndex(i).getId();
console.log("PostIt #%d has id = %d", i, pId);
}
}
Calling the getId() method of the PostIt object raises an exception:
jQuery.Deferred exception: myBoard.getPostItByArrayIndex(...).getId is not a function TypeError: myBoard.getPostItByArrayIndex(...).getId is not a function
I don't understand why "getId" is not a function. Is there something wrong when I call this getter method?
EDIT:
.getId() is a getter method of the PostIt object prototype:
PostIt.prototype.getId = function() {
return this.id;
};
The JSON format does not store the prototypes of the objects you convert to JSON. So when you parse those JSON strings again, the objects will just be plain objects, not related in any way to the prototype, like PostIt. By consequence prototype methods like getId are not available to those objects any more.
Similarly you would also lose the the direct methods on objects, as JSON does not provision for the function format.
How to make it work
I would first change the PostIt constructor, so that it has defaults for all arguments. More specifically, it needs some adjustment for the pos property:
function PostIt(id, msg, img, pos) {
this.id = id;
this.msg = msg;
this.img = img;
this.pos = Array.isArray(pos) ? pos.slice() : []; // default is empty array
}
And now you can use Object.assign to convert the JSON parsed objects back to PostIt objects:
Board.prototype.load = function () {
this.postIts = JSON.parse((localStorage.getItem('myboard')))
.map(x => Object.assign(new PostIt, x));
};

Merge objects dynamically - Object Property Name Issue

I'm trying to merge objects together, or add new objects to existing objects.
I've already got it working as for merge using jQuery, but it's the name of the property that wont work dynamically.
The parameter filters is an object, such as {test:123}.
When invoking filter({test:123});, I want the filter function to dynamically add objects to a global object. (and of course can't use push() since its not an array)
this.filter = function(filters) {
for (var key in filters) {
$.extend( settings.filter, {key:filters[key]} );
}
};
The problem is that "key" turns into "key" as the name of the property. When it should be "test" as the property name; I can not get the property name to be created dynamically.
The goal would be to allow the user to fire the function like this:
filter({test:123,test2:1321,test55:4})
and dynamically add objects to the global settings object without the user meddling with the object itself.
Your code does not work because key is not being interpreted as a variable when being directly set in the object.
$.extend( settings.filter, {key:filters[key]} );
Considering:
var keyName = "Test";
var badObj = { keyName: "Hello World" };
You would get undefined when calling newObj.Test because it is actually newObj.keyName.
In order to use a variable to create a property name, you need to use different syntax.
var keyName = "Test";
var newObj = {};
newObj[keyName] = "Hello World";
You could then reference and use newObj.Test to get "Hello World"
To fix the method you provided, you can adjust it to:
this.filter = function(filters) {
for (var key in filters) {
if (filters.hasOwnProperty(key)) {
var newObj = {};
newObj[key] = filters[key];
$.extend(settings.filter, newObj);
}
}
};
Keep in mind you can simplify this and just use the extend method. This would be better, unless you are looking to do your own filtering as the method name suggests.
this.filter = function(filters) {
$.extend(settings.filter, filters);
};
Demos
You should create temp obj before extend :
this.filter = function(filters) {
for (var key in filters) {
var obj = {};
obj[key] = filters[key];
$.extend( settings.filter, obj );
}
};

JavaScript class to populate object

I am trying to populate an object by using a JavaScript class. I am not even sure if I am doing it correctly, I am very new to JavaScript OOP.
var ImagesViewModel = {}; // Global object
function ImagesClass() {
this.addImage = function (iUrl) {
ImagesViewModel.push({ "ImageUrl": iUrl }) //< Error is here
}
}
var k = new ImagesClass()
k.addImage("http://www.yahoo.com")
k.addImage("http://www.xbox.com")
Basically I need an easy way to populate ImagesViewModel with multiple properties. Do I need to specify properties within ImagesViewModel? Maybe I can do all of this without having to specify a global variable?
I am getting the error
Object has no method PUSH
What you want is an array and not an object, push is a method on Array prototype and you are trying to use it on object.
Change:
var ImagesViewModel = {};
To:
var ImagesViewModel = [];
You can do it this way as well so that each instance of ImagesClass has its own set of images.
function ImagesClass() {
var _images = [];
this.addImage = function (iUrl) {
_images.push({ "ImageUrl": iUrl }) //< Error is here
}
this.getImages = function(){
return _images;
}
}
and use it as:
var k = new ImagesClass();
k.addImage("http://www.yahoo.com");
k.addImage("http://www.xbox.com");
var ImagesViewModel = k.getImages(); //You can either set it directly or as a property of object
console.log(ImagesViewModel);
Demo
the push method is only for Arrays, here you are trying to push() to an object, which is why it isn't working.
You will need to change var ImagesViewModel = {}; to var ImagesViewModel = [];
From a design perspective, you probably don't want your viewmodel to just be a flat array (even though you declared it as an object, as other posters pointed out).
I'd suggest declaring an array declaration to hold the images inside of your ImagesViewModel object.
var ImagesViewModel = { // ViewModel generic OBJECT
this.Images = new Array(); // prototype ARRAY object
};
function ImagesClass() {
this.addImage = function (iUrl) {
ImagesViewModel.Images.push({ "ImageUrl": iUrl })
}
}

Refering to an element from within an object

I am defining the following object:
var object = {
first: $('.first').eq(),
firstPosition: first.position()
}
returns first is not defined
this.first... is also undefined
What is the correct syntax?
The correct syntax is:
var ob = {
key: value,
otherKey: value
};
You cannot access the properties of an object until it has finished being constructed.
So you need to do it in multiple steps.
var object = {};
object.first = $('.first').eq();
object.firstPosition = object.first.position();
You need to define first before the object construction.
var first = $('.first').eq();
var object = {
first: first,
firstPosition: first.position()
};

Can you use custom objects as properties of an object in javascript?

Suppose I create a custom object/javascript "class" (airquotes) as follows:
// Constructor
function CustomObject(stringParam) {
var privateProperty = stringParam;
// Accessor
this.privilegedGetMethod = function() {
return privateProperty;
}
// Mutator
this.privilegedSetMethod = function(newStringParam) {
privateProperty = newStringParam;
}
}
Then I want to make a list of those custom objects where I can easily add or remove things from that list. I decide to use objects as a way to store the list of custom objects, so I can add custom objects to the list with
var customObjectInstance1 = new CustomObject('someString');
var customObjectInstance2 = new CustomObject('someOtherString');
var customObjectInstance3 = new CustomObject('yetAnotherString');
myListOfCustomObjects[customObjectInstance1] = true;
myListOfCustomObjects[customObjectInstance2] = true;
myListOfCustomObjects[customObjectInstance3] = true;
and remove custom objects from the list with
delete myListOfCustomObjects[customObjectInstance1];
but if i try to iterate through the list with
for (i in myListOfCustomObjects) {
alert(i.privilegedGetMethod());
}
I would get an error in the FireBug console that says "i.privilegedGetMethod() is not a function". Is there a way to fix this problem or an idiom in javascript to do what I want? Sorry if this is a dumb question, but I'm new to javascript and have scoured the internet for solutions to my problem with no avail. Any help would be appreciated!
P.S. I realize that my example is super simplified, and I can just make the privateProperty public using this.property or something, but then i would still get undefined in the alert, and I would like to keep it encapsulated.
i won't be the original object as you were expecting:
for (i in myListOfCustomObjects) {
alert(typeof i); // "string"
}
This is because all keys in JavaScript are Strings. Any attempt to use another type as a key will first be serialized by toString().
If the result of toString() isn't somehow unique for each instance, they will all be the same key:
function MyClass() { }
var obj = {};
var k1 = new MyClass();
var k2 = new MyClass();
obj[k1] = {};
obj[k2] = {};
// only 1 "[object Object]" key was created, not 2 object keys
for (var key in obj) {
alert(key);
}
To make them unique, define a custom toString:
function CustomObject(stringParam) {
/* snip */
this.toString = function () {
return 'CustomObject ' + stringParam;
};
}
var obj = {};
var k1 = new CustomObject('key1');
var k2 = new CustomObject('key2');
obj[k1] = {};
obj[k2] = {};
// "CustomObject key1" then "CustomObject key2"
for (var key in obj) {
alert(key);
}
[Edit]
With a custom toString, you can set the object as the serialized key and the value to keep them organized and still continue to access them:
var customObjectInstance1 = new CustomObject('someString');
var customObjectInstance2 = new CustomObject('someOtherString');
var customObjectInstance3 = new CustomObject('yetAnotherString');
myListOfCustomObjects[customObjectInstance1] = customObjectInstance1;
myListOfCustomObjects[customObjectInstance2] = customObjectInstance2;
myListOfCustomObjects[customObjectInstance3] = customObjectInstance3;
for (i in myListOfCustomObjects) {
alert(myListOfCustomObjects[i].privilegedGetMethod());
}
The for iteration variable is just the index, not the object itself. So use:
for (i in myListOfCustomObjects) {
alert(myListOfCustomObjects[i].privilegedGetMethod());
}
and, in my opinion, if you use an Object as an array index / hash, it just would be converted to the string "Object", which ends up in a list with a single entry, because all the keys are the same ("Object").
myListOfCustomObjects =[
new CustomObject('someString'),
new CustomObject('someOtherString'),
new CustomObject('yetAnotherString')
]
you will get access to any element by index of array.

Categories

Resources