I have uploaded a sample code where there are two objects defined using the modular pattern.
var obj1 = {
cachdom: function() {
// querying a dom element
this.$interactiveReport = $('#BugPreventiveIR_data_panel');
}
};
var obj2 = {
$IR: obj1.$interactiveReport,
logging: function() {
// This returns undefined
console.log(this.$IR);
}
};
// Invoking the Objects only after document is ready.
$(document).ready(function() {
console.clear();
interactiveReportRegion.init();
displayOfBugList.logging();
});
However, when I check in console the obj2.logging returns undefined for this.$IR. However, if I were to access obj1.$interactiveReport through console I can view the object values. Similairly, if I were to define the second object in the console again, it obtains the correct value for obj2.$IR.
Any reason I am unable to access the first object's properties in the second object?
My entire code can be found here
https://gist.github.com/alaghu/6225e50b35ecfdaf2ee8f752f596f49b
The logging function needs to know what "this" means since it's not a constructed object.. this should do it.
displayOfBugList.logging.apply(displayOfBugList);
defining a function changes the scope for "this". Consider using lamda functions to fix this. There is also the ability to bind what "this" is but I shy away from that method
Related
I've done a lot of searching and some playing around, and I'm pretty sure the answer to this question is no, but I'm hoping a JavaScript expert might have a trick up his sleeve that can do this.
A JavaScript function can be referenced by multiple properties, even on completely different objects, so there's no such thing as the object or property that holds the function. But any time you actually call a function, you must have done so via a single object (at the very least, the window object for global function calls) and property on that object.
(A function can also be called via a function-local variable, but we can consider the function-local variable to be a property of the activation object of the scope, so that case is not an exception to this rule.)
My question is, is there a way to get that property name that was used to call the function, from inside the function body? I don't want to pass in the property name as an argument, or closure around a variable in an enclosing scope, or store the name as a separate property on the object that holds the function reference and have the function access that name property on the this object.
Here's 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 searching, it looks like the closest you can get to the above is arguments.callee.name, but that won't work, because that only returns the name that was fixed to the function object when it was defined, and only if it was defined as a named function (which the function in my example is not).
I also considered that maybe you could iterate over all properties of the this object and test for equality with arguments.callee to find the property whose value is a reference to the function itself, but that won't work either (in the general case), because there could be multiple references to the function in the object's own (or inherited) property set, as in my example. (Also, that seems like it would be kind of an inefficient solution.)
Can this be done?
Short answer:
No, you cannot get "the property name" used to call your function.
There may be no name at all, or multiple names across different scopes, so "the property name" is pretty ill defined.
arguments.callee is deprecated and should not be used.
There exists no solution that does not use arguments or closure.
Long answer:
As thefourtheye commented, you should rethink what you are trying to do and ask that instead in a new question. But there are some common misconceptions, so I will try to explain why you cannot get the "simple property name".
The reason is because it is not simple.
Before we go ahead, let us clarify something. Activation Objects are not objects at all.
The ECMAScript 5.1 specification calls them Environment Records (10.2.1), but a more common term is Scope chain.
In a browser the global scope is (often) the window object, but all other scopes are not objects.
There may be an object that you use to call a function, and when you call a function you must be in some scope.
With few exceptions, scopes are not objects, and objects are not scopes.
Then, there are many names.
When you call a function, you need to reference it, such as through an object property. This reference may have a name.
Scope chain has declarations, which always have a name.
A Function (the real function, not reference) may also have a function name - your arguments.callee.name - which is fixed at declaration.
Not only are they different names, they are not (always) the "the property name" you are seeking.
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!
// P.S. "this" in f is undefined (strict mode) or window (non-strict)
So, a function reference may comes from a binding (e.g. function declaration), an Object (arguments.callee), or a variable.
They are all References (8.7). And reference does have a name (so to speak).
The catch is, a function reference does not always come from an object or the scope chain, and its name is not always defined.
For example a common closure technique:
(function(i){ /* what is my name? */ })(i)
Even if the reference does have a name, a function call (11.2.3) does not pass the reference or its name to the function in any way.
Which keeps the JavaScript engine sane. Consider this example:
eval("(new Function('return function a(){}'))()")() // Calls function 'a'.
The final function call refers the eval function, which refers the result of a new global scope (in strict mode, anyway), which refers a function call statement, which refers a group, which refers an anonymous Function object, and which contains code that expresses and returns a function called 'a'.
If you want to get the "property name" from within a, which one should it get? "eval"? "Function"? "anonymous"? "a"? All of them?
Before you answer, consider complications such as function access across iframes, which has different globals as well as cross origin restriction, or interaction with native functions (Function.prototype.bind for example), and you will see how it quickly becomes hell.
This is also why arguments.caller, __caller__, and other similar techniques are now all deprecated.
The "property name" of a function is even more ill defined than the caller, almost unrealistic.
At least caller is always an execution context (not necessary a function).
So, not knowing what your real problem is, the best bet of getting the "property name" is using closure.
there is no reflection, but you can use function behavior to make adding your own fairly painless, and without resorting to try/catch, arguments.callee, Function.caller, or other strongly frowned-upon behavior, just wasteful looping:
// 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 to make it look like an assignment and avoid the execution parens each time in the object literal, you can do this hacky routine to create an invoking 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's 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 all the looping required by this approach.
Advantages:
works on globals or object properties
requires no repetitive key/name passing
uses no proprietary or deprecated features
does not use arguments or closure
surrounding code executes faster (optimized) than
a try/catch version
is not confused by repeated uses
can handle new and deleted (renamed) properties
Caveats:
doesn't work on private vars, which have no property name
partially loops owner object each access
slower computation than a memorized property or code-time repetition
won't survive call/bind/apply
wont survive a setTimeout without bind() or a wrapper function
cannot easily be cloned
honestly, i think all the ways of accomplishing this task are "less than ideal", to be polite, and i would recommend you just bite the coding bullet and pass extra key names, or automate that by using a method to add properties to a blank object instead of coding it all in an object literal.
Yes.
Sort Of.
It depends on the browser. (Chrome=OK, Firefox=Nope)
You can use a factory to create the function, and a call stack parsing hack that will probably get me arrested.
This solution works in my version of Chrome on Windows 7, but the approach could be adapted to other browsers (if they support stack and show the property name in the call stack like Chrome does). I would not recommend doing this in production code as it is a pretty brittle hack; instead improve the architecture of your program so that you do not need to rely on knowing the name of the calling property. You didn't post details about your problem domain so this is just a fun little thought experiment; to wit:
JSFiddle demo: http://jsfiddle.net/tv9m36fr/
Runnable snippet: (scroll down and click Run code 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's 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'
I'm trying to store an object of selectors in jQuery for performance reasons and later use.
In some case, I may need to access a key in the same object literal I am creating.
(function($) {
'use strict';
var appCache = {
$navSecondary: $('#nav-secondary'),
$navMore: appCache.$navSecondary.find('.more')
};
})(jQuery);
The above code produces the error:
TypeError: appCache is undefined
how can I get around this? Would I have to perform the selection again in jQuery?
Thanks.
The right hand side of the assignment operator is evaluated first. The result is then passed left and assigned to the variable.
The variable is undefined while the object is being constructed, so you can't use its value (it has been declared though, var statements are hoisted).
You can assign a new property after the object has been created.
var appCache = {
$navSecondary: $('#nav-secondary')
};
appCache["$navMore"] = appCache.$navSecondary.find('.more');
var appCache = {
$navSecondary: $('#nav-secondary'),
$navMore: appCache.$navSecondary.find('.more')
^^^^^^^^
};
Because you are trying to access variable during its initialization.
You could use a function instead:
$navMore: function(){return this.$navSecondary.find('.more')}
Then call it once object is initialized:
appCache.$navMore(); // this will return set of jQuery matched elements
My question is dead simple.
I just casually discovered that once you have defined a property with this. into an object, you don't need to prepend this. anymore when you want to call them.
So this. is really meant to be used ad definition time, like var?
I found it my self shortly after, i was referencing the window object with this. since i called my object without using new, so like it was a function.
One extra question, maybe for comments. Inside the main object, if i create a new object, and use this during the object definition, this this what will be referring to?
No, unless the context of this is a global object, such as window. Take the following example:
function Foo(bar) {
this.data = bar;
console.log(this.data); // OK
console.log(data); // ReferenceError
}
In this example, you'll get a ReferenceError: data is not defined on the first console.log(data), unless, data is a global variable. To access the instance's public member, you have to use this.data.
References:
Understanding JavaScript’s this keyword
The this keyword
There are all sorts of circumstances where you MUST use this in order to reference the right data.
These two implementations do very different things:
Array.prototype.truncate(newLen) {
// sets the length property on the current Array object
this.length = newLen;
}
Array.prototype.truncate(newLen) {
// sets a global variable named length
length = newLen;
}
var arr = [1,2,3,4,5,6,7];
arr.truncate(2);
You MUST use this in order to control what happens if you want to modify the current object. Your assumption that you can leave it off and it will still modify the current object's properties is not correct. If you leave it off, you are modifying global variables, not member properties.
So this. is really meant to be used ad definition time, like var?
No, the point of this is to be the current scope of execution. You can (and will) run into weird errors if you don't use this. For example, imagine you are an object with a property val and then on the prototype of that object you have
App.obj = function(){
this.val = 'initial';
}
obj.prototype.myMethod = function(val) {
// how would you assign argument val to object val?
}
also note that your reasoning breaks down with methods.
obj.prototype.meth2 = function(){
myMethod(); // fails where this.myMethod() would work.
}
See http://jsfiddle.net/BRsqH/:
function f(){
this.public='hello!';
var hidden='TOP SECRET!';
}
var instance=new f();
alert('Public data: '+instance.public+ /* gives "hello!" */
'\nHidden data: '+instance.hidden /* gives undefined */
);
Variables created with var are hidden and cannot be viewed nor modified outside the function which created them.
But variables created with this are public, so you can access them outside the function.
I think I got it.
I defined my object as function My_Object(){...} and then called it with MyObject(). This way the My_Object was treated as a function, not an object and therefore this == window.
So in the end I was attaching properties and methods to window instead of My_Object! That's way there were available without prepending .this.
The right way to initialize My_Object as an object is to call it like this new My_Object, isn't right?
var a = {
text : 3,
logText : function () {
console.log(this.text);
},
callLogText : function() {
logText();
}
};
a.callLogText();
This will genernate a ReferenceError: logText is not defined error message.
Instead, you prefix this to the logText() method, it will be ok. No error msg will pop.
var a = {
text : 3,
logText : function () {
console.log(this.text);
},
callLogText : function() {
this.logText();
}
};
I really cant figure out the reason.
You need to learn the JavaScript scoping rules. This blog post gives a good introduction.
In a nutshell, JavaScript follows some rules when you use a variable name (for the purpose of this explanations, function definitions are pretty much like variable declarations).
What probably confuses you is this:
var a = { b: ...};
var a = function() { var b = ... }
In both cases, you get a new variable a. In the first case, it's an object with a property b. In the second case, it's a function which has a nested scope in which a new variable b is defined.
JavaScript will look in the current and all parent scopes for variables. But object definitions are no scopes. As far as JavaScript is concerned, the property b is invisible unless you make it visible by using the special variable this which always references the "current" object (in your example, that is a).
Since the properties of the object a are not "in scope", JavaScript can't find logText() unless you tell it to look in this. If you don't say anything, JavaScript will look in the current scope (the body of the function callLogText), then the parent scope (in which a is defined) and then in any parent scopes of that.
It's not a quirk. It's how most languages function when it comes to objects.
logText() is a method of the a object, not a function.
You need to call methods internally as this.methodName() or externally as object.methodName().
logText(); is to execute a global function logText which is undefined.
this.logText(); is to execute the function a.logText.
Calling
logText();
means somewhere there is a function named logText(), but here you have defined logText() as a property of an object, so to access the logText() you have to refer it with the help of the object it is defined in. In this case it is in the same object so you refer to the same object by saying this.
This is something that I'm sure I should know the answer to, but either I'm just being stupid or I've just somehow never come across this before...
Given the following array, declared in the global scope:
var arr = [function() {
console.dir(this);
}];
I would have expected this to refer to the Window object. However, when calling the function:
arr[0]();​ //Logs Array
It appears that this actually refers to the array. Then, when I store a reference to the function in another variable and call that, this does refer to the Window object:
var func = arr[0];
func(); //Logs Window
So, why does the context of the function change? Here's a fiddle demonstrating the above two cases.
When you call a function as property of an object, such as obj.func(), this refers to obj.
This is exactly what you are doing here. arr is your object and 0 is the property holding a function.
Note: After all, arrays are just objects and their elements are the values of their properties (though properties are typically numerical strings (all properties are strings)).
See MDN - this for more information, in this case:
When a function is called as a method of an object, its this is set to the object the method is called on.
In your second case, you call the function "standalone", hence this refers to window. If the code was run in strict mode though, this would be undefined.
This is because this keyword is actually an operator which returns the reference to the holder (or owner) of a function where it was called. Since in first case the holder is an array (which is an object) it returns an array. In the second case the holder is the window object.
See this article for more details.
be noted:
in JavaScript, this always refers to the function owner.
you can read more about it at quirksmode.
Because the element is a member of the array, and this always points to the owner object (unless you play with bind()/ call()/ apply() etc). If you're not part of an object, this will be the global object; which is window in the browser environment. The exception to this is if you're in strict mode, where this will be undefined.
What you're doing is effectively the same as:
var ar = {
'0' : function () {
console.dir(this);
}
}
ar[0]();
var func = ar[0];
func();
... which may make more sense conceptually.
http://jsfiddle.net/TVtwr/1/