Access parent's parent from javascript object

JavascriptOop

Javascript Problem Overview


Something like

var life= {
        users : {
             guys : function(){ this.SOMETHING.mameAndDestroy(this.girls); },
             girls : function(){ this.SOMETHING.kiss(this.boys); },
        },
        mameAndDestroy : function(group){ },
        kiss : function(group){ }
};

this.SOMETHING is what I imagine the format is, but it might not be. What will step back up to the parent of an object?

Javascript Solutions


Solution 1 - Javascript

I simply added in first function

parentThis = this;

and use parentThis in subfunction. Why? Because in JavaScript, objects are soft. A new member can be added to a soft object by simple assignment (not like ie. Java where classical objects are hard. The only way to add a new member to a hard object is to create a new class) More on this here: http://www.crockford.com/javascript/inheritance.html

And also at the end you don't have to kill or destroy the object. Why I found here: http://bytes.com/topic/javascript/answers/152552-javascript-destroy-object

Hope this helps

Solution 2 - Javascript

JavaScript does not offer this functionality natively. And I doubt you could even create this type of functionality. For example:

var Bobby = {name: "Bobby"};
var Dad = {name: "Dad", children: [ Bobby ]};
var Mom = {name: "Mom", children: [ Bobby ]};

Who does Bobby belong to?

Solution 3 - Javascript

In this case, you could use life to reference the parent object. Or you could store a reference to life in the users object. There can't be a fixed parent available to you in the language, because users is just a reference to an object, and there could be other references...

var death = { residents : life.users };
life.users.smallFurryCreaturesFromAlphaCentauri = { exist : function() {} };
// death.residents.smallFurryCreaturesFromAlphaCentauri now exists
//  - because life.users references the same object as death.residents!

You might find it helpful to use something like this:

function addChild(ob, childName, childOb)
{
   ob[childName] = childOb;
   childOb.parent = ob;
}

var life= {
        mameAndDestroy : function(group){ },
        kiss : function(group){ }
};

addChild(life, 'users', {
   guys : function(){ this.parent.mameAndDestroy(this.girls); },
   girls : function(){ this.parent.kiss(this.boys); },
   });

// life.users.parent now exists and points to life

Solution 4 - Javascript

About Call:

You can sort of solve this issue by using .call() which:

  • must be called on a function addName.call()
  • you pass it an object of what you want to be the "this" addName.call({"name" : 'angela'});
  • you can pass additional arguments that can be used in the function it is called on addName.call({"name": "angela"}, true); where perhaps addName accepts an argument of boolean append.
Use Call:

For this particular problem, we can pass the "parent" object through call to override the this normally present in the child object.

Look at our problem first
var app = {
    init: function() {
        var _this = this; // so we can access the app object in other functions

        $('#thingy').click(function(){
            alert(_this.options.thingy());
        });
        
        $('#another').click(function(){
            alert(_this.options.getAnother());
        });
    },
    options: {
      thingy: function() {
      // PROBLEM: the this here refers to options
      	return this.getThingy();
      },
      getAnother: function() {
        // PROBLEM 2: we want the this here to refer to options,
        // but thingy will need the parent object
      	return 'another ' + this.thingy();
      },
    },
    getThingy: function() {
        return 'thingy';
    }
};
Now, here's a solution use call

And JSFIDDLE to see it work.

var app = {
    init: function() {
        var _this = this; // so we can access the app object in other functions

        $('#thingy').click(function(){
            // SOLUTION: use call to pass _this as the 'this' used by thingy
            alert(_this.options.thingy.call(_this));
        });
        
        $('#another').click(function(){
            // SOLUTION 2: Use call to pass parent all the way through
            alert(_this.options.getAnother.call(_this)); 
        });
    },
    options: {
      thingy: function() {
        // SOLUTION in action, the this is the app object, not options.
      	return this.getThingy(); 
      },
      getAnother: function() {
        // SOLUTION 2 in action, we can still access the options 
        // AND pass through the app object to the thingy method.
      	return 'another ' + this.options.thingy.call(this); 
      },
    },
    getThingy: function() {
        return 'thingy';
    }
};
In conclusion

You can use .call() whenever you use a method on a property of your main object: app.options.someFunction(arg) should always be called with .call - app.options.someFunction.call(this, arg); - that way you can ensure that you'll always have access to every part of the object. It could give you access to another property's methods like app.helpers.anotherFunction().

A good idea is in the somefunction, store this in a variable _parentThis so it's obvious what the this reflects.

Solution 5 - Javascript

If I'm reading your question correctly, objects in general are agnostic about where they are contained. They don't know who their parents are. To find that information, you have to parse the parent data structure. The DOM has ways of doing this for us when you're talking about element objects in a document, but it looks like you're talking about vanilla objects.

Solution 6 - Javascript

Here you go:

var life={
        users:{
             guys:function(){ life.mameAndDestroy(life.users.girls); },
             girls:function(){ life.kiss(life.users.guys); }
        },
        mameAndDestroy : function(group){ 
          alert("mameAndDestroy");
          group();
        },
        kiss : function(group){
          alert("kiss");
          //could call group() here, but would result in infinite loop
        }
};

life.users.guys();
life.users.girls();

Also, make sure you don't have a comma after the "girls" definition. This will cause the script to crash in IE (any time you have a comma after the last item in an array in IE it dies).

See it run

Solution 7 - Javascript

As others have said, it is not possible to directly lookup a parent from a nested child. All of the proposed solutions advise various different ways of referring back to the parent object or parent scope through an explicit variable name.

However, directly traversing up to the the parent object is possible if you employ recursive ES6 Proxies on the parent object.

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() { return false });

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

