Javascript's Function constructor - scope issues? - javascript

This code new Function('fn', 'fn()') creates anonymous function which has param and (in this case the param is a function) executed.
Doing console.log(new Function('fn', 'fn()')) shows the output :
function anonymous(fn)
{
fn()
}
Now , the docs states :
Note: Functions created with the Function constructor do not create
closures to their creation contexts; they always are created in the
global scope. When running them, they will only be able to access
their own local variables and global ones, not the ones from the scope
in which the Function constructor was called. This is different from
using eval with code for a function expression.
Ok.
So why does this code yields 1 and not 44 ?
var a = 44;
function myFunc()
{
var a = 1;
function f()
{
alert(a)
}
new Function('fn', 'fn()')(f);
}
myFunc();
Why ?
What about this line above ?
they will only be able to access their own local variables and global
ones, not the ones from the scope in which the Function constructor
was called
It looks like the f is closure over the parent a , but how come ? it suppose to be run at global , and able to access local and global only !
What am I missing ?

Because you're invoking function f, which is a normal function that does create a closure.
The documentation refers to this:
var a = 44;
function myFunc()
{
var a = 1;
new Function('fn', 'alert(a)')(); //shows 44, not 1
}
myFunc();

In your example you call f function that defined inside myFunc function. Does not matter where you called it from. So you called you anonymous function that is defined in global scope and see a = 44, anonymous function called f function that is defined in myFunc scope and see a = 1.
I'm not sure what exactly you want to achieve with this code, but if you need 44 in your f you need to pass it there as an argument.
var a = 44;
function myFunc() {
var a = 1;
function f(myArgument) {
alert(myArgument)
}
new Function('fn', 'fn(a)')(f);
}
myFunc();

Related

JavaScript Inner Functions & 'this'

In my understanding, it is not until an object invokes a Function that this is actually assigned a value. And the value it is assigned is based exclusively on the object that invokes the Function.
Also, the scope chain rule in JS is LEG.
So, in (strict mode):
function one () {
var a = 2;
function two () {
console.log(a)};
two()
}
one() // output 2
But:
function one () {
var a = 2;
function two () {
console.log(this.a)};
two()
}
one() //output undefined
It was my understandig functions were objects, and in the previous invokation the function object one would be the making the call of two, which translates this.a into one.a. Obviously that is not the case.
Also:
function one () {
var a = 2}
console.log(one.a) //outputs undefined
Any clarification about what is happening would be very appreciated.
Thanks
function one () {
var a = 2;
function two () {
console.log(this.a)};
two()
}
one() //output undefined
Here you are calling both one and two as functions on their own, not as properties of some object (e.g. someObject.one()). This means that this will refer to the global scope (or to undefined if the code is in strict mode). The a property of your global scope is undefined, so that's why you see undefined. Calling two() inside of one() doesn't make it so that this refers to one.
function one () {
var a = 2}
console.log(one.a) //outputs undefined
a is not a property of one. It is a variable inside it. A property of one would look like this.
function one() {
}
one.a = 7;
console.log(one.a);
I think you are treating a regular function as a class object. You only call one() but you do not actually instantiate a new One() object.
Scope and Context are different concepts. Within a JS-Function scope one can address any value that either got assigned inside a function or any value that got assigned outside such a function as long as this function itself is a member of the outer scope. In addition, as long as a function does not create a closure, scope gets created just at a function's call time. Context is addressable too. But unlike scope, context gets evaluated at a functions call time. Thus this is just a placeholder for context that can vary from one time calling a function to another.
function contextAwareMethod(injectedValue) {
"use strict";
var
context = this, // dynamically evaluated.
innerScopeValue = "created at a functions call time";
console.log('dynamically evaluated context : ', context);
console.log('this.contextValue : ', (context && context.value));
console.log('innerScopeValue : ', innerScopeValue);
console.log('injectedValue : ', injectedValue);
}
console.log('"case A"');
contextAwareMethod('argument A');
console.log('\n');
console.log('"case B"');
contextAwareMethod.call({ value: "conext B"});
console.log('\n');
console.log('"case C"');
contextAwareMethod.call({ value: "conext C"}, 'argument C');
console.log('\n');
.as-console-wrapper { max-height: 100%!important; top: 0; }

