Javascript nested object access root level - javascript

I have a nested object in javascript like this one:
{
nameRoot: "my object",
sub: {
nameSub: "my sub object"
}
}
I want to access nameRoot from a function defined in sub.
Using a function i would have defined something like:
var self = this;
and used self but how can I do this in a literal object?

The following code allows you to link to a parent element and avoid the parent showing up in a for-in loop.
var parent = { a: 1 };
var child = { b: 2 };
Object.defineProperty(
child, 'parent',
{ value: parent,
enumerable: false }
);
parent.child = child;
child.performAction = function() {
console.log(this.parent.a) // 1
}

So the best way to do this is w/ function scope.
function myFunc(){
this.nameRoot = "my object";
}
then you could do something like:
var func = new myFunc();
func.sub = new myFunc();
func.sub.nameRoot = "my sub object";
Obviously there are smarter ways to do it (e.g. pass the name through the function params) but this is the general pattern.

Related

Creating object dynamically using Object.create()

I have a scenario, where I need to create objects dynamically.
In my example, an object meta contains the name for the constructor function to be used during initialization Object.create().
At the moment using the following code, I am able to create the objects dynamically but the property name is not defined.
I need that property on the result;
What is wrong in my script? Do you know a better way to achieve the same result?
(function () {
var costructors = {
A: function () {
this.name = 'A';
console.log(this.name);
},
B: function () {
this.name = 'B';
console.log(this.name);
},
C: function () {
this.name = 'C';
console.log(this.name);
}
},
meta = {
A: true,
B: true,
C: true,
},
result = [];
function createObjs() {
Object.keys(meta).forEach(function (type) {
var obj = Object.create(costructors[type].prototype);
result.push(obj);
}.bind(this));
}
createObjs.call(this);
console.log(result);
})();
You haven't defined a prototype for any of the constructors, so you're not creating the name in your instances, since you're creating an object from their prototype, not from their constructor. Try
Object.create(constructors[type])
An alternative without using Object.create would be:
var obj = new costructors[type]();
instead of:
var obj = Object.create(costructors[type].prototype);
Actually, Object.create does not call the constructor function, but only creates a new object from the given prototype. Any member variables can be provided via a property object:
var obj = Object.create(
constructors[type].prototype,
{ 'name' : { value: 'A', writable: true}}
);

instantiating more than one object

in Javascript, I am creating an object like this:
var testObject = {
value: "this is my initial value",
setup: function() {
value: "foo"
}
};
Now, I would like to be able to instantiate this object, so I am trying this:
var myFirstObject = new testObject();
var mySecondObject = new testObject();
so that when I call .setup() it will change the value only for that referenced newly created object. How can I achieve this? This code does not seem to work.
You don't instantiate objects, you instantiate functions.
var testObject = function() {
this.value = "this is my initial value";
this.setup = function() {
this.value = "foo";
}
}
var myFirstObject = new testObject();
var mySecondObject = new testObject();
EDIT:
As per your comment, here's how you would bind to the DOM using functions on this object:
document.getElementById('idOfElem').addEventListener(
'click', myFirstObject.clickHandler);
Bear in mind that you won't have any guarantee that the click handler will be executed in the context of your object (i.e. in your click handler, this might not be your testObject instance). If your clickHandler intends to modify the object's instance variable in any way, it's better to ensure the context like so:
document.getElementById('el').addEventListener('click',
function() {
myObj.handleClick.apply(myObj, arguments);
});
You have numerous problems with your code. Firstly, you are trying to instantiate something, by calling a constructor function. Your testObject is not a function, so you'll cause a type error. You need to change testObject to be something along these lines:
var TestObject = function () {
this.value = "this is my initial value";
};
TestObject.prototype.setup = function () {
this.value = "foo";
};
Notice that I've used an uppercase T in that identifier... that's just best practice for a constructor function. Also notice how I've defined the setup method on the prototype. This is much more efficient than defining it as a property of the instance (with this.setup) since only one copy of the function needs to exist in memory.
Now that TestObject is a function it can be instantiated by calling it with the new operator:
var myFirstObject = new TestObject();
var mySecondObject = new TestObject();
When you call the setup method on an instance of TestObject, it will apply to that instance. In other words, the value of this inside the setup method will refer to the instance on which the method has been called:
myFirstObject.setup();
console.log(myFirstObject.value); // 'foo'
console.log(mySecondObject.value); // 'this is my initial value'
You have incorrectly defined your constructor. Try this:
function testObject() {
this.value = "this is my initial value";
this.setup = function() {
this.value = "foo"
}
};
You can then call new testObject().
The object notation your using is something you can compare with a static class.
Here is the code for what you're trying to achieve:
var testObject = function(val) {
this.value = "This is my initial value",
if (arguments[0]) {
this.value = val;
}
};
var first = new testObject(); //uses initial value
var second = new testObject("hi"); //value = hi
If you'd like to write classes using this notation take a look at this: http://ejohn.org/blog/simple-javascript-inheritance/
function yourObject(value, setup) {
return {
value: value,
setup: setup
};
}
var myFirstObject = new yourObject('a', function(){});
var mySecond = new yourObject('b', function(){});

javascript get parent nested object?

