从 属性 的值中获取 属性 键

Getting the property key from within a property's value

给定以下 javascript 对象:

var commands = {
    back:{
        command: "b",
        aliases: ["back","go back","backwards"],
        action: function(){
            return this.key; //I want this to return "back" (the prop name)
        },
        desc: "goes back"
    }
}

如何从 action() 中访问 属性 名称 "back"?

我认为它应该很简单,但如果它不简单,我会添加更多细节。

编辑: 有时我们会变得复杂,但我们可以很快解决问题。 在这种情况下,我可以继续 return 字符串 "back"

如果有这样的解决方案,我将留下问题并接受解决我问题的答案。

如果您有该对象(文字对象),则不能使用 this 关键字。您有两个解决方案:

   return commands.back.aliases[0]

否则,您可以将对象构造为原型对象而不是文字对象:

var commands = function() {

  this.back = function() {
    this.command = "b";
    this.aliases = ["back","go back","backwards"];
    this.action = function() {
      return this.aliases[0];
    };
    this.desc = "goes back";
  };
};

然后像这样初始化

var instance = new commands();
instance.action(); // it returns "back" string

您可以像这样为您的每个对象添加 toString 方法。

var commands = {
back:{
    command: "b",
    name : "back",
    aliases: ["back","go back","backwards"],
    action: function(){
        return this.toString(); 
    },
    desc: "goes back",
    toString : function(){
      return this.name;
    }
 }
}
console.log(commands.back.action()); // back
console.log(commands.back.toString()); // back

您在这里看到的是一个嵌套对象,位于对象的 属性 上。

您无法手动获取 属性 - 除非您正在做一些奇怪的元编程,例如获取 AST 父节点并尝试确定 属性 持有对象等。最简单的方法是使用字符串保存 属性 名称,即:"back".

简单来说,就是把对象持有到一个var

var obj = {/*....*/};

并且您正在尝试从对象中获取 var 名称。

请记住,虽然在 JavaScript 中,您可以使用字符串和索引表示法访问对象 属性,因此也可以使用 commands['back'] 调用 commands.back。如果我没猜错的话,你正在尝试进行某种调度,所以这个符号对你很有用。

当您按以下方式调用 action 时:

commands.back.action();

action的范围是back。可悲的是,分配给 commands.back 的对象的创建并不知道 action 内部的 this 被称为 "back"。根据我的理解,这样做是因为我们可以将分配给 commands.back 的对象分配给另一个具有另一个名称的对象。如:

var foo = { f: function(){console.log(this) } };
var bar = foo;
bar.f();

或更接近您所拥有的...

var foo = {
    bar: {
        f:function(){console.log(this)}
    }
};
var other = { another: (foo.bar) };

我知道对象在哪里知道它在其中创建的名称的唯一方法是函数。因此,我们可以创建一个名为 back 的临时函数,它将根据需要创建一个对象。

var commands = {
    back:(new function back(){
        // I prefer to assign to a variable to assist with the readability as to what "this" is:)
        var self     = this;
        self.command = "b";
        self.aliases = ["back","go back","backwards"];
        self.action  = function(){
            // Can leave as "this" or change to "self".
            return this.key;
        };
        self.desc = "goes back";
        self.key  = self.prototype.constructor.name;
    })
}

最简单的解决方案

但到那时还不如添加一个已有名称的 属性。我建议做一个名为 keyname 的 属性 而不是将名称直接放入 action 函数中,这样更容易在多个地方使用该名称。此外,如果需要,允许在一个地方更改对象中的名称。

var commands = {
    back:{
        command: "b",
        aliases: ["back","go back","backwards"],
        action: function(){
            return this.key;
        },
        desc: "goes back",
        key: "back"
    }
}

编辑: 添加此编辑作为执行此操作的另一种方式,但我仍会采用以前的方式。我们可以利用 Object.keys 来获取 属性 的名称,因为 back 被添加为 commands.

的可枚举 属性
var i        = 0,
    commands = { back: {
        key: (function(id){return function(){return Object.keys(commands)[id]}})(i++)
    }}

然后可以通过以下方式得到key

commands.back.key();

或在 action 函数内:

this.key();

可以将 key 添加到 back 作为 get,看起来像:

var i        = 0,
    commands = { back: { 
        id: (i++),
        get key() {return Object.keys(commands)[this.id]}
    }}

这将允许您以 commands.back.key 访问 属性 并在 action 函数中以 this.key.

访问

也可以预先定义所有内容然后可以执行以下操作:

var i = 0, commands = { back: undefined };
commands.back = { key: Object.keys(commands)[i++] };

返回你提到的字符串绝对是最简单的方法。但是我可以看到有人可能希望能够使用动态创建的对象获得类似功能的情况,其中的密钥直到 运行 时间才为人所知。

在这种情况下可行的解决方案是将 commands 对象暴露给子对象,以便它们可以自行查找:

var commands = {
    back:{
        command: "b",
        aliases: ["back","go back","backwards"],
        action: function(){
            var commandKeys = Object.keys(commands);
            for(var i=0; i < commandKeys.length; i++){
                if(commands[commandKeys[i]] === this){
                    return commandKeys[i];
                }
            }
        },
        desc: "goes back"
    }
};

在这种情况下,在所有这些操作对象之间共享函数也可能更有意义:

var commands = {
    back:{
        command: "b",
        aliases: ["back","go back","backwards"],
        action: getAction,
        desc: "goes back"
    },
    forward: {
        //...
        action: getAction,
        //...
    }
};

function getAction() {
    var commandKeys = Object.keys(commands);
    for(var i=0; i < commandKeys.length; i++){
        if(commands[commandKeys[i]] === this){
            return commandKeys[i];
        }
    }
}

除非您需要为每个子对象执行一些特定的逻辑。


编辑:为了提高效率,我们可以在每次调用不执行getAction函数的地方添加一个属性来存储姓名。这样查找只会在第一次发生。

var commands = {
    back:{
        command: "b",
        aliases: ["back","go back","backwards"],
        action: getAction,
        desc: "goes back"
    },
    forward: {
        //...
        action: getAction,
        //...
    }
};
// Only needs to getKey the first time called.
function getAction() {
    if(!this.key) this.key = getKey(this);
    return this.key;
}
function getKey(obj) {
    var commandKeys = Object.keys(commands);
    for(var i=0; i < commandKeys.length; i++){
        if(commands[commandKeys[i]] === obj){
            return commandKeys[i];
        }
    }
}