Functions created with the Function constructor are always created in the global scope

From the MDN description of Function:
Note: Functions created with the Function constructor do not create
closures to their creation contexts; they always are created in the
global scope. When running them, they will only be able to access
their own local variables and global ones, not the ones from the scope
in which the Function constructor was called. This is different from
using eval with code for a function expression.
I understand,
var y = 10;
var tester;
function test(){
var x = 5;
tester = new Function("a", "b", "alert(y);");
tester(5, 10);
}
test(); // alerts 10
Replacing the tester = new Function("a", "b", "alert(y);"); with tester = new Function("a", "b", "alert(x);");, I will get
// ReferenceError: x is not defined
But couldn't understand the author's line-
...they always are created in the global scope.
I mean how is the new Function("a", "b", "alert(y);"); nested within the test fn is in global scope?
In fact, accessing it from outside the test fn will simply result in
Uncought TypeError:tester is not a function
Please elucidate.
In your example, "created in the global scope" means that tester will not have closure over x from test:
function test(){
var x = 5;
tester = new Function("a", "b", "alert(x);"); // this will not show x
tester(5, 10);
}
When you new up a Function, it does not automatically capture the current scope like declaring one would. If you were to simply declare and return a function, it will have closure:
function test(){
var x = 5;
tester = function (a, b) {
alert(x); // this will show x
};
tester(5, 10);
}
This is the trade-off you make for having dynamically compiled functions. You can have closure if you write the function in ahead of time or you can have a dynamic body but lose closure over the surrounding scope(s).
This caveat doesn't usually matter, but consider the (slightly contrived) case where you build a function body as a string, then pass it to a function constructor to actually be evaluated:
function addOne(x) {
return compile("return " + x + " + 1");
}
function addTwo(x) {
return compile("return " + x + " + 2");
}
function compile(str) {
return new Function(str);
}
Because the function is instantiated by compile, any closure would grab str rather than x. Since compile does not close over any other function, things get a bit weird and the function returned by compile will always hold a closure-reference to str (which could be awful for garbage collection).
Instead, to simplify all of this, the spec just makes a blanket rule that new Function does not have any closure.
You have to create an object to expose via return inside the test() function for it to be global. In other words, add var pub = {} and name your internal functions as properties and/or methods of pub (for example pub.tester = new func) then just before closing test() say return pub. So, that way it will be publically available (as test.tester). It's Called the Revealing Module Pattern.
What it means is that inside the function you can only refer to global variables, as you've found. However, the reference to the function itself is still in the local scope where it was created.
I'm confused as to where the confusion is.
It says that the function will be in global scope...and therefore will only have access to its own scope and the global scope, not variables local to the scope in which it was created.
You tested it and it has access to its own scope and the global scope, not variables local to the scope in which it was created.
So where's the confusion?
Is it in your assigning of the function to the variable testing? testing is just a local variable with a reference to the function...that has nothing to do with the scope of the creation of the function.
Scope is lexical, and has to do with where the function is created, not what random variables a function reference happens to be assigned to at runtime. And the documentation is telling you that when you make a function this way it acts as if it was created in the global scope...so it's acting completely as expected.
Here's an illustration:
This:
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and errors as not defined
tester = new Function("console.log(y); console.log(x);");
}
Is similar to this:
var y = 10;
var tester;
function test()
{
var x = 5;
// also 10 and errors as not defined
tester = something;
}
function something()
{
console.log(y);
console.log(x);
}
NOT
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and 5...since x is local to the function creation
tester = function()
{
console.log(y);
console.log(x);
}
}

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.

how to access function caller context?

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

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.

Categories

Resources