JavaScript Exception: is not a function - javascript

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));
};

Related

Create a collection of js objects by deserialising a collection of json objects in node.js

I have an prototype class:
function temp(){
this.a=77;
}
temp.prototype.getValue = function(){
console.log(this.a);
}
and an array of json objects:
var x=[{a:21},{a:22},{a:23}];
Is there any way to instantiate an array of class temp directly using the array of json objects in a similar way to what generics helps us to achieve in Java using Jackson TypeReference.
var y= new Array(new temp());
//something similar to what Object.assign achieves for a single object
Thus can it be extended to other collection of objects like Map<obj1,obj2>,etc.
There is no built-in way to do that in Javascript. With a few assumptions about your data or about a constructor, you could fairly simply create your own function to create such an array:
// pass the constructor for the object you want to create
// pass an array of data objects where each property/value will be copied
// to the newly constructed object
// returns an array of constructed objects with properties initialized
function createArrayOfObjects(constructorFn, arrayOfData) {
return arrayOfData.map(function(data) {
let obj = new constructorFn();
Object.keys(data).forEach(function(prop) {
obj[prop] = data[prop];
});
return obj;
});
}
Or, you can create a constructor that takes an object of data and then initializes itself from that object:
// pass the constructor for the object you want to create
// pass an array of data objects that will each be passed to the constructor
// returns an array of constructed objects
function createArrayOfObjects(constructorFn, arrayOfData) {
return arrayOfData.map(function(data) {
return new constructorFn(data);
});
}
// constructor that initializes itself from an object of data passed in
function Temp(data) {
if (data && data.a) {
this.a = data.a;
}
}
You could directly fill an array with the instances of Temp with Array.from.
function Temp(){
this.a=77;
}
Temp.prototype.getValue = function(){
console.log(this.a);
}
var array = Array.from({ length: 5 }, _ => new Temp);
array[0].a = 42;
console.log(array);
In case you are using NPM, I strongly recommend linq-collections package for this kind of things.
https://www.npmjs.com/package/linq-collections

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 })
}
}

Using JSON.stringify on custom class

