Is it possible to get the caller context in javascript? - javascript

var test = {
demo: function(){
//get the caller context here
}
}
//when this gets called, the caller context should be window.
test.demo();
I tried arguments.callee and arguments.callee.caller,and no luck...

Since this keyword referes to ThisBinding in a LexicalEnvironment, and javascript (or ECMAScript) doesn't allow programmatic access to LexicalEnvironment (in fact, no programmatic access to the whole Execution Context), so it is impossible to get the context of caller.
Also, when you try test.demo() in a global context, there should be no caller at all, neither an attached context to the caller, this is just a Global Code, not a calling context.

By context, I assume you mean this? That depends on how the function is invoked, not from where it is invoked.
For example (using a Webkit console):
var test = {
demo: function() {
console.log(this);
}
}
test.demo(); // logs the "test" object
var test2 = test.demo;
test2(); // logs "DOMWindow"
test.demo.apply("Cheese"); // logs "String"
Incidentally, arguments.caller is deprecated.

The value of a function's this keyword is set by the call, it isn't "context". Functions have an execution context, which includes its this value. It is not defined by this.
In any case, since all functions have a this variable that is a property of its variable object, you can't reference any other this keyword in scope unless it's passed to the function. You can't directly access the variable object; you are dependent on variable resolution on the scope chain so this will always be the current execution context's this.

