how to access function caller context? - javascript

I'm trying to understand javascript scopes, and I want to know if in the following example:
// B can access 'context'
var a = function (context) {
b = function () {
console.log(context);
};
b();
};
a('nicccce'); // works!
// C can't access 'context'
var d = function () {
console.log(context);
};
var c = function (context) {
d();
};
c('oh oh'); // breaks
is there a way of accessing 'context' from the 'd' function?
EDIT:
I know I could pass the argument to the d function and thats obviously the sane thing to do, but I wanted to know if there was another way of doing it, maybe using this.callee as this.callee.arguments[0].

You can either
1) pass context into d as you do for c and a. or
2) put context int the lexical scope of d.
Option 2 sounds complicated, but it isn't. That is exactly what you are doing in the case of b. context is "closed-in" to the execution scope of b because the variable is available on the scope of the caller of b (because it is an argument to a).

No, that's not possible and would result in very ugly/unreadable spaghetti code.
Why can't you pass it as an argument to the function? Another (but also ugly) solution would be declaring the variable in a scope where both functions can access it (i.e. in a parent function or global)

In your example, "context" is a variable scoped to function c() and a() (two different variables in two different scopes with the same name).
Judging from how you named the variable "context", I think .call is what you are looking for?
It is worth saying that scope in javaScript is actually very simple once you get block scope out of your mind. Anything appearing between function { ... } is a scope.

In your case if you pass context as an argument in c it will not break:
// C can access 'context'
var d = function () {
console.log(arguments[0]);
};
var c = function (context) {
d(context);
};
c('oh oh'); // doesn't break
And in d you will be able to access it by using argv/args (can't remember exactly)
So something like args[0] or argv[0]
EDIT:
Changed code with arguments

Define a variable that is in the top scope:
(function(){
var global;
var d = function () {
console.log(global);
};
var a = function (local) {
global = local;
b = function () {
console.log(global);
};
b();
};
}())

Related

Simultaneously Declaring Variables and Functions in JavaScript

Can anyone explain why
function x() {
console.log("Hello!");
}
var a = x;
a();
x();
produces
Hello!
Hello!
but this
var a = function x() {
console.log("Hello!");
}
a();
x();
throws an error when you try to call function x? Is the second x function not considered a hoisted function? I tried this in both nodejs and a browser.
What you have in the second example is what's called a named function expression.
Its name is not added to the containing scope, but is accessible within the scope of the function itself:
var a = function x() {
alert(x);
};
a();
This is useful in writing recursive functions or functions that otherwise reference themselves, as it ensures that the name won't get clobbered due to anything that happens outside the function's scope.
It also allows you to create self-referencing functions in places where you can't use a function declaration, such as in an object literal:
var myFavoriteFunctions = {
factorial: function f(n) {
return n === 1 ? 1 : n * f(n);
},
identity: function (v) { return v; }
};
console.log(myFavoriteFunctions.factorial(10));
Your first example is a function statement, which declares a name in its containing scope.
Your second example is a named function expression, which does not.
For more information, see here.

JavaScript call function inside a function by variable name [duplicate]

This question already has answers here:
dynamically call local function in javascript
(5 answers)
Closed 8 years ago.
I'm having a difficulty calling a function inside of another function when its name is in a variable:
var obj = {}
obj.f = function() {
var inner = {
a: function() {
function b() {
alert('got it!');
}
b(); // WORKS AS EXPECTED
x = 'b';
[x](); // DOESN'T WORK, NEITHER this[x]() window[x](), etc.
}
}
inner.a();
}
obj.f();
I tried prefixing [x]() with different scope paths but so far w/o success. Searching existing answers did not turn up anything. It works with this[x]() if b() is placed directly inside object inner. I would like to keep b() as a function inside function a() because of variable scope in function a(), otherwise I would need to pass many parameters to b().
////
Re duplicate question: Quentin provided a more elegant answer in this thread imo.
There is no sensible way of accessing an arbitrary variable using a string matching the name of the variable. (For a very poor way to do so, see eval).
[x](); // DOESN'T WORK
You're trying to call an array as a function
NEITHER this[x]()
The function isn't a property of the inner object.
window[x](), etc.
Since it isn't a global, it isn't a property of the window object either.
If you need to call a function based on the value of a string variable, then organise your functions in an object and access them from that.
function b() {
alert('got it!');
}
var myFunctions = {
b: b
};
x = 'b';
myFunctions[x]();
Try this. Currently you are assigning string to variable x, instead of a function variable.
x = b;
x();
The problem is with your assignment
use x = b instead of x = 'b' to assign the function object as the latter just assigns the string into x.
Once you fix your assignment you can invoke the function as
x();
a.x();
a[x]();
etc.
You should make array for the function and then access using name in your variable as follow:
var obj = {}
obj.f = function() {
var inner = {
a: function() {
// set up the possible functions:
var myFuncs = {
b: function b() {alert('got it!');}
};
//b(); // WORKS AS EXPECTED --> commented this code
x = 'b';
myFuncs[x]();
}
}
inner.a();
}
The function declaration b will be captured in the closure of the anonymous function expression assigned as a.
When var is used in a closure, there is no (available in JavaScript) Object which gets assigned a property similar to what happens with window in the Global Scope.
Writing a function declaration effectively vars the name of the function.
If you really want to access a variable (i.e. b) by String, you will either need to create an Object which holds b similar to what you've done for a, or (and possibly dangerously) rely on an eval to convert "b" to b.
If you can't create the whole Object ahead-of-time, you can use this format
/* in (higher?) scope */
var fnRef = {};
// now when you
function b() {/* define as desired */}
// also keep a ref.
fnRef['b'] = b;
// fnRef['b']() will work **after this line**
let's say your code is like this:
//instead of x = 'b'
x = function(){}
then your solution could be like this:
var obj = {}
obj.f = function() {
var inner = {
a: function() {
function b() {
alert('got it!');
}
b(); // WORKS AS EXPECTED
//you can define it as a variable
var x = function(){};
//and call it like this
x();
//or define it like this
this[x] = function(){};
//and call it like this
this[x]();
//but you are creating an array [x] which is not a function
//and you are trying to call an array????
[x](); // DOESN'T WORK, NEITHER this[x]() window[x](), etc.
}
}
inner.a();
}
obj.f();

JS function to return function, shares data which should be private

I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)