I'm trying to store an object in redis, which is an instance of a class, and thus has functions, here's an example:
function myClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
Is there a way to store this object in redis, along with the functions? I tried JSON.stringify() but only the properties are preserved. How can I store the function definitions and be able to perform something like the following:
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
// store in redis and retreive as stringObj again
var parsedObj = JSON.parse(stringObj);
console.log(myObj.getAttr()); //prints foo
console.log(parsedObj.getAttr()); // prints "Object has no method 'getAttr'"
How can I get foo when calling parsedObj.getAttr()?
Thank you in advance!
EDIT
Got a suggestion to modify the MyClass.prototype and store the values, but what about something like this (functions other than setter/getter):
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = function(){
this._accessCounts++;
return this._attr;
}
this.getCount = function(){
return this._accessCounts;
}
}
I'm trying to illustrate a function that calculates something like a count or an average whenever it is called, apart from doing other stuff.
First, you are not defining a class.
It's just an object, with a property whose value is a function (All its member functions defined in constructor will be copied when create a new instance, that's why I say it's not a class.)
Which will be stripped off when using JSON.stringify.
Consider you are using node.js which is using V8, the best way is to define a real class, and play a little magic with __proto__. Which will work fine no matter how many property you used in your class (as long as every property is using primitive data types.)
Here is an example:
function MyClass(){
this._attr = "foo";
}
MyClass.prototype = {
getAttr: function(){
return this._attr;
}
};
var myClass = new MyClass();
var json = JSON.stringify(myClass);
var newMyClass = JSON.parse(json);
newMyClass.__proto__ = MyClass.prototype;
console.log(newMyClass instanceof MyClass, newMyClass.getAttr());
which will output:
true "foo"
No, JSON does not store functions (which would be quite inefficient, too). Instead, use a serialisation method and a deserialisation constructor. Example:
function MyClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
MyClass.prototype.toJSON() {
return {attr: this.getAttr()}; // everything that needs to get stored
};
MyClass.fromJSON = function(obj) {
if (typeof obj == "string") obj = JSON.parse(obj);
var instance = new MyClass;
instance._attr = obj.attr;
return instance;
};
Scanales, I had the same issue and tried a technique similar to Bergi's recommendation of creating new serialization/deserialization methods...but found it didn't work for me because I have objects nested in objects (several deep). If that's your case then here's how I solved it. I wrote a base class (clsPersistableObject) from which all objects that I wanted to persist inherited from. The base class has a method called deserialize, which is passed the JSON string. This method sets the properties one by one (but does not wipe out the exist methods) and then recursively defer to the child object to do the same (as many times as necessary).
deserialize: function (vstrString) {
//.parse: convert JSON string to object state
//Use JSON to quickly parse into temp object (does a deep restore of all properties)
var tmpObject = JSON.parse(vstrString);
//objZoo2.animal.move();
//Note: can't just do something like this:
// CopyProperties(tmpObject, this);
//because it will blindly replace the deep objects
//completely...inadvertently wiping out methods on it. Instead:
//1) set the properties manually/one-by-one.
//2) on objects, defer to the deserialize on the child object (if it inherits clsPersistableObject)
//2b) if it doesn't inherit it, it's an intrinsic type, etc...just do a JSON parse.
//loop through all properties
var objProperty;
for (objProperty in tmpObject) {
//get property name and value
var strPropertyName = objProperty;
var strPropertyValue = tmpObject[objProperty]; //note: doing this .toString() will cause
if (objProperty !== undefined) {
//check type of property
if (typeof strPropertyValue == "object") {
//object property: call it recursively (and return that value)
var strPropertyValue_AsString = JSON.stringify(strPropertyValue);
//see if has a deserialize (i.e. inherited from clsPeristableObject)
if ("deserialize" in this[objProperty]) {
//yes: call it
this[objProperty]["deserialize"](strPropertyValue_AsString);
}
else {
//no: call normal JSON to deserialize this object and all below it
this[objProperty] = JSON.parse(strPropertyValue_AsString);
} //end else on if ("deserialize" in this[objProperty])
}
else {
//normal property: set it on "this"
this[objProperty] = tmpObject[objProperty];
} //end else on if (typeof strPropertyValue == "object")
} //end if (objProperty !== undefined)
}
}
it looks like you attempt to stringify a closed function. you can use ()=>{} to solve the scope problem.
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = ()=>{
this._accessCounts++;
return this._attr;
}
this.getCount = ()=>{
return this._accessCounts;
}
}
What you get back grom JSON.stringify() is a String. A string has no methods.
You need to eval first that string and then you'll be able to get the original object
and its methods.
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
---- EDIT -----
//Sorry use this:
var getBackObj = JSON.parse(stringObj);
//Not this
var getBackObj = eval(stringObj);
console.log(getBackObj.getAttr()); // this should work now

javascript / jquery - adding properties to an instantiated object

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;

Javascript: How to turn a JSON array of object back in to the Object type sharing prototypes?

