JavaScript object stores previous values even in new object - javascript

JavaScript really behaves weirdly in case of Objects. Although I'm not sure if it is the correct behaviour.
Inside a new Object(), I set some properties in the object. Next time when I do again new Object(), instead of default values I get values set in previous instance. Argh.
Below example explains the problem clearly
function testo() {}
testo.prototype = {
obj: {
what: {
value: 5
}
},
done: function () {
console.log(this.obj.what.value);
this.obj.what = {value: 10};
}
};
var x = new testo();
x.done();
var y = new testo();
y.done();
The output of above code is:-
5
10
I was expecting it to be:-
5
5
Why? Because I'm creating new Class() and in the previous instance I had set the value using 'this', it is not static and default properties of all objects inside it should show up.
I have created above example as the demo. I'm facing this issue in my library. I know it has to do with objects are stored as the reference.
How should I proceed to get the expected output? Any thoughts?

You could move the prototype property (which is for all instances the identical) to just a this object in the class.
function testo() {
this.obj = { what: { value: 5 } };
}
testo.prototype = {
done: function () {
console.log(this.obj.what.value); this.obj.what = { value: 10 };
}
};
var x = new testo();
x.done();
var y = new testo();
y.done();

Related

declaring array in javascript object crashes browser

I'm trying to write some objects using JavaScript. I have 1 small object that appears to be working but this object needs a multi-dimensional array.
When I try to use this snippet of this object my browser crashes...
Are there any good tutorials on how to write objects in javascript?
function map(sizeX, sizeY)
{
var cityArr = new Array();
this.sizeX = sizeX;
this.sizeY = sizeY;
this.generateCity = generateCity;
var cityXY = new Array();
function generateCity(cityNum)
{
alert(cityNum);
}
}
When I call this, it fails when I add the call to generateCity method my browser cashes.
var objMap = new map();
//objMap.generateCity(2);
What am I doing wrong?
First off, some JavaScript best practices:
use [] to create a new array, not new Array(),
use capital letters for constructor functions, so function Map(...) {...}, not map(...),
don't reference functions before declaring them, so put your function generateCity(cityNum) before your this.generateCity = generateCity,
use console.log, not alert,
if you're building an object, define your object as one by putting its function on its prototype.
That means doing this:
function Map(sizeX, sizeY)
{
this.sizeX = sizeX;
this.sizeY = sizeY;
// you're not using these arrays. Why are they in your example code?
var cityArr = [],
cityXY = [];
}
Map.prototype = {
sizeX: 0, // Some default values, shared by all instances
sizeY: 0, // unless overriden in the constructor function.
generateCity: function(cityNum) {
// let's not block the entire page by using alert.
console.log("cityNum: " + cityNum);
}
}
So, with that said, this code works just fine, as can be seen on http://jsfiddle.net/mtr24 (run with your console open, and you'll see "cityNum: 2" being printed.

How are properties shared across instances using javascript prototypes

