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.
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.
When I type this in node.js, I get undefined.
var testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
=>undefined
Without var keyword, it passes (=>15). It's working in the Chrome console (with and without var keyword).
It doesn't work in Node when using var because testContext is a local of the current module. You should reference it directly: console.log(testContext);.
When you don't type var, what happens is that testContext is now a global var in the entire Node process.
In Chrome (or any other browser - well, I'm unsure about oldIE...), it doesn't matter if you use var or not in your example, testContext will go to the global context, which is window.
By the way, the "global context" is the default this of function calls in JS.
The key difference is that all modules (script files) in Node.js are executed in their own closure while Chrome and other browsers execute all script files directly within the global scope.
This is mentioned in the Globals documentation:
Some of these objects aren't actually in the global scope but in the module scope - this will be noted.
The vars you declare in a Node module will be isolated to one of these closures, which is why you have to export members for other modules to reach them.
Though, when calling a function without a specific context, it will normally be defaulted to the global object -- which is conveniently called global in Node.
function testFunction() {
return this;
}
console.log(testFunction() === global); // true
And, without the var to declare it, testContext will default to being defined as a global.
testContext = 15;
console.log(global.testContext); // 15
As mentioned in the document
var something inside an Node.js module will be local to that module.
So, it is going to be different as the var testContext is in module context and the context of this is global.
You can alternatively, use:
global.testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
I believe the problem has to do with the this key word. If you do console.log(this) you will see that testContext is not defined. You may want to try:
this.testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
I read some JS module design patterns recently. I came across this small code snippet as below.
(function(window) {
var Module = {
data: "I'm happy now!"
};
window.Module = Module;
})(window);
Still not quite understand this code well, my questions are:
How to use/call this module outside this particluar JS file? Need I
assign a variable to this module? e.g. var module1 = (...)(...);
Could anyone explain what the window parameter here stands for?
Is it a good practice to have two/three such kind of modules in the
same file?
The main reason to create an anonymous function in this case is to prevent global object pollution. It's not really a module pattern.
The problem arise when you declare a variable. Without the function scope, the variable will be added to the global object (window). If you were to declare the variables within a function. It would add the variable to the function scope without polluting the global object window.
What happen is that a javascript file could add a variable named foo and on a different file use that variable named foo too. Unless you really want to have a variable shared by two javascript files, it would probably create conflicts and bug that are difficult to fix.
For example: a.js
var foo = "one"
b.js
var foo = "two"
c.js
alert(foo)
In this case, the alert box might show either "one" or "two", depending of the order in the javascript files are included.
But having this a.js:
(function () {
var foo = "one"
})()
b.js
(function () {
var foo = "two"
})()
c.js
(function () {
alert(foo)
})()
This would create an error as you cannot alert a non declared variable.
One way to detect undefined variables, is to make sure to execute the javascript code in strict mode.
To do that, add the string "use strict" at the top of the file or function.
function () {
"use strict"
...
}
Undeclared variable will raise errors and it should be possible to fix the code that way.
Also, if you forget to declare a variable with the var keyword, it might end up adding the variable to the global scope even if the code is scoped into a function. The only way to prevent global scope pollution is to run the code in strict mode.
In the code snippet that you provided, the module with name Module is explicitly added to the window object. You can access the window object from any place in javascript unless the window name is ghosted by an other variable.
Now, back to the modules. If you want to define modules, it can be done in many ways. As an exemple, you could create an object on the window object called modules. In this object, you'll insert your modules.
module.js
window.modules = {}
foo.js
(function (window) {
var module = {}
...
window.modules.foo = module
})(window)
This variant isn't super good as you have to manually add the module to the modules object. You have to manually modify the window object, and that can be subject to errors.
modules.js
window.modules = {}
function define(name, constructor) {
var module = {exports: {}}
constructor(module)
window.modules[name] = module.exports
}
function require(name) {
return window.modules[name]
}
foo.js
define("foo", function (module) {
module.exports.one = function () {
return 1
}
module.exports.plus = function (a, b) {
return a + b
}
})
bar.js
define("bar", function (module) {
module.exports = function () {
var foo = require("foo")
return foo.plus(foo.one(), foo.one())
}
})
This is a module definition that looks a bit like module defined with http://requirejs.org/. It is quite basic as it doesn't take into account module dependencies so if bar is loaded and used before foo. Then the require method won't be able to return the module.
Also, if you want to store modules without having them visible into the global scope, you can only define the require and define method on the window object and hide modules into an anonymous scope like this:
(function (window) {
var modules = {}
function define(name, constructor) {
var module = {exports: {}}
constructor(module)
modules[name] = module.exports
}
function require(name) {
return modules[name]
}
window.define = define
window.require = require
})(window)
This way, define and require are the only function that can give you access to modules. Other modules won't be able to modify other modules without requiring them first. This can be useful when using third parties script that could conflict with your module system.
In fact this is not a module, but a Self-Invoking Ananymous function or an Immediate function which gets an object in parameter and assign a Module property to it:
The page window is a parameter passed to this function.
So an object named Module containing a data property is assigned to window.
JavaScript Self-Invoking Functions:
A self-invoking expression is invoked (started) automatically, without being called.
Function expressions will execute automatically if the expression is
followed by ().
You cannot self-invoke a function declaration.
You have to add parentheses around the function to indicate that it is
a function expression
So As you can see Immediate Functions can't be called as its name states it will be immediately executed and by its self, no other function or scope can execute it.
For better reference take a look at :
Javascript Self Invoking Functions.
Self-Invoking Functions section in JavaScript Function Definitions.
And concerning your last question about its benefits and good practices as shown on the given Article reference:
Where to use self-executing functions?
One obvious situation is when you want to auto-run a function like I
showed in above example but that is trivial. If you are in a situation
where you want to run a piece of code repeatedly like updating
something in the database based on user interaction or fetching
records from database every 10 seconds or you want to load new stories
via ajax similar to how facebook does on its homepage or some other
similar situation, one would normally go for setInterval function
something like this:
setInterval(doStuff, 10000);
Above, doStuff function will get called every 10 seconds. That is the
normal approach most developers seem to go with. However, there is a
huge problem with that.
The setInterval will call doStuff function exactly at specified time of 10 seconds again and again irrespective
of whether doStuff function actually finished doing what it is
supposed to do. That is bad and will certainly get you into unexpected
results.
That is one example of where setInterval is "bad" and should be
avoided.
This is exactly where self-executing functions come in handy. We can
do the same task with the help of self-executing function along with
setTimeout like this:
function foo(){
// your other code here
setTimeout(foo, 10000);
}();
This code will also repeat itself again and again with one difference.
setTimeout will never get triggered unless doStuff is finished. A much
better approach than using setInterval in this situation.
Calling it from another file:
And if this function is on another file it will be called automatically if this file is included.
Why do we use Self-Invoking Functions in JavaScript?
And if you ask yourself why do we use these functions, self-invoked function are used to manage Variable Scope.
Take a look at the answer here for further information.
I use eval() to execute javascript that is returned by an ajax request from the server. However, I cant call a function that was created with eval() and got a ReferenceError: function is not defined.
Is it something normal that functions inside a javascript that was executed with eval() cannot be accessed? Is there a way to access such functions?
I think this simple jsFiddle illustrate the problem: http://jsfiddle.net/M2GLs/
The created function isn't in the correct scope. So your onclick can't 'see' it. Use
window.addFeatureToTable = function() {
// definition
}
to force it in the window-scope.
Working JsFiddle
To answer your question in the comment:
What you actualy have is something like this code:
function a()
{
function b(where) {
alert('b can be called inside a, but not outside, we are now ' + where);
}
b('inside');
}
a();
b('outside');
b is defined in the scope of a, and can only be accessed within this scope (demo). In your case the function-definition is within an eval, but the same rule aplies there. In that case within the scope of function(r). You can't access the scope of this function from within the a.onclick, so you have to change the function-definition. Alternatively you can bind the on-click just after the eval (jsFiddle), since it is then still in scope:
js = "function someFunction() { alert('function called') }"
eval(js)
document.getElementById('myA').onclick = someFunction;
If I want to give a JavaScript variable global scope I can easily do this:
var myVar;
function functionA() {
myVar = something;
}
Is there a similarly simple and clean way -- without creating an object -- to separate the "declaring" and the "defining" of a nested function? Something like:
function functionB; // declared with global scope
function functionA() {
functionB() { // nested, would have local scope if declared here
CODE;
}
}
I should clarify that I'm referring to the scope of the function name itself -- so that if it is in an iframe it can be called from a script in the parent document. (Nothing to do with scope of variables inside nested functions.)
You can create global variables and functions by creating instances on the window object:
function _A()
{
// scoped function
function localFunctionInTheScopeOf_A()
{
}
// global function
window.globalFunctionOutsideTheScopeOf_A = function ()
{
};
}
In your case, though, all you need to do is this:
var myFn; // global scope function declaration
function randomFn()
{
myFn = function () // global scope function definition
{
};
}
Note: It is never a good idea to clog up the global scope. If you can; I'd recommend that you re-think how your code works, and try to encapsulate your code.
Perhaps I'm misunderstanding the question, but it sounds like you want something like this:
var innerFunc;
function outerFunc() {
var foo = "bar";
innerFunc = function() {
alert(foo);
};
}
You cannot globalize variables/functions cross windows/iframes that way. Each window/iframe has it's own global scope and to target variables/functions in another window/iframe, you need explicit accessor code and conform to the same origin policy. Only variables/functions inside the windows/iframes global scope are accessible.
code in top window.
var iframe = document.getElementById('iframeId');
var iframeContext = iframe.contentWindow || iframe;
// this will only work if your iframe has completed loading
iframeContext.yourFunction();
You could also possibly define functions/variables in the top window instead and simply work in one scope by binding the stuff you need from the iframe through a closure. Again, assuming you meet the same origin policy. This will not work cross domain.
code in iframe.
var doc = document;
var context = this;
top.myFunction = function(){
// do stuff with doc and context.
}
It is also important to note, that you need to check if your iframe content and it's scripts are fully loaded. Your top page/window will inadvertidly be done and running before your iframe content is done, ergo variables/functions might not be declared yet.
As for exposing a private function, others have awnsered this, but copy/pasting for completeness.
var fnB;
var fnA = function(){
var msg = "hello nurse!";
fnB = function(){
alert(msg);
}
}
I have the habbit of declaring stand alone functions as variables (function expression) and only use function statements to signify constructors/pseudo-classes. It also avoids a few possible embarrasing mistakes.. In any case, fnB resides in the global scope of the iframe and is available to the top window.
Why exactly you want this beats me, seems it makes matters more complicated to debug or update a few months later.
You can kind of do what you want.
You can create a function that acts like a namespace for properties and methods, and then you could essentially call either...
functionB();
or
functionA.functionB();
There is an article on how to do it here:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/JavaScript-Name-Spacing/
In response to the update...
Is the iframe on the same domain as the parent site? You can't call JavaScript across the domain boundary, which may explain the problem.