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.
Related
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 have a simple thing like this:
function init() {
var $something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
I thought this would work, but it doesn't, console says that $something is not defined. What's the issue?
Many thanks!
$something is defined within the scope of the function 'init' so you will only ever be able to access it from within that function as it is. If you wanted to get a value back, you could return it, like so:
function init() {
var $something = 'something';
return $something;
}
jQuery(document).ready(function($) {
var $something = init();
alert($something);
}
Notice that both those variables have the same name (normally a bad idea). They are each defined within their own scope, and thus they are totally different variables.
An alternate pattern might be to wrap the entire thing and use that scope, like so:
(function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
})();
That way you have a single variable, but you avoid polluting the global namespace.
Edit:
In response to your comment, the above could be written like:
var newscope = function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
}
newscope();
But I have defined the function AND called it at basically the same time without having to give it a name.
This is a scoping issue.
$something is defined within the scope of the init() function, and therefore, it will be disposed of when the init() function completes.
Vars declared with var will be local to the closure in which they are declared. As you've found, this means, therefore, that outside of that closure they are not reachable.
There's many ways round this and each means a different design pattern. Here's one pattern you could use:
({
init: function() {
this.something = 'hello';
jQuery(function() { this.dom_ready(); }.bind(this));
},
dom_ready: function() {
//DOM code here
alert(this.something); //hello
}
}).init();
Here I declare several methods of an object, or namespace. Since they belong to, and are called in the context of, this one object, they communicate between one another with properties rather than variables.
Variables are thus demoted to being useful only within (but never outside of) the closure in which they are declared.
One advantage of this pattern is that you can separate any code that needs to wait for the DOM to be ready from any code that doesn't. This is achieved by having a dedicated dom_ready method.
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.
function validateHistoryId(input) {
$.getJSON('request.php', {"action": "historyExists", "history_id" : input.value},
function(data) {
console.log(input.value);
}
);
}
i use jQuery ajax call from javascript function. I tried the above code and it works, but I don't know will it cause any problems in the future.
I want to know, can ajax callback function see variables of it's parent function? and is it a bad practice to do so?
This is the JavaScript functional way to doing things. It's called closure: functions carry variable pointers from their current scope and from any other parent scope.
So it's not only good practice, but this is the pattern you should normally follow instead of pushing around parameter objects, etc.
Please notice that the "this" reference is special, it is never closured (as opposed to any other references) and thus always point the global object in anonymous functions.
In fact a developer needs some time to fully employ the power of the closure feature - this is a basic example you just written. In more advanced (and not solely async) scenarios closure helps you to create "fire and forget" behavior, or can provide you with "private" variables that are not accessible from client code (useful for library development). Closure also help you isolate your code so that it will not mess with the global scope.
1) Example: how to create protected variables with closures. You are able to acccess two methods that can access "protectedVariable" but you are not able to access it yourself - so the control is guaranteed.
function protectedScope(initData) {
var protectedVariable = initData;
return {
getter: function() { return protectedVariable; }
setter: function(v) { protectedVariable = v; }
}
}
var methods = protectedScope(10);
console.log(methods.getter());
2) isolation: the following code will not garbage the global scope even with "global" function definitions
var API = (function() {
var protectedVariable = 0;
var myNotGlobalFunction() {
return protectedVariable;
}
return myNotGlobalFunction;
})();
I have made a Web page using jquery and php where all files are used in a modular style. Now I have two JavaScript files which must communicate with each other. One Script generates a variable (id_menu_bar) which contains a number. I want that this variable gets transported to the second JavaScript and is used there.
How do I make that?
Here the Script
menu_bar.js
$(document).ready(function() {
function wrapper_action(id_menu_bar) {
$(".wrapper").animate({height: "0px"});
$("#changer p").click(function() {
$(".wrapper").animate({height: "300px"});
});
}
$("#select_place li").live("click", function() {
var wrapper_id = $(".wrapper").attr("id");
var id_place = this.id;
if (wrapper_id != "place")
{
$("#select_level li").remove();
$("#select_building").load("menu_bar/menu_bar_building.php?placeitem="+id_place, function() {
$("#select_building li").click(function() {
var id_building = this.id;
if (wrapper_id != "building")
{
$("#select_level").load("menu_bar/menu_bar_level.php?buildingitem="+id_building, function() {
$("#select_level li").click(function() {
var id_level = this.id;
wrapper_action(id_level);
});
});
}
else if (wrapper_id == "building")
{wrapper_action(id_building);}
});
});
}
else if (wrapper_id == "place")
{wrapper_action(id_place);}
});
});
if the variable id_menu_bar is in global scope then it can be used by another script on the page.
jQuery's $.data() is also good for storing data against elements and means that you do not need to use a global variable and pollute the global namespace.
EDIT:
In response to your comment, there is a difference in how you declare variables that determines how they are scoped in JavaScript.
Global Variables
Outside of a function declaring a variable like
var myVariable;
or
myVariable;
will make no difference - both variables will have global scope. In fact, the second approach will give a variable global scope, even inside of a function. For example
function firstFunction() {
// Local scope i.e. scoped to firstFunction
var localVariable;
// Global scope i.e. available to all other JavaScript code running
// in the page
globalVariable = "I'm not really hiding";
}
function secondFunction() {
// I can access globalVariable here but only after
// firstFunction has been executed
alert(globalVariable); // alerts I'm not really hiding
}
The difference in this scenario is that the alert will fail and not show the value for globalVariable upon execution of secondFunction() until firstFunction() has been executed, since this is where the variable is declared. Had the variable been declared outside of any function, the alert would have succeeded and shown the value of globalVariable
Using jQuery.data()
Using this command, you can store data in a cache object for an element. I would recommend looking at the source to see how this achieved, but it is pretty neat. Consider
function firstFunction() {
$.data(document,"myVariable","I'm not really hiding");
globalVariable = "I'm not hiding";
}
function secondFunction() {
// alerts "I'm not really hiding" but only if firstFunction is executed before
// secondFunction
alert($.data(document, "myVariable"));
// alerts "I'm not hiding" but only if firstFunction is executed before
// secondFunction
alert(globalVariable);
}
in this scenario, a string value "I'm not really hiding" is stored against the document object using the key string myVariable in firstFunction. This value can then be retrieved from the cache object anywhere else in the script. Attempting to read a value from the cache object without having first set it will yield undefined.
Take a look at this Working Demo for more details.
For reasons not to use Global Variables, check out this article.
Does it have to ve a JavaScript variable?
Can you store the information using the .data() function against a relevant element?