My understanding of prototypical inheritance is every object has a prototype property. If a property doesn't exist on an object then it's prototype object is checked, so on and so on up the chain.
In this example my prototype object is a simple object with a counter property.
I'm expecting every instance to share the same prototype but they appear to get new instances. The output of this code is 00, I was expecting 01.
Am I missing something obvious?
"use strict";
var ConstructorFunction = function () {};
ConstructorFunction.prototype = {
counter: 0,
count: function () {
return this.counter++;
}
};
var a = new ConstructorFunction();
var b = new ConstructorFunction();
$("#output").append(a.count());
$("#output").append(b.count());
Here is the jsfiddle: http://jsfiddle.net/hdA6G/5/
It is true that the prototype properties are shares across all instances. The problem is that you never change the prototype property. Take a look at your fiddle with some extra logs:
"use strict";
var ConstructorFunction = function () {};
ConstructorFunction.prototype = {
counter: 0,
count: function () {
return ++this.counter;
}
};
var a = new ConstructorFunction();
var b = new ConstructorFunction();
$("#output").append(a.hasOwnProperty("counter") + " "); //false
a.count();
$("#output").append(a.hasOwnProperty("counter") + " "); //true
As you see, as soon as you call ++this.counter, a local property will be created which will be used from that on.
I assume that this is what happens:
++this.counter is interpreted as this.counter = this.counter + 1. First, the part right of the equal sign is evaluated and since your instance doesn't have a counter property, the counter property of the prototype is used. The value of this property will be added to 1 and then assigned to this.counter, which now creates a local property, in the same way that it does, when you assign a property that hasn't been there at all, like a.xy = 1. xy will be a local property of the instance in that case.
EDIT
There are two workarounds that still would let you use the prototype property:
1) explicitly set the prototype property inside of the count method:
ConstructorFunction.prototype.count = function() {
return ++this.constructor.prototype.counter;
};
2) call the count method with apply and use the prototype as context:
a.count.apply(a.constructor.prototype);
BUT, if you set the prototype property the way you did, you will get problems with both of these methods.
ConstructorFunction.prototype = {
//...
};
This overrides the complete prototype object and therefore also the constructor property of it. The constructor property will now point to the next higher object in the prototype chain, the Object object. To fix that, you could either set the constructor manually after you assigned the prototype object:
ConstructorFunction.prototype.constructor = ConstructorFunction;
or assign every property of the prototype object seperately:
ConstructorFunction.prototype.counter = 0;
ConstructorFunction.prototype.count = function () {
return ++this.counter;
};
Consider using a closure so you can keep that variable private and shared across instances:
var Class = (function ClassModule() {
var counter = 0;
function Class() {}
Class.prototype = {
count: function() {
return counter++;
}
};
return Class;
}());
var class1 = new Class();
var class2 = new Class();
console.log(class1.count()); //=> 0
console.log(class2.count()); //=> 1
The counter variable is kept in context with the closure (module pattern), and won't be duplicated with each new instance.
In prototype based OOD, sharing means to share the same definition and use it for different contexts. If you need a static property to be shared between contexts you can do as given below
var ConstructorFunction = function (context) {
this.count = function () {
return context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
};
ConstructorFunction.prototype.counter = 0;
ConstructorFunction.staticCounter = 0;
var context1 = new ConstructorFunction("context1");
var context2 = new ConstructorFunction("context2");
$("#output").append(context1.count());
$("#output").append(" ");
$("#output").append(context2.count());
http://jsfiddle.net/hdA6G/1/
and a better way to define it
var ConstructorFunction = function (context) {
this.context = context;
this.counter = 0;
};
ConstructorFunction.staticCounter = 0;
ConstructorFunction.prototype.count = function () {
return this.context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
http://jsfiddle.net/hdA6G/3/
You have your inheritance backwards, the prototype inherits from the base object. I think this is more similar to what you are trying to accomplish.
function MyObject () {
this.count = 0;
};
ConstructorFunction.prototype = {
counter: function () {
return this.count++;
},
print: function () {
return this.count;
}
};
Var Foo = new MyObject();
console.log(Foo.counter()); // 1
console.log(Foo.counter()); // 2
console.log(Foo.print()); // 2
I hope this helps.
edit
If you want your counter shared across all instances then it should go in you base object.
function MyObject () {
this.count = 0;
this.counter = function () {
return this.count++; //may not need the this
}
};
I know this is an old post, but I am new to Javascrpt and while experimenting I came across something similar, so wanted to put in my 2 cents.
It seems every object gets its own copy of variables declared in prototype once it is instantiated using new. From then on, accessing using this will do the usual look up and find the private copy which is operated upon.
However, if you create a prototype variable after creation of all objects, that variable will be shared and behaves like static. I think it is fairly simple to reason why this happens, but, nonetheless, I found this to be an interesting hack. I am not sure if this is a bug in technical specs that might be resolved in future versions, or side effect of standard behavior, so don't know if this is reliable. I am not even sure if this is a newly introduced 'feature' in later versions of the language. Actually, I started googling about this fact and came across this post.
Try this code.
var Worker = function (name) {
this.name = name;
}
Worker.prototype.jobs = 0;
Worker.prototype.Work = function () {
console.log("jobs finished", this.name, ++this.jobs);
}
Worker.prototype.WorkCount = function () {
console.log("work count", this.name, this.workCount);
}
var ca = new Worker("ca");
var cb = new Worker("cb");
ca.Work();// shows 1
cb.Work();// shows 1
ca.WorkCount();// shows undefined
cb.WorkCount();// shows undefined
Worker.prototype.workCount = 2;
ca.WorkCount();// shows 2
cb.WorkCount();// shows 2

How to reset na object property to the default one?

I am using jQuery and I am still pretty new to JavaScript. I am implementing an object as the following:
MyObject = {
properties : [{}],
resetProperties: function resetProperties() { this.properties = [{}] }
};
As you can see in the above code I can reset the properties by running MyObject.resetProperties() but, in order to do that, I state two times the [{}] variable. How should I accomplish the same thing without repeating that code?
Update
I tried to do the following:
MyObject = {
properties : this.propertiesDefault,
resetProperties : function resetProperties() { this.properties = [{}] },
propertiesDefault: [{}]
};
but I get "TypeError: invalid 'in' operand MyObject.properties" and I am not sure that is the right way to proceed.
It seems to me that it would be impossible to avoid having your default / reset properties as a separate object to the one that will be modified.
I would recommend having a default value, and cloning it in your initialisation and reset function. Since you tagged your question with jQuery, I assume you are happy to clone the object with that:
MyObject = {
defaultProperties : [{}],
properties : jQuery.extend(true, {}, this.defaultProperties),
resetProperties: function() {
this.properties = jQuery.extend(true, {}, this.defaultProperties);
}
};
See this Stack Overflow question for more information on cloning objects:
What is the most efficient way to deep clone an object in JavaScript?
This is the documentation for jQuery.extend:
http://docs.jquery.com/Utilities/jQuery.extend
From what I know this isn't possible. You're going to have to hard-code the property reset. I tried setting a variable cache outside the object, but when I reset the property it unfortunately maintains its value.
var obj = {
p: [ {} ],
r: function() { this.p = this.cache; }
};
obj.cache = obj.p; // attempt to set to original
obj.p[0].m = 5; // modify
obj.r(); // reset
--------
>>> obj.p[0].m; // 5
We can assume the the cache property is being modified in the same way as p is. Therefore, we can't reset like that.
Depends on what you want. Since you're new to javascript, you may be unfamiliar with using functions to create custom objects, which is the general javascript "OOP" kinda way to do it.
function MyObjectClass() {
this.properties = null;
this.resetProperties();
}
MyObjectClass.prototype.resetProperties = function () { this.properties = [{}] };
var MyObject= new MyObjectClass();
But we don't really know that function MyObject needs to fulfill. There may be a requirement that it NEEDs to be a plain old javascript object. Or maybe not, and you're done.
Of course, you can always directly:
MyObject = {
properties : null,
resetProperties: function () { this.properties = [{}];}
};
MyObject.resetProperties();

variable is undefined, but I have defined as property in class

I have the following code:
Javascript:
slideShow = {
ImgsFolder: "images/",
ImgsSrc: ['img.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg'],
MainElem: document.getElementById('SlideShow'),
ImgElem: (this.MainElem.firstElementChild) ? this.MainElem.firstElementChild : this.MainElem.firstChild
doit: function(){
for (i = 0; i < this.ImgsSrc.length; i++) {
document.writeln(this.ImgsFolder + this.ImgsSrc[i] + "<br/>");
}
}
}
When print the value of ImgElem variable , gives me error message this.MainElem is undefined, and the problem in the last line.
I don' know what's the problem in this part of code
As I mentioned in a comment, it is not possible to refer to the object being created via object literal notation from within the notation itself.
You need to fully create the object before you can reference it.
slideShow = {
ImgsFolder: "images/",
ImgsSrc: ['img.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg'],
MainElem: document.getElementById('SlideShow')
}
slideShow.ImgElem = slideshow.MainElem.firstElementChild ||
slideshow.MainElem.firstChild
To refer to the object during creation, you need a constructor function.
function SlideshowMaker() {
this.ImgsFolder = "images/",
this.ImgsSrc = ['img.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg'],
this.MainElem = document.getElementById('SlideShow')
this.ImgElem = this.MainElem.firstElementChild ||
this.MainElem.firstChild
}
// use "new"----v----to create a new object
var slideshow = new SlideshowMaker()
In order to use the this keyword you have to instantiate object with the new keyword. Otherwise the this keyword will point to the window object in this case.
When you use the object literals such as { key: value, ... }, the object is actually created after the whole block is parsed/executed.
var a = {
// a is not defined here
key: value
};
// it is defined here
Also, you cannot use this as a reference to the object you are about to create. this can only be used in methods of an object (functions bound to the object or executed in their context).
You have two options:
1.) You need to either create a getter function such as
var a = {
b: value,
c: function () { return this.b.c; }
};
and call it in order to access b.c: a.c()
2.) The better way in your case is to define the property ImgElem after the object is actually created:
var slideShow = {
MainElem: document.getElementById('SlideShow')
};
slideShow.ImgElem = slideShow.MainElem.firstElementChild || slideShow.MainElem.firstChild;
Notice the usage of slideShow instead of this.
Maybe you have missplaced some parentheses?
var slideShow = {
ImgsFolder: "images/",
ImgsSrc: ['img.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg'],
MainElem: document.getElementById('SlideShow'),
ImgElem: (this.MainElem.firstElementChild ? this.MainElem.firstElementChild : this.MainElem.firstChild)
}

