从JavaScript对象访问父级的父级

58

类似于某事

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是我所想象的格式,但不一定正确。如何回到对象的父级?

12个回答

61

我只是在第一个函数中添加了

parentThis = this;
在子函数中使用parentThis。为什么?因为在JavaScript中,对象是软的。可以通过简单的赋值向软对象添加新成员(不像Java中的经典对象是硬的。向硬对象添加新成员的唯一方法是创建一个新类)。更多信息请参见这里:http://www.crockford.com/javascript/inheritance.html
同时,在最后你不必杀死或销毁对象。我在这里找到了原因:http://bytes.com/topic/javascript/answers/152552-javascript-destroy-object
希望这能有所帮助。

9
非常晚了,但能否有人详细说明一下,并提供一个代码示例?我不明白这如何帮助像操作者的代码中的对象。 - dtbarne
5
我很惊讶这个答案在这里得到了最多点赞。"第一个函数"是谁?它在对象为函数时有效(例如tntu的回答),但当对象为静态对象,如OP的问题时,它就不起作用了!parentThis将返回Window对象。 - Andre Figueiredo
1
非常有用!谢谢,你节省了我很多时间! - Aurangzeb

50

JavaScript本身并没有提供这种功能。而且我怀疑你甚至无法创建这种类型的功能。例如:

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

Bobby属于谁?


8
这个例子更清晰地展示了与多位父母有关的异常情况,比所选答案更易于理解。 - Jordan Arsenault
1
如果我们希望父母具有相同的属性和方法,我认为我们可以通过抽象出父母的声明和设置其子项来实现所需的功能。您对以下 JSFiddle 有何看法?http://jsfiddle.net/whrom/nP5eM/ - Walter Roman
1
原始问题是如何从一个对象获取其“容器”。我举了一个具有两个“容器”的对象的例子。您的示例在关系的两侧引入了多个容器。这并没有解决原始问题,但它确实进一步展示了如此简单概念的复杂性。例如,(使用您的代码),如果子对象的“父亲”集合与父对象的“孩子”集合不同步,您将遇到真正的问题。 - harley.333
"我怀疑你甚至无法创建这种类型的功能。你只需要写Bobby.father=Dad。而且我不想把父亲的名字设为“Dad”,那会让人感到困惑。最好使用“Joe”或者其他常见的名字。" - john-jones

19
在这种情况下,您可以使用life引用父对象。或者您可以在用户对象中存储对life的引用。语言中不能为您提供固定的parent,因为用户只是对象的引用,并且可能有其他引用...
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!

你可能会发现使用类似这样的东西会很有帮助:

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

似乎所有对象都可以有父成员,如果不存在,则可能只是null或空对象。这将使在对象之间移动对象时使用一种多态变得可能...或者这只是一个愚蠢的概念吗? - Robert
1
在我所举的例子中,罗伯特,谁是父对象——生命还是死亡?您可以自由地存储对父对象的引用,并为其赋予任何名称,但是由您来执行单一父结构。 - Shog9
我现在明白了,谢谢。JavaScript非常具有表达能力,这只是又一个例子而已 :) - Robert

5

关于Call:

您可以使用.call()来解决这个问题,它:

  • 必须在函数上调用addName.call()
  • 您将传递一个对象作为“this”addName.call({"name" : 'angela'});
  • 您可以传递其他参数,这些参数可以在调用的函数中使用addName.call({"name": "angela"}, true);,其中可能addName接受一个布尔值append作为参数。

使用Call:

对于这个特定的问题,我们可以通过call将“parent”对象传递来覆盖子对象中通常存在的this

先看看我们的问题

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';
    }
};

现在,这里有一个使用call的解决方案

并且可以在JSFIDDLE上查看它的工作原理

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';
    }
};

总之

无论何时你要在主对象的属性上使用方法:app.options.someFunction(arg) 都应该使用.call - app.options.someFunction.call(this, arg); - 这样可以保证您始终可以访问对象的每个部分。这可能会使您访问到另一个属性的方法,例如app.helpers.anotherFunction()

一个好的想法是在somefunction中,将this存储在变量_parentThis中,这样就清楚了this反映了什么。


4
如果我正确理解了您的问题,那么一般情况下对象并不知道它们所包含的位置。它们不知道自己的父级是谁。要查找这些信息,您需要解析父数据结构。当您谈论文档中的元素对象时,DOM 有办法为我们完成这项工作,但看起来您正在谈论原始对象。

这就是我理解的 - 我想我可能只是不知道解决方案。 - Robert

3
正如其他人所说,直接从嵌套的子级查找父级是不可能的。所有提出的解决方案都建议通过显式变量名引用回父对象或父范围的不同方式。
然而,如果您在父对象上使用递归ES6代理,直接向上遍历到父对象是可能的。
我编写了一个名为ObservableSlim的库,其中包括使您能够从子对象向上遍历到父对象的功能,以及其他功能。
这里是一个简单的示例(jsFiddle演示):
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);

3

给您:

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

同时,请确保在“girls”定义后没有逗号。这会导致脚本在IE中崩溃(在IE中,如果在数组中的最后一项后面有逗号,它就会死机)。

查看运行情况


1
我使用了类似单例模式的东西:
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;
    }
}

0

如果因為一些原因(通常是因為從庫中獲得的)你無法修改對象的結構,你可以遞歸地遍歷對象以找到其父對象。

/**
 * 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]);

0

我已经做了类似的事情,它非常有效。

简单明了。

附言:对象还有更多内容,但我只发布了相关部分。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接