If you have an array of product objects created from JSON, how would you add a prototype method to the product objects so that they all point to the same method? How would you train JavaScript to recognize all product objects in an array are instances of the same class without recreating them?
If I pull down a JSON array of Products for example, and want each product in the array to have a prototype method, how would I add the single prototype method to each copy of Product?
I first thought to have a Product constructor that takes product JSON data as a parameter and returns a new Product with prototypes, etc. which would replace the data send from the server. I would think this would be impractical because you are recreating the objects. We just want to add functions common to all objects.
Is it possible to $.extend an object's prototype properties to the JSON object so that each JSON object would refer to exactly the same functions (not a copy of)?
For example:
var Products = [];
Products[0] = {};
Products[0].ID = 7;
Products[0].prototype.GetID = function() { return this.ID; };
Products[1].ID = 8;
Products[1].prototype = Products[0].prototype; // ??
I know that looks bad, but what if you JQuery $.extend the methods to each Product object prototype: create an object loaded with prototypes then $.extend that object over the existing Product objects? How would you code that? What are the better possibilities?
For one, you're not modifying the Products[0].prototype, you're modifying Object.prototype, which will put that function on the prototype of all objects, as well as making it enumerable in every for loop that touches an Object.
Also, that isn't the proper way to modify a prototype, and ({}).prototype.something will throw a TypeError as .prototype isn't defined. You want to set it with ({}).__proto__.something.
If you want it to be a certain instance you need to create that instance, otherwise it will be an instance of Object.
You probably want something like:
var Product = function(ID) {
if (!this instanceof Product)
return new Product(ID);
this.ID = ID;
return this;
};
Product.prototype.GetID = function() {
return this.ID;
};
Then, fill the array by calling new Product(7) or whatever the ID is.
First, one problem is that prototype methods are associated when the object is created, so assigning to an object's prototype will not work:
var Products = [];
Products[0] = {};
Products[0].prototype.foo = function () { return 'hello' } // ***
Products[0].foo(); // call to undefined function
(*** Actually, the code fails here, because prototype is undefined.)
So in order to attach objects, you'll need to assign actual functions to the object:
Products[0].foo = function () { return 'hello'; };
You can create a helper function to do so:
var attachFoo = (function () { // Create a new variable scope, so foo and
// bar is not part of the global namespace
function foo() { return this.name; }
function bar() { return 'hello'; }
return function (obj) {
obj.foo = foo;
obj.bar = bar;
return obj; // This line is actually optional,
// as the function /modifies/ the current
// object rather than creating a new one
};
}());
attachFoo(Products[0]);
attachFoo(Products[1]);
// - OR -
Products.forEach(attachFoo);
By doing it this way, your obj.foos and obj.bars will all be referencing the same foo() and bar().
So, if I'm getting this all correctly, this is a more complete example of KOGI's idea:
// Create a person class
function Person( firstName, lastName ) {
var aPerson = {
firstName: firstName,
lastName: lastName
}
// Adds methods to an object to make it of type "person"
aPerson = addPersonMethods( aPerson );
return aPerson;
}
function addPersonMethods( obj ) {
obj.nameFirstLast = personNameFirstLast;
obj.nameLastFirst = personNameLastFirst;
return obj;
}
function personNameFirstLast() {
return this.firstName + ' ' + this.lastName;
}
function personNameLastFirst() {
return this.lastName + ', ' + this.firstName;
}
So, with this structure, you are defining the methods to be added in the addPersonMethods function. This way, the methods of an object are defined in a single place and you can then do something like this:
// Given a variable "json" with the person json data
var personWithNoMethods = JSON.parse( json ); // Use whatever parser you want
var person = addPersonMethods( personWithNoMethods );
You could do this...
function product( )
{
this.getId = product_getId;
// -- create a new product object
}
function product_getId( )
{
return this.id;
}
This way, although you will have several instances of the product class, they all point to the instance of the function.
Could try doing something like this (without jquery)
Basic prototypal object:
function Product(id){
this.id = id;
}
Product.prototype.getId() = function(){return this.id;};
var Products = [];
Products[0] = new Product(7);
Products[1] = new Product(8);
Products[2] = new Product(9);
alert(Products[2].getId());
IMO I found a pretty good answer right here:
Return String from Cross-domain AJAX Request
...I could serialize my
data in the service as a JSON string
and then further wrap that in JSONP
format? I guess when it comes over to
the client it would give the JSON
string to the callback function.
That's not a bad idea. I guess I would
also have the option of sending a
non-JSON string which might allow me
to just use eval in the callback
function to create new Person objects.
I'm thinking this would be a more
efficient solution in both speed and
memory usage client-side.

Categories

Resources