What does this JavaScript mean?

I am working on a legacy application and all JS seems mystrious to me.
Here is the few mysterious lines which are loaded before all and I don't have any idea what they are doing.
var i2b2 = {sdx:{TypeControllers:{},Master:{_sysData:{}}},events:{},hive:{cfg:{},helpers:{},base_classes:{}},h:{}};
if (undefined==i2b2.hive) { i2b2.hive = {}; }
i2b2.hive.tempCellsList = [
{ code: "PM",
forceLoading: true
},
{ code: "ONT" },
{ code: "CRC" },
{ code: "WORK"},
{ code: "SHRINE"},
{ code: "PLUGINMGR",
forceLoading: true,
forceConfigMsg: { params: [] }
}
];
There are many more var and if statements but they are doing same thing with different variables.
Please help me to solve this mystery.
The first line initialises i2b2 using nested object literals.
var obj = {}; is a shorter way of writing var obj = new Object();
A simple object literal will be
var simpleObject = {
property1: "Hello",
property2: "MmmMMm",
property3: ["mmm", 2, 3, 6, "kkk"],
method1: function() {
alert("my method")
}
};
A nested one will be
var rectangle = {
upperLeft: {
x: 2,
y: 2
},
lowerRight: {
x: 4,
y: 4
}
};
Yours is a classic.
var i2b2 = {
sdx: {
TypeControllers: {},
Master: {
_sysData: {}
}
},
events: {},
hive: {
cfg: {},
helpers: {},
base_classes: {}
},
h: {}
};
The second line should be IMHO
i2b2.hive = i2b2.hive || {};
This just says that if hive is undefined create a new object.
The last lines create a property tempCellsList to the object hive. ( Please note that hive in turn is a property of i2b2 )
Lastly a new array of objects are added to the property tempCellsList
This javascript code creates a variable called ib2b that has a number of properties: sdx, events, hive, etc. Those properties hold more composite objects, which are constructed below.
The idea is that this global object can be referenced from other javascript code and it stores global configuration for the client-side application.
I'm not quite sure, what exactly you don't understand. There are two "strange" points about the code above, which I'll try to explain, but if that's not enough you will need to describe better what you don't understand:
The code checks is i2b2.hive is is undefined and set it as an empty object, if it is. Since the property is obviously set in the previous line, my guess is that this code is generated dynamically and some of the logic (such as this check) is defined in the JavaScript code even if it could (should?) be the the server side code.
undefined==i2b2.hive is a bad/wrong way to test "undefinedness", because undefined is not a reserved word in JavaScript.This just works, because undefined is just a variable that - by chance - happens to be undefined. Instead one should use if (typeof i2b2.hive == "undefined") ... or just if (i2b2.hive) ....
It seems like it's setting a few parameters in that i2b2 object. It doesn't "do" anything in itself, but it seems to set a few basic configuration settings for further execution. Try and look for similar occurrences in the code further below.
E.g it sets i2b2.hive.tempCellList[5].forceLoading to true. Later on the application probably has if-conditions, such as
for(var i in i2b2.hive.tempCellList)
{
if(i2b2.hive.tempCellList[i].forceLoading === true)
{
// do something...
}
}

Categories

Resources