I didn't have a understanding on
difference between intializing a
variable with {} and a named-function
with new keyword. I mean which
practice should I use to give a
definition of an object. Which is more
appropiate and for which case?
Then I made a little example to test
both practices. And
I found a very simple difference.
Whenever you intialized an
variable with {}, that variable is
the only reference of this object
definition given in {}. {} itself
doesn't have a name so it can't be
called to intialized with new. Only a
reference is avaliable to get it.
So it seems we can easily implement
singleton pattern on objects using {}.
What I see you can't have more than
one instances with {} not even you can
apply clone if you do you will get
only a reference of that object.
Am I assuming a correct behavior of
{}?
var A = {
B : 0
};
// A is an object?
document.write("A is an " + typeof A);
Lets try to clone object A
var objectOfA = new Object(A);
objectOfA.B = 1;
//Such operation is not allowed!
//var objectOfA = new A();
var referenceOfA = A;
referenceOfA.B = -1;
document.write("A.B: " + A.B);
document.write("<br/>");
The above referenceOfA.B holds a reference of object A, so changing the value of referenceOfA.B surely reflects in A.B.
document.write("referenceOfA.B: " + referenceOfA.B);
document.write("<br/>");
If successfully cloned then objectOfA should hold value 1
document.write("objectOfA.B: " + objectOfA.B);
document.write("<br/>");
Here are the results:
A is an object
A.B: -1
referenceOfA.B: -1
objectOfA.B: -1
This may be of use, excerpt:
CatNames.instance = null; // Will contain the one and only instance of the class
// This function ensures that I always use the same instance of the object
CatNames.getInstance = function() {
if (CatNames.instance == null) {
CatNames.instance = new CatNames();
}
return CatNames.instance;
}
Note: you should not clone singletons.
A is already an object, so new Object(A) just returns A. You can prove this by running
var c = {};
alert(c === new Object(c));
So no cloning is going on.
What are you actually trying to do, and what does the Singleton pattern have to do with this cloning business?
For cloning objects you will have to do a bit more work. Something like below.
var a = {
val:1,
clone : function(){
return {val: a.val, clone : a.clone}
}
};
var b = a.clone();
b.val = 2;
console.log(a);
console.log(b);
Now you can clone an object and change it values. If you want to clone more complex objects, you could write a recursive function for this.
You can use these object literals as either static classes or as objects with key/value pairs.
If you want to use non static classes (sort of), use the following:
var MyClass = new function(){}
MyClass.prototype = {
val : 1
};
var a = new MyClass();
Hope this helps.
Related
I have created an array like prototype:
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
now i create an instance:
var aInst = new METracker('a', 'b', 'c');
Now I want to clone it so I do:
var cloneInst = aInst.slice();
however cloneInst no longer has the method .myMethod is there a way to keep the prototype on the clone?
Thanks
If you're going to create your own array-alike, the trick is to extend an array instance, not the array prototype:
function MyAry() {
var self = [];
[].push.apply(self, arguments);
// some dark magic
var wrap = 'concat,fill,filter,map,slice';
wrap.split(',').forEach(function(m) {
var p = self[m];
self[m] = function() {
return MyAry.apply(null, p.apply(self, arguments));
}
});
// add your stuff here
self.myMethod = function() {
document.write('values=' + this.join() + '<br>');
};
return self;
}
a = new MyAry(11,44,33,22);
a.push(55);
a[10] = 99;
a.myMethod()
b = a.sort().slice(0, 4).reverse();
b.myMethod();
Basically, you create a new array (a normal array, not your object), wrap some Array methods so that they return your object instead of generic arrays, and add your custom methods to that instance. All other array methods and the index operation keep working on your object, because it's just an array.
I have created an array like prototype:
No, you haven't.
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
The METracker constructor does nothing at all, it will just return a new Object with Array.prototype on its [[Prototype]] chain.
var aInst = new METracker('a', 'b', 'c');
Just returns an instance of METracker, it has no data since the constructor doesn't do anything with the arguments passed. Assigning Array.prototype to the inheritance chain doesn't mean the Array constructor is invoked.
var cloneInst = aInst.slice();
Note that callling slice() on aInst just returns a new, empty array. aInst doesn't have a length property, so the algorithm for slice has nothing to iterate over. And even if aInst had properties, slice will only iterate over the numeric ones that exist with integer values from 0 to aInst.length - 1.
If you want to create a constructor that creates Array–like objects, consider something like:
function ArrayLike() {
// Emulate Array constructor
this.length = arguments.length;
Array.prototype.forEach.call(arguments, function(arg, i) {
this[i] = arg;
}, this);
}
ArrayLike.prototype = Object.create(Array.prototype);
var a = new ArrayLike(1,2,3);
document.write(a.length);
document.write('<br>' + a.join());
The above is just play code, there is a lot more to do. Fixing the length issue isn't easy, I'm not sure it can be done. Maybe there needs to be a private "fixLength" method, but methods like splice need to adjust the length and fix indexes as they go, so you'll have to write a constructor that emulates the Array constructor and many methods to do re–indexing and adjust length appropriately (push, pop, shift, unshift, etc.).
jQuery has made my life easier but I'm still very beginner to JavaScript. So, may be, I'm asking a very stupid question here:
var t = {
rows: 3,
columns: 5,
getCellCount: function () {
return this.rows * this.columns;
}
};
var tn = t;
tn.rows = 6;
document.write(tn.rows + " , " + t.rows); // returns 6 , 6
I also tried var tn = new t(); // but seems wrong
So, How to retrieve old intrinsic value from object so that it results 6 , 3
tn and t are both pointing to the same object, that's why when you change tn.rows it also changes t.rows. There is no such thing as old intrinsic value.
You must copy the object in order to keep the old value. There are two: shallow copy and deep copy.
Copying the key-value pairs is pretty easy with Object.create.
var newObj = Object.create(oldObj);
Now if you change the values in newObj, it will not change the original one:
var a = {a:1}, b = Object.create(a);
b.a = 2;
console.log(a.a, b.a); //1,2
However, to perform a complete copy is really complicated. See more: How do I correctly clone a JavaScript object?
PS: The new keyword you mentioned is for creating an object as in classes.
function Car(n, y){ //This is called a "constructor"
var name = n, year = y; //Private variables
return {
getName: function(){ return name; }, //Getters
getYear: function(){ return year; }
};
}
var myNewCar = new Car("Toyota", 2010);
myNewCar.getName(); //"Toyota"
myNewCar.getYear(); //2010
(This is also how you create objects in Java, if you have taken those CS courses you would recognize this pattern.)
var tn = t;
simply makes both tn and t to point to the same object in memory. So, change in one object will reflect in other as well. You can clone the object, like this
function copyObject(sourceObject) {
var result = {};
for (var key in sourceObject) {
if (sourceObject.hasOwnProperty(key)) {
result[key] = sourceObject[key];
}
}
return result;
}
var tn = copyObject(t);
You are asking to clone a JSON object.
var tn={};
for (key in t) tn[key]=t[key];
There may be other "prettier" ways, but this guarantees the clone.
The = operator in javascript just changes what the object points to, so it will not create a copy of the original object. You can take a look here to see possible ways to create a clone of the object.
If you want to create a Javascript object, the conventional way is to create a function:
// Use capitalized names for Object-creating functions.
// I guessed that you wanted a matrix of some sort.
function Matrix(rows, columns) {
this.rows = rows;
this.columns = columns;
this.cellCount = function() {
return this.rows * this.columns;
}
this.dimensions = function() {
return this.rows, + ", " + this.columns;
}
this.copy = function() {
return new Matrix(this.rows, this.columns);
}
}
var t = new Matrix(6, 3);
There are more sophisticated ways to do this, using Object.create. Look at Javascript: The Good Parts.
Why is the result {"a":"b","c":1}?
var foo = {"a":"b","c":0};
var bar = foo;
bar.c++;
alert(JSON.stringify(foo));
How to disable this behavior?
Both foo and bar variables reference the same object. It doesn't matter which reference you use to modify that object.
You cannot disable that behaviour, this is how JavaScript and many other major languages work. All you can do is to clone the object explicitly.
var foo = {"a":"b","c":0};
var bar = {"a":foo.a, "c": foo.c};
bar.c++;
What you're doing is making a second reference to an object but what it seems you want is a copy of that object instead.
If you want that functionality then you really want a copy function that copies all of the properties, one by one, into a new object:
// Take all the properties of 'obj' and copy them to a new object,
// then return that object
function copy(obj) {
var a = {};
for (var x in obj) a[x] = obj[x];
return a;
}
var foo = {"a":"b","c":0};
var bar = copy(foo);
bar.c++;
alert(JSON.stringify(foo));
and you'll get {"a":"b","c":0}
First, Javascript doesn't pass pointers, it passes references, slightly different. Secondly, there's no way to modify Javascript's default behavior, unfortunately fortunately.
What you might want to do is create a constructor and use that to create two similar, but separate instances of an object.
function Foo(a, b) {
this.a = a;
this.b = b;
}
var bar1 = new Foo(0, 0);
var bar2 = new Foo(0, 0);
bar2.b++;
console.log(bar1);
console.log(bar2);
>> {a:0, b:0};
>> {a:0, b:1};
You can't disable the way javascript works.
If you change a reference object, it effects all the object references...
Is there a way of making an associative array where each key is a hash of several objects? I'm not interested in inspecting each object's state, but rather the object's identity.
var myarray = {};
var a = new A();
var b = new B();
var c = new C();
// + is not right, but illustrates the hashing I'm after.
myarray[a + b + c] = 42;
The + operator is not right. In java I would arithmetically combine the System.identityHashCode() for each of these three instances and use the result to make my new hash key. Is there some similar mechanic in javascript?
Overriding the .toString() method in A, B and C is not an option since I'm interested in object identity, not state.
Actually impossible since Object keys in this language only can be strings and there's no equivalent of java's object identity.
:o)
You could overwrite the toString() method of the prototypes to create a unique hash for each instance. E.g.
A.prototype.toString = function() {
return /* something instance specific here */;
};
Even a + b + c would work then.
Update: Afaik, you cannot get an instance unique id (whatever that is) in JavaScript. You could however assign each instance some identifier.
This only works if you are creating the objects.
E.g.
var addIdentityTracker = (function() {
var pad = "0000000000",
id = 1;
function generateId() {
var i = (id++).toString();
return pad.substr(0, 10 - i.length) + i;
}
return function(Constr) {
var new_constr = function() {
this.___uid = generateId();
Constr.apply(this, arguments);
};
new_constr.prototype = Constr.prototype;
new_constr.prototype.toString = function() {
return this.___uid;
};
return new_constr;
};
}());
and then do:
A = addIdentityTracker(A);
var a = new A();
I'd suggest just assigning a unique ID to each object. Javascript doesn't come with a built-in unique ID mechanism, but you can assign a unique ID to any object you want and then use it as such. For example, you could do this:
// getUniqueID is a function that returns a unique ID for any javascript object.
// If no uniqueID is already present on the object, it coins one using a global
// counter and then stores it on the object.
// So that the uniqueID can be combined with other uniqueIDs easily and still
// create a unique union, the uniqueID here is a unique 10 character string.
// There is no randomness in the IDs as they are only required to be unique
// within the page, not random or unique in the universe. The monotomically
// increasing counter guarantees uniqueness within the page.
// Two globals we need for generating the unique ID
var idCntr = 0;
var controlStr = "0000000000"; // 10 digits long
function getUniqueID(o) {
if (!o.uniqueID) {
var base = idCntr++ + ""; // get string version of idCntr
o.uniqueID = controlStr.slice(0, controlStr.length - base.length) + base; // zero pad
}
return(o.uniqueID);
}
var myobj = {};
var a = new A();
var b = new B();
var c = new C();
myobj[getUniqueID(a) + getUniqueID(b) + getUniqueID(c)] = 42;
For you to fetch the same object back in the future, you'd have to combine the objects in the proper order again. If that wasn't easy, then you could make sure and always combine them in numeric order with the lowest numbers first so you always got a consistent order.
I'm faced with a situation in JavaScript when I need to update an object via its pointer similar to С++ array of pointers to objects
Example code for my issue:
var foo = new Array();
var bar = function(){
this.test = 1;
foo.push(this); // push an object (or a copy of object?) but not pointer
};
var barInst = new bar(); // create new instance
// foo[0].test equals 1
barInst.test = 2;
// now barInst.test equals 2 but
// foo[0].test still equals 1 but 2 is needed
So, how can I solve this? Should I use a callback or something like this or there is an easy way to help me to avoid copying the object instead pushing the raw pointer into an array?
JS is pass-by-value, so your original assignment was this.test = the value of 1, in my example, it's this.test = the object pointed to by ptr, so when I change ptr this.test changes as well.
var foo = [],
ptr = {val: 1},
bar = function(){
this.test = ptr;
foo.push(this); // push an object (or a copy of object?) but not pointer
},
barInst = new bar(); // create new instance
// foo[0].test.val equals 1
ptr.val = 2;
// foo[0].test.val equals 2
Although if you thought that foo.push(this); was similar, it isn't. Since this is an object, the array will indeed contain "raw pointers" to objects, just like you want. You can prove this simply:
foo[0].test = 3;
// barInst.test === 3
Which shows that it is indeed a pointer to the object that was pushed onto the array
"create object method pointer"
Object.defineProperty(Object.prototype,'pointer',{
value:function(arr, val){
return eval(
"this['"+arr.join("']['")+"']"+
((val!==undefined)?("="+JSON.stringify(val)):"")
);
}
});
ex of use
var o={a:1,b:{b1:2,b2:3},c:[1,2,3]}, arr=['b','b2']
o.pointer(arr) // value 3
o.pointer(['c',0], "new_value" )