Javascript scope issue: variable not being identified

I have two functions, one function calling the other.
Below is a simple analogy I created of the structure of code which I have:
function test1(){
alert(a);//a is not identified here even when test1() is called under test2 which has a defined in scope
alert(b);//b is identified here
}
function test2(){
var a = 'a';//Defining a
test1();//Calling test1()
}
var b = 'b';
test2();
As per the code above, function test1() is able to identify variable b but not variable a.
My question is that why variable a is not in the scope of test1 function even when we are calling test1() inside test2() which defines variable a?
Thanks in advance.
Functions remember their scope chain at the time they're being created, not at the time they're called.
Note that b gets "hoisted", so your code actually does this:
var b; // push b on scope chain
function test1(){ // store scope chain [b]
alert(a);
alert(b);
}
function test2(){ // store scope chain [b]
var a; // push a on scope chain
a = 'a';
test1(); //Calling test1() which still has scope chain [b], not [b,a]
}
b = 'b';
test2();
your expectation is wrong about the scope of the variables,
according to your code b is a global variable to 2 functions and a is in the scope of test2 function only because it is defined withing test2,
If you want something like you expected you can define test1 function in test2 scope like this,
function test2(){
var a = 'a';
test1();
function test1(){
alert(a);
alert(b);
}
}
var b = 'b';
test2();
JavaScript has 'function scope' which means that anything defined in a function is not visible outside of the function. So variable 'a' is not visible outside of test2. However, variable 'b' is a global variable so it is visible in every scope.
Function scope also means that inner functions get access to things defined in the outer function but that doesn't come in to play here...test1 is called from within test2 but it's not an inner function of test2. To see this in action you'd need to define the body of the test1 function within test2...
function test2(){
var a = 'a';//Defining a
test1();//Calling test1()
function test1(){
alert(a);//a is identified here because of function scope
alert(b);//b is identified here because it's global
}
}
It is a good practice to call the functions with arguments. This will make your code easily readable and maintainable. Just adding arguments to the functions make the code working:
function test1(a,b){
alert(a);
alert(b);
}
function test2(b){
var a = 'a';
test1(a,b);
}
var b = 'b';
test2(b);
If you don`t want to use arguments in the function declarations, a solutions is to use the arguments object inside the code:
function test1(){
alert(arguments[0]);
alert(arguments[1]);
}
function test2(){
var a = 'a';
test1(a,arguments[0]);
}
var b = 'b';
test2(b);
If you want you can make the arguments with default values. Since in JavaScript you can`t do it in the function argument list, it is done in the body of the function:
function test1(a,b){
a = a || 'a';
b = b || 'b';
alert(a);
alert(b);
}
function test2(b){
var a = 'a';
test1(a);//here b is not passed to the function and b will get the default value
}
var b = 'some new string for b';
test2(b);
Live on jsfiddle
According to your functions you have declared and defined the variable "a" within the function "test2()" so the scope of the variable "a" is restricted within that function itself and accessing the variable will return "undefined". But it is not in the case of the variable "b" which is declared and defined globally, So it can be accessed any where in the script part.
hope this helps you.

