Getting a property key from a property value

Given the following javascript object:

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" } } 

How can I access the name of a property that is "back" from action() ?

I think it should be pretty simple, but if it is not something simple, I will add more information.

  • NOTE. aliases[0] holds the name by chance, and it is not promised to keep it in the future or in other teams.

EDIT: Sometimes we get complicated and we can solve the problem pretty quickly. In this case, I can just go back and return the string "back"

I will leave a question and accept an answer that solves my question if there is such a solution.

+7
javascript javascript-objects
source share
5 answers

Returning a string, as you mentioned, is definitely the easiest way. But I could see cases where someone might want to get similar functionality with a dynamically created object in which the keys are not known until runtime.

The solution that will work in this case is the commands object for the auxiliary objects, so they can look themselves:

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

In this case, it may also make sense to divide the function into all these action objects:

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

If you do not need to perform certain logic for each sub-object.


EDIT: To improve efficiency, we can do this when the getAction function getAction every call and adds a property that will hold the name. Thus, the search occurs only for the first time.

 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]; } } } 
+2
source share

When you call action like this:

 commands.back.action(); 

the scope of action is back . Unfortunately, creating an object that is assigned to commands.back does not know that this inside the action is called "back" . In my opinion, this is done because we can assign the object assigned to commands.back another object with a different name. How in:

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

Or closer to what you have ...

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

The only way to find out where the object knows the name of what it was created inside is through functions. Thus, we can create a temporary function called back that will create the object as desired.

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

Simplest solution

But at this point, you can simply add a property that already has a name. I would recommend making a property with the name key or name instead of putting the name directly in the action function, so that it is easier to have several places where the name is used. In addition, if necessary, there may be a place for changing the name inside the object.

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

EDIT: Added this edit as another way to do this, but I will still do the previous path. We can use Object.keys to get the property name, since back added as an enumerated property of commands .

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

Then you can get key as follows:

 commands.back.key(); 

Or in the action function:

 this.key(); 

You can add key to the back as get , which will look like this:

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

This will allow you to access the property as commands.back.key and in the action function as this.key .

You can also predefine everything, and then do the following:

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

You can add, and it’s also advisable to add a toString method for each of your objects like this.

 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 
+1
source share

What you have here is a nested object stored in the property of the object.

You cannot get this property manually - unless you are doing some weird metaprogramming things, like getting the AST node parent and trying to determine the property the object is in, etc. The easiest way is to name the property using a string, that is: back.

Simply put, this is similar to moving an object to var

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

And you are trying to get the name var inside the object.

Remember that in JavaScript you can access the property of an object using both string and index notations, so commands.back can also be called using commands['back'] . If I guess correctly, you are trying to do some sort of dispatch, so this notation may be useful to you.

+1
source share

If you have this object (literal object), you cannot use the this . You have two solutions:

  return commands.back.aliases[0] 

Or else you can build the object as a prototype object, not a literal object:

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

And initialize it as

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

All Articles