I'm going to be getting an array of objects and want to set instance variables inside of a class based on a property. So if I get this:
ary = [{type: 'walrus', name: 'GorbyPuff'}, {type: 'humanoid', occupation: 'KingSlayer'}]
I want to initialize an object where #walrus == ary[0] and #humanoid == ary[1]
In Ruby I could user instance_variable_set, but how can this be accomplished in the Javascripts?
I'm not sure if I get what you're trying to acchieve, but the easiest way to do this would be:
var theObj = {};
for(var i=0;i<ary.length;i++)
{
theObj[ary[i].type] = ary[i];
}
The worry here is, that by altering the ary variable you will inadvertently alter the theObj:
console.log(theObj.walrus.name);//Outputs: GorbyPuff
ary[0].name = 'Nips!';
console.log(theObj.walrus.name);//Outputs: Nips! <-- objects are passed by reference, always
If the ary variable is part of a function scope, and the resulting object is its return value, you needn't worry. But if both are part of the global scope (Which they shouldn't, it's bad practice), this becomes an issue.
I therefore propose this approach:
var obj = {};
var i;
while (ary.length !== 0)
{
i = ary.splice(0,1)[0];//removes element from array
if (i.hasOwnProperty('type'))//always best to check the property you're going to use is there
{
obj[i.type] = i;
}
}
There's nothing in JS that can do this for you, just do a loop to build the object you want:
ary = [{type: 'walrus', name: 'GorbyPuff'}, {type: 'humanoid', occupation: 'KingSlayer'}]
instances={}
for(x=0;x<ary.length;x++) instances[ary[x].type]=ary[x]
document.write(instances.walrus.name) //GorbyBuff
document.write(instances.humanoid.occupation) //KingSlayer
If you want to use that array of objects as prototypes, you can do this:
var Walrus = function(){};
Walrus.prototype=ary[0];
var aWalrus = new Walrus(); // creates a new Walrus. aWalrus.name => GorbyPuff
In Javascript the Good Parts, Douglas Crawford describes a more general way of doing it:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
Which you can use like this:
var aWalrus = Object.create(ary[0]);
here is a example of what you want:
// the class:
function MyClass(){
// stuff
}
// the data object
var o = [
{type:"MyClass",name:"a name"}
]
// how to instantiate:
var instances = [];
for(var i=0;i<o.length;i++){
if(typeof this[o[i].type] == "function")
instances.push(new this[o[i].type](o[i].name))
}
If you create the classes in a function you need to use "this" as a reference to that function, else you can use "window"
Related
I know all about JSON.stringify or JSON.parse in the sense that one serializes an object and one deserializes the string back into an object. This is great!
However, I have the following situation:
var i = new MyMagicalObject();
var oi = JSON.parse(JSON.stringify(i));
console.log(i.numFields()); // this is fine
console.log(oi.numFields()); // this throws since Object has no method 'numFields'
Basically, I'd like to treat oi as an instance of "MyMagicalObject" since that's what it is.
I'm sure there's some magic about setting the prototype on oi or something, but I'm fairly new to JavaScript. Any help would be appreciated.
You can't "store" JavaScript functions in JSON strings.
The only data types that can be stored in JSON are:
Number
String
Boolean
Array
Object
null
(source)
Anything that isn't one of those types, gets ignored:
function Test(){
this.foo = function(){
return 'bar';
}
this.theAnswer = '42';
}
var t = new Test();
alert(t.foo());
alert(JSON.stringify(t))
Your problem could be easily solved by redesigning your MyMagicalObject class. Here is an example of JSON-friendly class:
function MyMagicalObject(props) {
this.props = props || {};
}
MyMagicalObject.prototype.get = function(key) {
return this.props[key];
};
MyMagicalObject.prototype.set = function(key, val) {
this.props[key] = val;
return this;
};
MyMagicalObject.prototype.toJSON = function() {
return this.props;
};
MyMagicalObject.prototype.numFields = function() {
return Object.keys(this.props).length;
};
This realization follows two rules:
It's constructor accepts JSON representation as a first argument.
It provides toJSON method to tell JS engine how to convert its instance to JSON.
Check the following example:
var obj = new MyMagicalObject();
obj.set('foo', 42).set('bar', 'baz');
alert(obj.numFields()); // 2
var str = JSON.stringify(obj);
var obj2 = new MyMagicalObject(JSON.parse(str));
alert(obj2.numFields()); // 2
You can create a new MyMagicalObject() and then overwrite its properties with the one from oi.
var t = new MyMagicalObject();
for(var k in oi) t[k]=oi[k];
That should do the trick. If you have a more complex object (with more than 1 dimension), search for a copy function that deep copies all properties.
Add oi.prototype = MyMagicalObject.prototype; after line 3.
or
create a new object and copy the properties:
var oi2 = new MyMagicalObject();
for (var p in oi) {
if (oi.hasOwnProperty(p)) {
oi2[p] = oi[p]
}
}
console.log(oi2.numFields());
I am doing a project which requires to pass Perl objects to javascript via JSON. I am facing a problem in terms of "intermediate" object definition.
In Perl, object is represented by hash, and programmers don't have to define anything "in the middle". Once a property is created, all intermediate objects are automatically created as hash references. e.g.
$graph{chart}{yAxis}{title} = "Temperature Tracking";
However, once this object is passed to Javascript, if I want to add any new properties in the "intermediate" object, like:
graph.chart.xAxis.title = "Time Sequence";
I'll have an "undefined graph.chart.xAxis" error. Unlike Perl, Javascript doesn't automatically create objects if we simply assign a property for it.
At the moment I have to use below solution:
if (!graph.chart.xAxis) {
graph.chart.xAxis = {};
graph.chart.xAxis.title = "Time Sequence";
}
Unfortunately, in our project the objects passed from Perl are pretty dynamic and there are plenty of other objects that Javascript may not know. Above way makes JS code pretty lengthy and "ugly looking". Are there any better solutions to make Javascript behave like Perl, which means I don't have to create intermediate objects manually?
I'm not sure whether this meets your requirements but a simple function to create the missing objects could look like this:
function insertNode(obj, node, value) {
var segments = node.split('.');
var key = segments.pop();
var ref = obj;
while (segments.length > 0) {
var segment = segments.shift();
if (typeof ref[segment] != 'object') {
ref[segment] = {};
}
ref = ref[segment];
}
ref[key] = value;
};
var x = {};
insertNode(x, 'foo.bar.baz', 'hello world');
alert(x.foo.bar.baz); // "hello world"
Demo: http://jsfiddle.net/sNywt/1/
You may be interested in a library called steeltoe
steelToe(graph).set('chart.xAxis.title', 'Time Sequence');
not sure, if its suitable in your case, but you could check for property existence and create it if does not exist, like:
function use_or_create(obj, prop) {
return (obj.hasOwnProperty(prop)) ? true : (obj[prop] = {});
}
var graph.chart = {}; //your object
//function call to check if property exist before trying to use it
use_or_create(graph.chart, 'xAxis'); //check if xAxis exists and creates one if doesnot
graph.char.xAxis.title = "tested";
Maybe this Object extension will do:
Object.prototype.val = function(prop,val){
prop = /\./i.test(prop) ? prop.split('.') : prop;
if (prop.constructor === Array){
var objnow = this, pr;
while (pr = prop.shift()){
if (!objnow[pr]){
objnow[pr] = {};
}
if (!prop.length) {
objnow[pr] = val;
}
objnow = objnow[pr];
}
for (var l in objnow){
this[l] = objnow[l];
}
} else {
this[prop] = val;
}
}
// usage
var myO = {};
myO.val('a.b.c',3); //=> myO.a.b.c = 3
myO.val('someprop',3); //=> myO.someprop = 3
myO.val('a.b.someprop',5); //=> myO.a.b.someprop = 3
The code below does what I want, but I would like to avoid eval. Is there a function in Javascript that looks up an object by its name as defined by in a string?
myobject = {"foo" : "bar"}
myname = "myobject";
eval(myname);
Some context: I am using this for an application in which a large number of nodes in the dom has a html5 data-object attribute, which is used in the handler function to connect back to the model.
Edit: myobject is neither global nor local, it is defined in one of the parent frames of the handler.
If variables are global then:
myobject = {"foo" : "bar"};
myname = "myobject";
window[myname].foo
DEMO
For local:
(function(){
myobject = {"foo" : "bar"};
myname = "myobject";
alert( this[myname].foo );
})();
DEMO
Local Variable Solution:
You could make all objects that you want to access with a string properties of another object. For example:
var objectHolder = {
myobject: {"foo" : "bar"},
myobject2: {"foo" : "bar"},
myobject3: {"foo" : "bar"}
};
And then access your desired object like this:
var desiredObject = objectHolder["myobject"];
Global Variable Solution:
You can access global variables using a string like this:
window["myobject"];
This question is pretty old, but since it's the top result on Google for the query "javascript get object from string", I thought I'd share a technique for longer object paths using dot notation.
Given the following:
var foo = { 'bar': { 'alpha': 'beta' } };
We can get the value of 'alpha' from a string like this:
var objPath = "bar.alpha";
var alphaVal = objPath.split('.')
.reduce(function (object, property) {
return object[property];
}, foo);
// alphaVal === "beta"
If it's global:
window.foo = { 'bar': { 'alpha': 'beta' } };
Just pass window as the initialValue for reduce:
var objPath = "foo.bar.alpha";
var alphaVal = objPath.split('.')
.reduce(function (object, property) {
return object[property];
}, window);
// alphaVal === "beta"
Basically we can use reduce to traverse object members by passing in the initial object as the initialValue.
MDN article for Array.prototype.reduce
since window is a global namespace, you could simply use
window[myname]
We use a fair bit of dotted notation variable structures. Needed a way to traverse them without erroring on undefined, and return value or undefined as the answer. Hope this helps.
function getValueOrUndefined(id) {
let parent = window;
if (id.indexOf(".") !== -1) {
let current = null;
let child = undefined;
let names = id.split(".");
for (let i = 0; i < names.length; i++) {
current = names[i];
child = parent[current];
if (child === undefined)
return child;
else
parent = child;
}
return child;
}
return parent[id];
}
I have an object created from JSON via AJAX from the server. The object has several sub-objects in an array, e.g.:
obj.subObj1[0].value="abc";
obj.subObj1[1].value="abc";
obj.subObj2[0].value="abc";
Now I want to set some values in this object but I dont know if they already exist.
obj.subObj1[0].value="new Value"; // No Problem
obj.subObj2[1].value="new Value2"; // Problem because obj.subObj2[1] is no Object.
I would need to do obj.subObj2[1]={} first.
Because I have this problem very often I am looking for method to automate this. A method or class which does automatically create the needed object (or array if I use an integer).
It should be able to handle an infinite depth of such sub-objects. Like this:
var obj = TheObject();
obj.sub1.sub2[10].sub3[1].sub4='value';
Now automatically all needed sub-objects and arrays should be created.
Cannot really guarantee anything about cross-browser compatibility, but how about trying this on for size (works in Chrome):
// Safely sets value property of index of an array of an object.
function setObj(obj, subObjName, index, val) {
// Ensure the object exists
if (typeof obj == 'undefined') {
obj = new Object();
}
// Ensure the array property exists
if (typeof obj[subObjName] == 'undefined') {
obj[subObjName] = new Array();
}
// Ensure the array properties index exists
if (typeof obj[subObjName][index] == 'undefined') {
obj[subObjName][index] = {};
}
// Set the value
obj[subObjName][index].value = val;
// Return the object
return obj;
}
Example use:
<script type="text/javascript">
var obj;
obj = setObj(obj, "something", 1, "val");
setObj(obj, "something", 0, "someValue");
alert(obj.something[1].value);
alert(obj.something[0].value);
</script>
If you can assume that the referenced item in the array will be either undefined or an object it simplifies things. Of course the simple (non-automatic) way would be something like this:
if (!obj.subObj2[1]) obj.subObj2[1] = {};
obj.subObj2[1].value = "new Value2";
A not-very generic function to do it for you would be:
function setArrayObjectProp(arr, index, prop, val) {
if (!arr[index])
arr[index] = {};
arr[index][prop] = val;
}
// called as
setArrayObjectProp(obj.subObj2, 1, "value", "new Value2");
heloo
try testing the type of the array item first if its not object then equal it to the new object format {value:"new Value2"}
if(typeof(obj.subObj2[1])!='object')
{
obj.subObj2[1] = {value:"new Value2"};
}
else
{
obj.subObj2[1].value = "new Value2";
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to clone js object?
This is another way to create a javascript object (using object literal notation instead of function):
user = {
name: "Foo",
email: "bar#baz.com"
}
Is there a way to clone this object or is it a singleton?
Try this:
var clone = (function(){
return function (obj) { Clone.prototype=obj; return new Clone() };
function Clone(){}
}());
Here's what's going on.
Clone is a dummy constructor.
We assign the object we want to clone to the Clone constructor's prototype.
We call Clone using 'new', so the constructed object has the original object as its constructor's prototype aka (non-standard) __proto__.
The cloned object will share all the properties of the original object without any copies of anything being made. If properties of the cloned object are assigned new values, they won't interfere with the original object. And no tampering of built-ins is required.
Keep in mind that an object property of the newly-created object will refer to the same object as the eponymous property of the cloned object. Assigning a new value to a property of the clone won't interfere with the original, but assigning values to the clone's object properties will.
Try this in chrome or firebug console:
var user = {
name: "Foo",
email: "bar#baz.com"
}
var clonedUser = clone(user);
console.dir(clonedUser);
A detailed explanation of this cloning technique can be found here.
You can use JSON object (present in modern browsers):
var user = {name: "Foo", email: "bar#baz.com" }
var user2 = JSON.parse(JSON.stringify(user))
user2.name = "Bar";
alert(user.name + " " + user2.name); // Foo Bar
See in jsfiddle.
EDIT
If you need this in older browsers, see http://www.json.org/js.html.
I like to use this:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
then any object I want to clone can be done as:
user = {
name: "Foo",
email: "bar#baz.com"
};
var user2 = Object.create(user);
As shown in (or similar to) JavaScript The Good Parts
Most of the javascript frameworks have good support for object cloning.
var a= {'key':'value'};
var b= jQuery.extend( true, {}, a );
Object.prototype.clone = function clone(obj) {
obj = obj || this;
var new_obj = {};
for( var p in obj ) {
if ( obj.hasOwnProperty(p) ) {
if( obj[p] !== null && typeof(obj[p]) === "object" ) {
new_obj[p] = clone( obj[p] );
}
else {
new_obj[p] = obj[p];
}
}
}
return new_obj;
};
/* Example */
var foo = {
name: "Foo"
, email: "bar#baz.com"
, obj: {a:"A",b:"B"}
};
var bar = foo.clone();
bar.name = "Bar";
bar.obj.b = "C";
// foo and bar should have a different 'name'
// foo and bar should retain the same email
// foo and bar should have different values for <foo/bar>['obj']['b']
// foo and bar should have the same values for <foo/bar>['obj']['a']
console.dir(foo);
console.dir(bar);