I have an object like this for example:
obj = {
subobj1: {
},
subobj2: {
func1: function(){
},
func2: function(){
}
},
subobj3: {
func3: function(){
},
func4: function(){
}
},
}
How do I call func1 from within func4 without having to call obj.subobj2.func1() ?
You can't exactly. You have no mean to know in what objects your function exists.
Note that it could be in more than one : you could have written this after your existing code :
var obj2 = {some:obj.subobj3};
So there can't be a unique link (and there is no accessible link) from a property value to the object holding it.
Now, supposing you'd be satisfied with a link made at object creation, you could use a factory to build your object :
obj = (function(){
var parent = {
subobj1: {
},
subobj2: {
func1: function(){
},
func2: function(){
}
},
subobj3: {
func3: function(){
},
func4: function(){
parent.subobj2.func1();
}
}
};
return parent;
})();
Then you can call
obj.subobj3.func4();
Demonstration
EDIT
I see you gave the tag OOP to your question. You should know that the pattern I gave is more frequently used to define modules. OOP in javascript is more often done using new and prototype, in order to enable instances sharing methods and inheritance. As you probably want modules rather than OOP, you seem to be fine, though.
See this introduction.
Here's how you can add .parent to any sub-object with a recursive init:
var obj = {
init: function() {
for (var i in this) {
if (typeof this[i] == 'object') {
this[i].init = this.init;
this[i].init();
this[i].parent = this;
}
}
return this;
},
subobj1: {
},
subobj2: {
func1: function(){
console.log('hey');
},
func2: function(){
}
},
subobj3: {
func3: function(){
},
func4: function(){
this.parent.subobj2.func1();
}
}
}.init();
obj.subobj3.func4();
With this solution you can also use .parent as many times as your nesting requires, like for instance if you had two levels of nesting:
this.parent.parent.subobjX.funcY();
http://jsbin.com/yuwiducadoma/1/watch?js,console
You can't do that directly, since there is no way to "go up" the object hierarchy like you can with ".." in a filesystem.
What you can do is have variables pointing to the subobjects or subfunctions directly, so that you don't need to go through the hierarchy to call them. The following is a common pattern for creating Javascript modules:
obj = (function(){
var obj1 = {...}
var obj2 = {...}
var func3 = function(){...};
var func4 = function(){...};
return {
subobj1: obj1,
subobj2: obj2,
subobj3: {
func3: func3,
func4: func4
}
}
}());
In this example, the inner functions can access obj1, obj2, func3 and func4 directly from their variables. The self-calling function makes so these inner variables are private and hidden from the outside and the return statement allows you to export only the functions that you want to make public.
As others have said, with a plain object it is not possible to lookup a parent from a nested child.
However, it is possible if you employ recursive ES6 Proxies as helpers.
I've written a library called ObservableSlim that, among other things, allows you to traverse up from a child object to the parent.
Here's a simple example (jsFiddle demo):
var test = {"hello":{"foo":{"bar":"world"}}};
var proxy = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
function traverseUp(childObj) {
console.log(JSON.stringify(childObj.__getParent())); // returns test.hello: {"foo":{"bar":"world"}}
console.log(childObj.__getParent(2)); // attempts to traverse up two levels, returns undefined because test.hello does not have a parent object
};
traverseUp(proxy.hello.foo);
That isn't possible. Object properties are obtain by the object through which they are set. JavaScript doesn't implicitly set the root object as global, and therefore you can't alias obj.subobj2.func1 as func1 or in any way shorter. If you find yourself calling that function excessively, try setting a variable instead.
Here is an asnwer more ActionScript like and easily to understand:
function MainObject() {
this.x = 100;
this.y = 50;
this.second = new SecondObject(this);
}
function SecondObject(p) {
this.parent = p;
}
var firstobject = new MainObject();
// Saving a reference to SecondObject.
var secondObject = firstobject.second;
// Because of parent variable, you can access second object parent without needing to have the parent object in a variable.
console.log(secondObject.parent.x);
console.log(secondObject.parent.y);
Just remember that an object can't have multiple parents.
It's impossible with your implementation of the object. Depends on your needs - the following implementation could be useful:
obj = function(){
var obj = {
subobj1: {
},
subobj2: {
func1: function(){
console.log('func1');
},
func2: function(){
}
},
subobj3: {
func3: function(){
},
func4: function(){
this.parentObject.subobj2.func1();
}
}
}
obj.subobj3.parentObject = obj;
/*
//You can also init parentObject reference of the rest of the nested objects if you need
obj.subobj1.parentObject = obj;
obj.subobj2.parentObject = obj;
*/
return obj;
}();
obj.subobj3.func4();
The idea is that nested objects can't know who is their parent, but the parent can tell its children who is he (but then init. code is needed as you can see in my implementation).
You can test it here: http://jsbin.com/eVOpITom/1/edit

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

How do you access the main obj from obj.foo.bar in javascript?

Objects in javascript throw me for a loop!
In this set up...
var obj = {
someVar: "my awesome variable",
foo: {
bar: function(){
alert(this.someVar);
}
}
};
How would I get obj.foo.bar to correctly alert the value of someVar?
Using a captured obj:
var obj = {
someVar: "my awesome variable",
foo: {
bar: function(){
alert(obj.someVar);
}
}
};
alert(obj.someVar);
There's no clever way to walk up the ancestor chain. Objects don't know where they're contained, if you're looking for some type of this.parent type of notation. There's nothing to say that an object even has a single "parent".
foo.foo = new Object();
bar.bar = foo.foo;
bar.bar.parent == ???
A function in Javascript is invoked only in the context of the object which the . operator was applied to. It is not possible to walk up the chain, since Javascript objects are not intrinsically aware of their parent objects.
The only way to do this is to have a separate reference to obj. (Either as a property of bar or a separate variable)
Here's a generalized pattern I just cooked up for upwards traversal. Depending on your needs/assumptions, you can probably drop some complexity.
var obj = (function (parent) {
var obj = {
foo: "foo",
up: function () {
return parent;
}
};
obj.bar = (function (parent) {
var obj = {
baz: function () {
alert(this.up().foo);
},
up: function () {
return parent;
}
};
return obj;
}(obj));
return obj;
}(window));
obj.bar.baz(); // "foo"
It's almost certainly more trouble than it's worth.

Categories

Resources