Declaring variables with this or var?

What is the difference between declaring a variable with this or var ?
var foo = 'bar'
or
this.foo = 'bar'
When do you use this and when var?
edit: is there a simple question i can ask my self when deciding if i want to use var or this
If it is global code (the code is not part of any function), then you are creating a property on the global object with the two snippets, since this in global code points to the global object.
The difference in this case is that when the var statement is used, that property cannot be deleted, for example:
var foo = 'bar';
delete foo; // false
typeof foo; // "string"
this.bar = 'baz';
delete bar; // true
typeof bar; "undefined"
(Note: The above snippet will behave differently in the Firebug console, since it runs code with eval, and the code executed in the Eval Code execution context permits the deletion of identifiers created with var, try it here)
If the code is part of a function you should know that the this keyword has nothing to do with the function scope, is a reserved word that is set implicitly, depending how a function is called, for example:
1 - When a function is called as a method (the function is invoked as member of an object):
obj.method(); // 'this' inside method will refer to obj
2 - A normal function call:
myFunction(); // 'this' inside the function will refer to the Global object
// or
(function () {})();
3 - When the new operator is used:
var obj = new Constructor(); // 'this' will refer to a newly created object.
And you can even set the this value explicitly, using the call and apply methods, for example:
function test () {
alert(this);
}
test.call("hello!"); //alerts hello!
You should know also that JavaScript has function scope only, and variables declared with the var statement will be reachable only within the same function or any inner functions defined below.
Edit: Looking the code you posted to the #David's answer, let me comment:
var test1 = 'test'; // two globals, with the difference I talk
this.test2 = 'test'; // about in the beginning of this answer
//...
function test4(){
var test5 = 'test in function with var'; // <-- test5 is locally scoped!!!
this.test6 = 'test in function with this'; // global property, see below
}
test4(); // <--- test4 will be called with `this` pointing to the global object
// see #2 above, a call to an identifier that is not an property of an
// object causes it
alert(typeof test5); // "undefined" since it's a local variable of `test4`
alert(test6); // "test in function with this"
You can't access the test5 variable outside the function because is locally scoped, and it exists only withing the scope of that function.
Edit: In response to your comment
For declaring variables I encourage you to always use var, it's what is made for.
The concept of the this value, will get useful when you start working with constructor functions, objects and methods.
If you use var, the variable is scoped to the current function.
If you use this, then you are assigning a value to a property on whatever this is (which is either the object the method is being called on or (if the new keyword has been used) the object being created.
You use var when you want to define a simple local variable as you would in a typical function:-
function doAdd(a, b)
{
var c = a + b;
return c;
}
var result = doAdd(a, b);
alert(result);
However this has special meaning when call is used on a function.
function doAdd(a, b)
{
this.c = a + b;
}
var o = new Object();
doAdd.call(o, a, b);
alert(o.c);
You note the first parameter when using call on doAdd is the object created before. Inside that execution of doAdd this will refer to that object. Hence it creates a c property on the object.
Typically though a function is assigned to a property of an object like this:-
function doAdd(a, b)
{
this.c = a + b;
}
var o = new Object();
o.doAdd = doAdd;
Now the function can be execute using the . notation:-
o.doAdd(a, b);
alert(o.c);
Effectively o.doAdd(a, b) is o.doAdd.call(o, a, b)
var foo = 'bar'
This will scope the foo variable to the function wrapping it, or the global scope.
this.foo = 'bar'
This will scope the foo variable to the this object, it exactly like doing this:
window.foo = 'bar';
or
someObj.foo = 'bar';
The second part of your question seems to be what is the this object, and that is something that is determined by what context the function is running in. You can change what this is by using the apply method that all functions have. You can also make the default of the this variable an object other than the global object, by:
someObj.foo = function(){
// 'this' is 'someObj'
};
or
function someObj(x){
this.x=x;
}
someObj.prototype.getX = function(){
return this.x;
}
var myX = (new someObj(1)).getX(); // myX == 1
In a constructor, you can use var to simulate private members and this to simulate public members:
function Obj() {
this.pub = 'public';
var priv = 'private';
}
var o = new Obj();
o.pub; // 'public'
o.priv; // error
Example for this and var explained below:
function Car() {
this.speed = 0;
var speedUp = function() {
var speed = 10; // default
this.speed = this.speed + speed; // see how this and var are used
};
speedUp();
}
var foo = 'bar'; // 'var can be only used inside a function
and
this.foo = 'bar' // 'this' can be used globally inside an object

Categories

Resources