I would like to see the content of a closure in JavaScript.
In the following code, I would like to see the closure of the function closure_f returned by the anonymous function. The local scope of the anonymous function must be stored somewhere, I would like to see where it is stored. How can this be done in Node or in the browser?
var closure_F = (function(){
var info = "info-string";
var f1 = function(){
console.log(info);
};
return f1;
}());
closure_F(); // logs 'info-string' as expected.
console.log(closure_F); // This did not provide any valuable information.
WAY 1: Internal property [[Scope]]
You can modify your code by adding console.dir and then run it in the Chrome Dev Console:
var closure_F = (function(){
var info = "info-string";
var f1 = function(){
console.log(info);
};
return f1;
}());
closure_F();
console.dir(closure_F);
// console.dir prints all the properties of a specified JavaScript object
If you open the console you will see that it prints all the properties of the object (function), including the internal property [[Scopes]].
This internal property [[Scopes]] will contain any surrounding scopes of the closure_f, and its closure. See example:
Note: [[Scope]] is an internal implementation of JS and cannot be programatically accessed within the program.
WAY 2: Setting a breakpoint - debugger
Another way to see the Closure of a function is to add a debugger statement and create a break point in the function who's closure you want to inspect.
As an example you can run this in the console:
function createClosure (){
var secret = "shhhhh";
return function inner(){
debugger;
console.log(secret);
};
};
var innerFunction = createClosure();
innerFunction();
www.ecma-international.org >> [[Scope]] >> Table 9
The local scope of the anonymous function must be stored somewhere
That's an implementation detail of the JavaScript runtime. It isn't stored anywhere that is exposed to the JavaScript program.
How can this be done in Node or in the browser?
Dedicated debugging tools can inspect the data there. Set a breakpoint on the console.log call.
Note that optimisations mean that only variables used within the returned function will be visible.
Related
Might be a silly question but it still got me a bit stuck, not being 100% sure of the answers.
So I have an index.html file which calls a function in an example.js-file (just adding it for clarification):
function sinusGraph() {
var plotstart = 0,
plotrange = 20,
stepsize = 0.5; // not in use right now
var yValues, xValues;
function sinusValues(startinput, stopinput)
{
return d3.range(startinput, stopinput+1).map(function(i)
{
return Math.sin(i);
})
};
function xAxisValues(startinput, stopinput)
{
return d3.range(startinput, stopinput+1).map(function(i)
{
return i;
})
};
xValues = xAxisValues(plotstart, plotrange);
yValues = sinusValues(plotstart, plotrange); };
Writing, for example, "xValues" with the variables declared in the browser's returns "xValues is not defined(...)".
Removing "var xValues" letting it be a global variable does return the value.
My questions:
The browser's Tool Console can't see non-global variables within functions?
If that is the case, then is this a good tool to look for potential global variables that you have created by mistake?
Is there any way to view these variables in the browser's tool console, other than using console.log(myVariable) within the function where it is declared?
The browser's Tool Console can't see non-global variables within functions?
Yes.
Here is why it can't work: Local variables of a function only exist while the function is running. Code you type into the console will either be executed before or after the function is executed.
Even if you were able to access local variables after a function was executed:
which value would you expect to get when you type varname in the console? The inital value, the last value or all values?
what if multiple functions have a local variable with the same name?
what if the same function was already executed multiple times?
You can only inspect the current state of your application.
If that is the case, then is this a good tool to look for potential global variables that you have created by mistake?
No. You should use a linter such as ESLint that warns you if you forget e.g. a var declaration. Also enable strict mode: Assignments to undeclared variables will throw an error then.
Is there any way to view these variables in the browser's tool console, other than using console.log(myVariable) within the function where it is declared?
Set a break point in your code, either through the devtools or via debugger. Code execution will pause at the breakpoint and the console has access to everything that is accessible at the breakpoint.
The console has to run the code some scope (and it can't access a scope defined by a function that isn't running anyway).
Set a breakpoint inside the function, to pause execution there. When that breakpoint is triggered, the console's scope will be to that function.
I'm using PhantomJS v2.0 and CasperJS 1.1.0-beta3. I want to query a specific part inside the page DOM.
Here the code that did not work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc());
this.echo("value: " + del);
And here the code that did work:
var del=this.evaluate(function()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
});
this.echo("value: " + del);
It seems to be the same, but it works different, I don't understand.
And here a code that did also work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc);
this.echo("value: " + del);
The difference here, I call the myfunc without the '()'.
Can anyone explain the reason?
The problem is this:
var text = this.evaluate(myfunc());
Functions in JavaScript are first class citizen. You can pass them into other functions. But that's not what you are doing here. You call the function and pass the result into evaluate, but the result is not a function.
Also casper.evaluate() is the page context, and only the page context has access to the document. When you call the function (with ()) essentially before executing casper.evaluate(), you erroneously try to access the document, when it is not possible.
The difference to casper.evaluate(function(){...}); is that the anonymous function is defined and passed into the evaluate() function.
There are cases where a function should be called instead of passed. For example when currying is done, but this is not applicable to casper.evaluate(), because it is sandboxed and the function that is finally run in casper.evaluate() cannot use variables from outside. It must be self contained. So the following code will also not work:
function myFunc2(a){
return function(){
// a is from outer scope so it will be inaccessible in `evaluate`
return a;
};
}
casper.echo(casper.evaluate(myFunc2("asd"))); // null
You should use
var text = this.evaluate(myfunc);
to pass a previously defined function to run in the page context.
It's also not a good idea to use reserved keywords like del as variable names.
I was reading Chapter 2: this All Makes Sense Now! from You Don't Know JS, and decided to do this experiment.
I have this simple enough script foo.js:
var a = 'foo';
var output;
// lets find a way to output strings in both
// Chrome and Node.js
if (typeof alert === 'undefined') {
output = console.log;
} else {
output = alert;
}
function getA() {
return this.a;
}
var foo = getA();
output(foo);
I am expecting following things when getA() is called:
Since the call site of getA is in global scope, getA() will be bound to global object.
Since var a is declared in global scope, I take it that global object will have a property named a, and this property is same as the variable a.
Because of that, I expect this.a to refer to variable a.
Thus I expect output(foo) to print the string foo.
However, when run in Node.js (non-strict mode), this is the output:
$ node foo.js
undefined
Then I included the same script in a simple HTML page, and loaded it in chrome.
<html>
<head>
<script src="foo.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
Chrome alerts the string foo, just as expected.
Why does the output of Chrome differ from Node.js?
Since the call site of getA is in global scope, getA() will be bound to global object.
This is a misunderstanding of the this binding rules from my book. The call site's location (aka "in global scope") is entirely irrelevant. It's the manner in which the call is made, and only that.
It's not where getA() happens that matters, but that getA() is a plain normal function call. THAT is what determines that you'll get the global object bound to the this for that call.
The other answers here are correct... the scope your node main program runs in is actually a module (function wrapped), not a real global scope.
Since the call site of getA is in global scope, getA() will be bound to global object.
no, that's not true for node - your script is wrapped into a function here so your example is actually this code:
(function (exports, require, module, __filename, __dirname) {
var a = 'foo';
var output;
// lets find a way to output strings in both
// Chrome and Node.js
if (typeof alert === 'undefined') {
output = console.log;
} else {
output = alert;
}
function getA() {
return this.a;
}
var foo = getA();
output(foo);
})(exports, require, module, 'file.js', '/dir/name');
NodeJS behaves differently than browsers. The top-level scope is not the global scope, it's the scope within that file or module. Drop the "var" and your code will work (a will become truly global) in a node environment and it will console.log the string 'foo'.
See the following page for a full reference: http://nodejs.org/api/globals.html
OR
How to use global variable in node.js?
I have several script blocks depend on each other. I need to perform them in one scope.
My attempt:
var scopeWrapper = {};
with(scopeWrapper) {
(function() {
this.run = function(code) {
eval(code);
};
}).call(scopeWrapper);
}
scopeWrapper.run('function test() { alert("passed"); }');
scopeWrapper.run('test();');
I get 'test is not defined' error. It seems that the code is executed in different scopes.
Why is this happening?
Edit: Bergi pointed out my original answer was wrong, he is correct. Since eval runs in its own scope and the function constructor still runs in function scope according to the spec this is not possible with either.
While I have done this sort of thing myself several times with node.js using the vm module where you get much finer grain of control over where your code executes, it seems browsers require a different approach.
The only way you can share variables in such a way is to do so in the global scope of JavaScript execution (possibly, in an iframe). One way you could do this is script tag injection.
function run(code){
var sc = document.createElement("script");
sc.setAttribute("type","text/javascript");
sc.innerHTML = code;
document.body.appendChild(sc);
}
run("var x = 5");
run("document.write(x)");
(here is this code in action)
As for the scope wrapper, instead of injecting them in the same frame inject them in another iframe. That will scope their window object to that iframe and will allow you to share context.
I humbly apologize for my previous answer, I misread the spec. I hope this answer helps you.
I'm leaving my previous answer here because I still believe it provides some insight into how eval and the Function constructor work.
When running code in non-strict mode eval runs in the current context of your page
After your function declaration is done, the scope it was declared in dies, and with it the function.
Consider using the Function constructor and then .calling it
In your case that would be something like:
var scopeWrapper = {};
scopeWrapper.run = function(code){
var functionToRun = new Function(code);
functionToRun.call(scopeWrapper);
}
scopeWrapper.run('this.test = function() { alert("passed"); }');
scopeWrapper.run("this.test()")
Here is a reference directly from the spec:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
Initialize the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
If this code is run in the node.js consider using the vm module. Also note that this approach is still not secure in the way it'll allow code you run to change your code.
test only exists in the scope of this.run and only at call time :
// global scope
(function(){
// local scope (equivalent of your "run" function scope)
eval('function f(){};');
console.log(f); // prints "function f(){}"
})();
console.log(f); // prints "ReferenceError: f is not defined"
Each call of run creates a new scope in which each code is evaluated separately.
I'm working my way through the Eloquent JavaScript Book and in it there is the following code:
function createFunction(){
var local = 100;
return function(){return local;};
}
When I run this via the node console (run node from command prompt) by calling createFunction(), I get [Function] as a returned value. However, according to the book I should get 100.
So my two questions: Why is this? and Second, is running these little examples in the node console a bad idea for testing JS code?
You need to call the response of createFunction().
createFunction()();
The first invocation (()) calls createFunction() and returns the inner function, which the second invocation executes and returns the local variable which was closed over.
Running small examples in a node console (or any other) is fine, so long as you know the environment, e.g. a browser's console is generally eval()'d, which can create side effects, such as how delete can apparently delete variables, not just object properties.
You get 100 by invoking the return value of createFunction, which is itself a function.
createFunction()();
...or perhaps more clearly...
var new_func = createFunction();
new_func();
function createFunction(){
var local = 100;
// v---v-----------------------v return a function from createFunction
return function(){return local;};
}
// v------- the returned function is assigned to the new_func variable
var new_func = createFunction();
// v------- the returned function is invoked
new_func();
For those that have a similar problem, I completely missed the double () so the call looks like createFunction()().