Can you get the name of the property through which the function was called?

I searched and played many times, and I’m sure that the answer to this question is no , but I hope that the JavaScript expert can trick his sleeve, which can do it.

A JavaScript function can refer to several properties, even to completely different objects, so there is no such thing as an object or property that contains this function. But at any time when you actually call a function, you must do this through one object (at least a window object for global function calls) and a property for this object.

(A function can also be called through a local variable function, but we can consider a local variable function as a property of the scope object, so this case is no exception to this rule.)

My question is, is there a way to get this property name, which was used to call the function, from inside the function body? I don’t want to pass the name of the property as an argument or close around the variable in the closing area or to store the name as a separate property of the object containing the function reference, and have a function to access this name with the this object.

Here is an example of what I want to do:

 var callName1 = function() { var callName = /* some magic */; alert(callName); }; var obj1 = {'callName2':callName1, 'callName3':callName1 }; var obj2 = {'callName4':callName1, 'callName5':callName1 }; callName1(); // should alert 'callName1' obj1.callName2(); // should alert 'callName2' obj1.callName3(); // should alert 'callName3' obj2.callName4(); // should alert 'callName4' obj2.callName5(); // should alert 'callName5' 

From my search, it seems that closest you can go to the arguments.callee.name above, but this will not work, because it only returns the name that was corrected for the function object when it was defined, and only if it was defined as a named function (which is missing in my example).

I also thought that perhaps you can iterate over all the properties of this object and check the equality with arguments.callee to find a property whose value is a reference to the function itself, but this will not work either (in the general case), because it may be several references to the function in the object's own (or inherited) object, as in my example. (Also, it looks like it will be a kind of ineffective solution.)

Can this be done?

+1
source share
3 answers

Short answer:

  • No, you cannot get the "property name" used to call your function.
  • There can be no name at all or several names in different areas, so the "property name" is rather poorly defined.
  • arguments.callee deprecated and should not be used.
  • There is no solution that does not use arguments or closure.

Long answer:

As the commentary commented, you should rethink what you are trying to do and ask for it instead in a new question. But there are some common misconceptions, so I will try to explain why you cannot get a "simple property name".

The reason is that it is not easy.

Before moving on, let's clarify something. Activation objects are not objects. The ECMAScript 5.1 specification calls them Environment Records (10.2.1) , but the more general term is Scope chain . In the browser, the global scope is ( often ) a window object, but all other areas are not objects. There may be an object that you use to call the function, and when you call the function, you should be in some area. They are different.

And there are many names.

  • When you call a function, you need to reference it, for example, through an object property. This link may have a name.
  • A chain of objects has declarations that always have a name.
  • A Function (a real function, not a link) can also have a function name - your arguments.callee.name - which is fixed in the declaration.

Not only different names, they are not always (always) the "name of the property" you are looking for.

 var obj = { prop : function f(){} }, func = obj.prop; // "obj" and "func" are declarations. // Function name is "f" - use this name instead of arguments.callee // Property name is "prop" func(); // Reference name is "func" obj.prop(); // Reference names are "obj" and "prop" // But they are the same function! // PS "this" in f is undefined (strict mode) or window (non-strict) 

Thus, a function reference can come from binding (e.g., function declarations), Object (arguments.callee), or variable . All of them are References (8.7) . And the link has a name (so to speak).

A function reference trap does not always come from an object or chain of scopes, and its name is not always determined. For example, a general closure method:

 (function(i){ /* what is my name? */ })(i) 

Even if the link has a name, a function call (11.2.3) does not pass the link or its name to the function in any case. This ensures the functionality of the JavaScript engine. Consider this example:

 eval("(new Function('return function a(){}'))()")() // Calls function 'a'. 

The final function call refers to the eval function, which refers to the result of a new global scope (in strict mode , anyway), which refers to a function call statement that refers to a group that refers to an anonymous Function object, and which contains code that expresses and returns a function named "a".

If you want to get the "property name" from inside a, which should it get? "Eval"? "Function"? "Anonymous"? "A"? All of them? Before you answer, consider such difficulties as accessing functions through iframes, which has different global variables, as well as the restriction on crossing crosses or interacting with native functions (for example, Function.prototype.bind ), and you will see how he quickly turns into hell.

That is why arguments.caller , __caller__ and other similar methods are now deprecated. The "property name" of a function is even more poorly defined than the caller, which is almost unrealistic. At the very least, the caller is always the execution context (optionally a function).