Solution 8 - Javascript

I used something that resembles singleton pattern:

function myclass() = {
    var instance = this;

    this.Days = function() {
        var days = ["Piątek", "Sobota", "Niedziela"];
        return days;
    }

    this.EventTime = function(day, hours, minutes) {
        this.Day = instance.Days()[day];
        this.Hours = hours;
        this.minutes = minutes;
        this.TotalMinutes = day*24*60 + 60*hours + minutes;
    }
}

Solution 9 - Javascript

I have done something like this and it works like a charm.

Simple.

P.S. There is more the the object but I just posted the relevant part.

var exScript = (function (undefined) {
    function exScript() {
        this.logInfo = [];
        var that = this;
        this.logInfo.push = function(e) {
            that.logInfo[that.logInfo.length] = e;
            console.log(e);
        };
    }
})();

Solution 10 - Javascript

With the following code you can access the parent of the object:

var Users = function(parent) {
    this.parent = parent;
};
Users.prototype.guys = function(){ 
    this.parent.nameAndDestroy(['test-name-and-destroy']);
};
Users.prototype.girls = function(){ 
    this.parent.kiss(['test-kiss']);
};

var list = {
    users : function() {
        return new Users(this);
    },
    nameAndDestroy : function(group){ console.log(group); },
    kiss : function(group){ console.log(group); }
};

list.users().guys(); // should output ["test-name-and-destroy"]
list.users().girls(); // should output ["test-kiss"]

I would recommend you read about javascript Objects to get to know how you can work with Objects, it helped me a lot. I even found out about functions that I didn't even knew they existed.

Solution 11 - Javascript

If you want get all parents key of a node in a literal object ({}), you can to do that:

(function ($) {
    "use strict";

    $.defineProperties($, {
        parentKeys: {
            value: function (object) {
                var
                    traces = [],
                    queue = [{trace: [], node: object}],

                    block = function () {
                        var
                            node,
                            nodeKeys,
                            trace;

                        // clean the queue
                        queue = [];
                        return function (map) {
                            node = map.node;
                            nodeKeys = Object.keys(node);

                            nodeKeys.forEach(function (nodeKey) {
                                if (typeof node[nodeKey] == "object") {
                                    trace = map.trace.concat(nodeKey);
                                    // put on queue
                                    queue.push({trace: trace, node: node[nodeKey]});

                                    // traces.unshift(trace);
                                    traces.push(trace);
                                }
                            });
                        };
                    };

                while(true) {
                    if (queue.length) {
                        queue.forEach(block());
                    } else {
                        break;
                    }
                }

                return traces;
            },

            writable: true
        }

    });

})(Object);

This algorithm uses the FIFO concept for iterate the graphs using the BFS method. This code extend the class Object adding the static method parentKeys that expects a literal Object (hash table - associative array...) of the Javacript, as parameter.

I hope I have helped.

Solution 12 - Javascript

If you can't modify the structure of the object because it's given to you (typically from a library), you can alway traverse recursively the object to find its parent.

/**
 * Recursively traverse the rootObject to find the parent of childObject.
 * @param rootObject - root object to inspect
 * @param childObject - child object to match
 * @returns - parent object of child if exists, undefined otherwise
 */
function findParent(rootObject, childObject) {
  if (!(rootObject && typeof rootObject === 'object')) {
    return undefined;
  }
  if (Array.isArray(rootObject)) {
    for (let i = 0; i < rootObject.length; i++) {
      if (rootObject[i] === childObject) {
        return rootObject;
      }
      const child = this.findParent(rootObject[i], childObject);
      if (child) {
        return child;
      }
    }
  } else {
    const keys = Object.keys(rootObject);
    for (let i = 0; i < keys.length; i += 1) {
      const key = keys[i];
      if (rootObject[key] === childObject) {
        return rootObject;
      }
      const child = this.findParent(rootObject[key], childObject);
      if (child) {
        return child;
      }
    }
  }
  return undefined;
}

// tests

const obj = {
  l1: { l11: { l111: ['a', 'b', 'c'] } },
  l2: { l21: ['a', 1, {}], l22: 123 },
  l3: [ { l33: {} } ],
};

assert.equal(findParent(obj, obj.l1), obj);
assert.equal(findParent(obj, obj.l1.l11), obj.l1);
assert.equal(findParent(obj, obj.l2), obj);
assert.equal(findParent(obj, obj.l2.l21), obj.l2);
assert.equal(findParent(obj, obj.l2.l22), obj.l2);
assert.equal(findParent(obj, obj.l3[0]), obj.l3);
assert.equal(findParent(obj, obj.l3[0].l33), obj.l3[0]);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobertView Question on Stackoverflow
Solution 1 - JavascriptjazkatView Answer on Stackoverflow
Solution 2 - Javascriptharley.333View Answer on Stackoverflow
Solution 3 - JavascriptShog9View Answer on Stackoverflow
Solution 4 - JavascriptamurrellView Answer on Stackoverflow
Solution 5 - JavascriptAdam BellaireView Answer on Stackoverflow
Solution 6 - JavascriptcmccullohView Answer on Stackoverflow
Solution 7 - JavascriptElliot B.View Answer on Stackoverflow
Solution 8 - JavascriptParchandriView Answer on Stackoverflow
Solution 9 - JavascripttransilvladView Answer on Stackoverflow
Solution 10 - JavascriptRodrigo MoralesView Answer on Stackoverflow
Solution 11 - JavascriptrplaurindoView Answer on Stackoverflow
Solution 12 - JavascriptNinrootView Answer on Stackoverflow