javascript get parent nested object?

2020-02-17 11:12发布

问题:

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() ?

回答1:

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.



回答2:

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



回答3:

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.



回答4:

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


回答5:

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.



回答6:

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.



回答7:

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