So, not knowing what your real problem is, the best way to get the “property name” is to use closure.

+7
source

there is no reflection, but you can use the behavior of the function to add your own rather painless and without resorting to try / catch, arguments.callee, Function.caller or other strongly frowned behavior, just a wasteful cycle:

 // returning a function from inside a function always creates a new, unique function we can self-identify later: function callName() { return function callMe(){ for(var it in this) if(this[it]===callMe) return alert(it); } }; //the one ugly about this is the extra "()" at the end: var obj1 = {'callName2':callName(), 'callName3':callName() }; var obj2 = {'callName4':callName(), 'callName5':callName() }; //test out the tattle-tale function: obj1.callName2(); // alerts 'callName2' obj2.callName5(); // alerts 'callName5' 

if you REALLY want it to look like an assignment, and every time avoid doing paired in object literature, you can do this hacked procedure to create a call alias:

 function callName() { return function callMe(){ for(var it in this) if(this[it]===callMe) return alert(it); } }; //make an alias to execute a function each time it used : Object.defineProperty(window, 'callNamer', {get: function(){ return callName() }}); //use the alias to assign a tattle-tale function (look ma, no parens!): var obj1 = {'callName2': callNamer, 'callName3': callNamer }; var obj2 = {'callName4': callNamer, 'callName5': callNamer }; //try it out: obj1.callName2(); // alerts 'callName2' obj2.callName5(); // alerts 'callName5' 

all that aside, you can probably accomplish what you need to do without the whole cycle needed for this approach.

<strong> Benefits:

  • works on global or object properties.
  • does not require retransmission of the key / name.
  • Does not use proprietary or legacy features.
  • doesn't use arguments or close
  • the surrounding code is faster (optimized) than the try / catch version
  • not embarrassed by reuse
  • can handle new and deleted (renamed) properties

Cautions:

  • does not work on private vars that do not have a property name
  • partially bypasses the owner object every access
  • slower calculation than stored property or code-time repetition
  • will not stand call / bind / apply
  • won't setTimeout without bind () or wrapper function
  • cannot be easily cloned

Honestly, I think that all the ways to solve this problem are “less ideal” to be polite, and I would recommend that you just bite the coding bullet and pass extra key names or automate this using the method to add a property for an empty object instead to encode all this in an object literal.

+2
source

Yes.

Sorting.

It depends on the browser. (Chrome = OK, Firefox = Nope)

You can use factory to create a function and hack a call stack session, which is likely to make me arrest.

This solution works in my version of Chrome on Windows 7, but this approach can be adapted to other browsers (if they support stack and show the property name in the call stack, as Chrome does). I would not recommend doing this in production code, as it is a rather fragile hack; instead, improve the architecture of your program so that you do not have to rely on the name of the name of the calling property. You did not post details of your problem area, so this is just a fun experiment with small thoughts; to wit:

JSFiddle demo: http://jsfiddle.net/tv9m36fr/

Runnable snippet: (scroll down and click Run snippet )

 function getCallerName(ex) { // parse the call stack to find name of caller; assumes called from object property // todo: replace with regex (left as exercise for the reader) // this works in chrome on win7. other browsers may format differently(?) but not tested. // easy enough to extend this concept to be browser-specific if rules are known. // this is only for educational purposes; I would not do this in production code. var stack = ex.stack.toString(); var idx = stack.indexOf('\n'); var lines = ex.stack.substring(idx + 1); var objectSentinel = 'Object.'; idx = lines.indexOf(objectSentinel); var line = lines.substring(idx + objectSentinel.length); idx = line.indexOf(' '); var callerName = line.substring(0, idx); return callerName; } var Factory = { getFunction: function () { return function () { var callName = ""; try { throw up; // you don't *have* to throw to get stack trace, but it more fun! } catch (ex) { callName = getCallerName(ex); } alert(callName); }; } } var obj1 = { 'callName2': Factory.getFunction(), 'callName3': Factory.getFunction() }; var obj2 = { 'callName4': Factory.getFunction(), 'callName5': Factory.getFunction() }; obj1.callName2(); // should alert 'callName2' obj1.callName3(); // should alert 'callName3' obj2.callName4(); // should alert 'callName4' obj2.callName5(); // should alert 'callName5' 
+1
source

All Articles