Strange how we are talking about this except for
'Qix - MONICA WAS MISTREATED''s comment. Either you are able to capture
window,
worker self,
v8::Isolate v8::context is extended in fetch for cloudflare workers (or use this of the class), or
rename some other function-method's scope-this-context outside, so
this hypothetical situation has no use case.
var test = {
demo: function(){
//get the caller context here
}
}
test.demo();
For closure, one could have a dynamically named function factory for some reason, while maintaining its context like so:
function body () {}
var test = {
demo: {
[name]: function () {return body.apply(body, arguments);}
}[name];
Without for loop, thisArg argument would be {demo:fn()}
var test = {demo:{}}
const createNamedFunc = (body) => {
test.demo = return {
[name]: function () {
return body.apply(body, arguments);
} } [name];
}
createNamedFunc(function body (){})
I gather this 'functional-method-parameter-object' is
the deepest-functional-method-child of the object-declarations
of the global scope receiver[ object]
passing it on, until the next non-object nor -data-type returnable type definition or instantiation, not necessarily declaration but always implement/extension scope interface.

Related

Why is this.defaultNumber undefined? Trying to understand IIFE

Given this code:
const numberManipulator = (function() {
this.defaultNumber = 5;
const doubleNumber = function(num) {
console.log(this.defaultNumber);
return num !== undefined ? num * 2 : new Error('no number provided!');
}
return {
doubleNumber
}
})();
const test = numberManipulator.doubleNumber(20);
Why is console.log(this.defaultNumber showing undefined but if I console.log(defaultNumber, it shows 5? I thought that it would show undefined for the latter.
Thanks
You should read How does the “this” keyword work?
When the IIFE is called, its this is not set so it defaults to the global object, so in:
const numberManipulator = (function() {
this.defaultNumber = 5;
...
})();
this.defaultNumber = 5 creates a property of the global (window in a browser) object called defaultNumber and assigns it a value of 5.
If the return statement should really be:
return {doubleNumber: doubleNumber}
then the IIFE returns an object reference to numberManipulator. When called as:
numberManipulator.doubleNumber(20)
then this within doubleNumber is numberManipulator, which doesn't have a defaultNumber property so this.defaultNumber returns undefined.
Long story short:
1. The IIFE is executing in the context of the window I was wrong,
see RobG's comment.
2. this thus refers to the window inside the IIFE, since it is not set by default.
3. Your doing this.defaultNumber = 5; is hence equivalent to
window.defaultNumber = 5;
4. The second method executes in the context of numberManipulator and so logging this.defaultNumber is equivalent to numberManipulator.defaultNumber which was never set by you. Hence, it is undefined.
Explanation on this:
Inside any function, the this keyword refers to the current context in which the function is executing. It doesn't matter where you defined that function inside your codebase. All it matters in what context are you calling it. In your current case, you're calling the IIFE in the context of the window. Doing:
const numberManipulator = (function() {
// MAGIC
})();
amounts to:
running an IIFE in the window context.
assigning the result it returned to a constant.
However, the context of a function may be changed by using .call, .apply, or by simply calling that method as a property of another object. In your second log, this.defaultNumber, this refers to the object since you called the method using numberManipulator.doubleNumber(20);. Calling methods using object.prop() sets their context (=this) to that object.
Hope that helps! :D

Why didn’t the anonymous function pick up the containing scope’s this object?

I saw the code below in a text book on closures:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
alert(object.getNameFunc()()); // "The Window"
A detailed explanation on why the this object in the inner function cannot access the object scope but rather accesses the global scope could be of great help.
Thanks for the help.
In Javascript, every single function call causes a new value of this to be set and if it is just a plain function call, then this is set to either the global object (which is window in a browser) or undefined (if running in strict mode). That's what is happening for you. In this line:
object.getNameFunc()()
object.getNameFunc() makes the first method call and returns the internal anonymous function. Then, the last () is just a regular function call of that function and that regular function call causes this to get set to the global object (window in your case). Any plain function call resets the value of this to window or undefined. This is how Javascript works.
Here's a simple demo that show you how this is reset:
See this answer for the five ways that the value of this is controlled.
You can work around this issue in several ways by either saving the value of this in the parent function or using one of the methods to explicitly control the value of this.
One of the simpler mechanisms to understand is to just save the value you want in the parent function:
var myName = "The Window";
var object = {
myName: "My Object",
getNameFunc: function () {
var self = this;
return function () {
return self.myName;
};
}
};
document.write(object.getNameFunc()()); // "My Object"
Or, you can use .bind():
var myName = "The Window";
var object = {
myName: "My Object",
getNameFunc: function () {
return function () {
return this.myName;
}.bind(this);
}
};
document.write(object.getNameFunc()()); // "My Object"
FYI, I changed your variable name to myName so there could never be any confusion in following what was happening with window.name which already exists in the browser environment.
Break it down into separate function invocations. Instead of
object.getNameFunc()()
let's break that down, call the first function, then invoke the result of that.
var theFunc = object.getNameFunc(); // invocation 1
theFunc(); // invocation 2
in invocation 1 we call getNameFunc(), and we specify a context (we have something before the . ) So within that invocation "this" has a specified value, object
In the second case we are calling the function "bare", no specified context, so we have the global context.
In getNameFunc(), this refers to object. It returns a function which is not bound to a context, just to the containing scope. The context (what this refers to) is not inherited (although scopes are).
Inner functions do not inhert the context which they are defined in, although they do inherit the scope they are defined in.
One way to work around this is to .bind() the returned function to the context:
getNameFunc: function () {
return function () {
return this.name;
}.bind(this);
}

'this' in a function VS 'this' in a method

From Javascript-Garden:
Foo.method = function() {
function test() {
//this is set to the global object
}
test();
}
In order to gain access to Foo from within test, it is necessary to create a local variable inside of method that refers to Foo:
Foo.method = function() {
var that = this;
function test(){
//Use that instead of this here
}
test();
}
Could anyone explain this? As far as I understood, this refers to the global object if it's called in the global scope. But here it's called inside of a function, which is inside a method (first example). Why exactly does it refer to the global object, while the second example doesn't?
As far as I understood, this refers to the global object if it's called in the global scope.
No. this will refer to the default object if the function is called without explicit context. The scope is irrelevant. (In strict mode it will refer to undefined instead).
Why exactly does it refer to the global object
We can't tell what it refers to. The value of this is determined by how the function is called, not how it is defined.
Now you have updated the example, we can see that it is called without context, so this (in the inner function) will be the default object, which in a web browser is window (it would be undefined in strict mode).
while the second example doesn't?
In the second example, the inner function doesn't use this (which will have the same value as the previous example).
The second example uses that instead. that is defined in the scope of the outer function and is set to whatever the value of this is when that function is called.
Assuming that function is called as Foo.method() then (outer) this (and hence that) will be Foo because that is the context on which method was called.
this in a function isn't set when you define the function. It's only dynamically defined to the receiver of the function call.
If you call foo.test(), this in test will be foo.
But if you do
var f = foo.test;
f();
then this in f (which is foo.test) will be the external object (window if you're executing it at the root level).
It would be the same with
foo.test.call(window);
The second example uses a closure to place the outer functions variables on the scope chain of the inner function.
Foo.method = function() {
var that = this;
function test(){
//This function has access to the outer variables scope chain.
}
}

Function context ("this") in nested functions

When you invoke a top-level function in Javascript, the this keyword inside the function refers to the default object (window if in a browser). My understanding is that it's a special case of invoking the function as method, because by default it is invoked on window (as explained in John Resig's book, Secrets of the JavaScript Ninja, page 49). And indeed both invocations in the following code are identical.
function func() {
return this;
}
// invoke as a top-level function
console.log(func() === window); // true
// invoke as a method of window
console.log(window.func() === window); // true
So far so good... Now here is the part I don't understand:
When a function is nested in another function and invoked without specifying an object to invoke on, the this keyword inside the function also refers to window. But the inner function cannot be invoked on window (see code below).
function outerFunc() {
function innerFunc() {
return this;
}
// invoke without window.* - OK
console.log(innerFunc() === window); // true
// invoke on window
//window.innerFunc(); - error (window has no such method)
console.log(window.innerFunc) // undefined
}
outerFunc();
It makes perfect sense that the nested function isn't available on window, as it is after all nested... But then I don't understand why the this keyword refers to window, as if the function was invoked on window. What am I missing here?
EDIT
Here is a summary of the great answers below and some of my follow up research.
It is incorrect to say that invoking a function "normally" is the same as invoking it as a method of window. This is only correct if the function is defined globally.
The function context (the value of the this keyword) does not depend on where / how the function is defined, but on how it is being invoked.
Assuming that the code is not running in in strict mode, Invoking a function "normally" will have the function context set to to window (when running in a browser, or the corresponding global object in other environments).
An exception to the above rules is the use of bind to create a function. In this case even if the function is invoked "normally", it could have a context other than window. That is, in this case the context is determined by how you create the function, rather than how you invoke it. Although strictly speaking this isn't accurate, because bind creates a new function that internally invokes the given function using apply. The context of that new function will still be determined by the way it's invoked, but it shields the context of the function it internally invokes by using apply.
By invoking "normally" I refer to the following simple way of invocation:
myFunction();
To complete the picture, here is a brief coverage of other ways of invocation and the corresponding context:
As a property of an object (method) - the context is the object
Using apply or call - the context is specified explicitly
With the new operator (as a constructor) - the context is a newly created object
Feel free to update the above as necessary, for the benefit of people with similar questions. Thanks!
You can call any function that is in scope with functionName(). Since you haven't called it on an object, it will be called in the context of the default object (window). (IIRC, it will be called in the context of undefined if you are in strict mode).
The default object for context has nothing to do with where a function is defined or what scope that function appears in. It is simply the default object.
If a function is a property of an object, you can call it as reference.to.object.function(), and it will be called in the context of object instead of the default object.
Other things that change the context are the new keyword and the apply, call, and bind methods.
In JavaScript, when a function is invoked without an explicit context, the context is the global object. In the case of web browsers, the global object is window.
Additionally, JavaScript has functional scope, so any variables or functions within a function are not accessible in a scope outside of that function. This is why you can't access window.innerFunc.
Whether a function is nested inside another one has nothing to do with the value of this when the function is called. The only things that matter are:
If the function is "found" by traversing a property on an object, then the value of this will be a reference to that object:
someObject.prop( whatever );
It doesn't matter how the function was declared.
If you use call() or apply() to invoke a function, then the value of this is taken from the first argument to whichever of those functions you use.
If you've created a bound wrapper for the function with bind(), then the value of this will be as requested when bind() was called.
If you're calling a function as a constructor with new, then this will refer to the newly-created object instance.
Otherwise, this is either a reference to the global context, or else it's undefined (in "strict" mode or in an ES5-compliant runtime).
The "location" in the code where a function is defined does matter, of course, in that the scope includes whatever symbols it includes, and those are available to the function regardless of how a reference to it is obtained.
It does not depend where the function is declared but how it is called:
var obj = {
f: function() {
return this;
}
}
var f = obj.f;
console.log(obj.f()) // obj
console.log(f()) // window/default obj
Or in other words. The syntax obj.f() executes the function with this=obj while f() executes the function with this=window. In JavaScript the caller specifies the value of this.
When you define func in the global scope, it actually is assigned as a property of the window object. That is, the window object holds all globally scoped variables. (*) Therefore, func and window.func represent the same thing. innerFunc is defined inside a function scope and is not available outside of that scope. Therefore, window.innerFunc is (still) undefined.
However, the this context is determined by how you call the function. When you call a method like obj.method(), the this context is set to obj. On the other hand, you can also call the method on its own:
var f = obj.func;
f(); // in this call: this === window
In this case, you're not calling a function on an object and thus the this context is set to the default. The default however is the global scope and as stated above, this is represented by window.
You can always override the this context by using Function.prototype.call() or Function.prototype.apply() which take a this context as first argument. For example:
var f = obj.func;
f.call(obj); // in this call: this == obj
(*) Note that this only applies to JavaScript running inside a browser. In other environments, this may differ. For example, in Node.js the GLOBAL variable holds the global scope.

Reason behind this self invoking anonymous function variant

While looking at code on github, I found the following:
(function() {
}).call(this);
This is clearly a self invoking anonymous function. But why is it written this way? I'm used to seeing the canonical variant (function() {})().
Is there any particular advantage to using .call(this) for a self invoking anonymous function?
Edit: It looks like some commonjs environments set this to a non-global value at the top level of a module. Which ones, and what do they set this to that you might want to preserve?
By default, invoking a function like (function(){/*...*/})() will set the value of this in the function to window (in a browser) irrespective of whatever the value of this may be in the enclosing context where the function was created.
Using call allows you to manually set the value of this to whatever you want. In this case, it is setting it to whatever the value of this is in the enclosing context.
Take this example:
var obj = {
foo:'bar'
};
(function() {
alert( this.foo ); // "bar"
}).call( obj );
http://jsfiddle.net/LWFAp/
You can see that we were able to manually set the value of this to the object referenced by the obj variable.
.call(this) (was actually just () until I changed it) ensures your top level this to be consistent through strict mode, --bare option and/or the running environment (where top level this doesn't point to global object).
By using:
> (function() {
> ...
> }).call(this);`
then this in the scope of the code (probaby the global object) is set as the function's this object. As far as I can tell, it's equivalent to:
(function(global) {
// global references the object passed in as *this*
// probably the global object
})(this);
In a browser, usually window is (or behaves as if it is) an alias for the global object.
C={
descript: "I'm C!<br>",
F: function() {
//set this to the caller context's 'this'
(function() {
document.write(this.descript);
}).call(this);
//set this to 'window' or 'undefined' depend the mode
(function() {
document.write(this.descript);
})();
//member function's 'this' is the object self
document.write(this.descript);
}
}
window.descript="I'm window!<br>";
C.F();
(function() {}).call(this); could set the this in the anonymous to the caller context this, in above is C.(function() {})(); will set this to window or undefined depend the mode.
Self-invoking function are useful to execute its content immediately when the script is loaded. This is convenient to initialize global scope elements.

Categories