Ways to break JavaScript Scope - javascript

JavaScript normally follows the function scope i.e. variables are accessible only within the function in which they are declared.
One of the ways to break this convention and make the variable accessible outside the function scope is to use the global window object
e.g.
window.myVar = 123;
My question is are there any other ways in JavaScript/jQuery to make the variable accessible outside the function scope?

Not with variable declarations, no. You can obviously declare a variable in an outer scope so that it's accessible to all descendant scopes:
var a; // Available globally
function example() {
a = "hello"; // References a in outer scope
}
If you're not in strict mode you can simply remove the var keyword. This is equivalent to your example:
// a has not been declared in an ancestor scope
function example() {
a = "hello"; // a is now a property of the global object
}
But this is very bad practice. It will throw a reference error if the function runs in strict mode:
function example() {
"use strict";
a = "hello"; // ReferenceError: a is not defined
}

As you wrote, variables are only visible inside the function in which they were defined (unless they are global).
What you can do is assign variables to the function object itself:
function foo() {
foo.myVar = 42;
}
console.log(foo.myVar); // outputs "undefined"
foo();
console.log(foo.myVar); // outputs "42"
But I would advise against it. You should really have a very good reason to do something like this.

You can define them as part of a global object, then add variables later
// Define your global object
var myObj = {};
// Add property (variable) to it
myObj.myVar = 'Hello world';
// Add method to it
myObj.myFunctions = function() {
// Do cool stuff
};
See the link below:
Variable declaration
Also, you can declare it without the var keyword. However, this may not be a good practice as it pollutes the global namespace. (Make sure strict mode is not on).
Edit:
I didn't see the other comments before posting. #JamesAllardice answer is also good.

Related

In javascript, are functions added to scope like variables are? [duplicate]

The var keyword in javascript causes a variable to be stored in the local scope. Without var variables belong to the global scope. What about functions? It's clear what happens when functions are declared like variables
var foo = function() {...}
but what scope does
function foo() {...}
belong to?
EDIT:
I realized I didn't ask quite the right question so as a follow up. In the outer most nesting is there a difference between the above two declarations and the following declaration?
foo = function() {...}
It belongs to the current scope, always. For example:
// global scope
// foo is a global function
function foo() {
// bar is local to foo
function bar() {
}
}
Regarding your second question, this:
foo = function() {...}
is an anonymous function expression assigned to a global variable (unless you're running is strict mode, then foo would be undefined). The difference between that and function foo() {} is that the latter is a function declaration (versus a variable declaration, which is assigned an anonymous function expression).
You might be interested in this excellent article about function declarations and function expressions: Named function expressions demystified.
Function declarations are always local to the current scope, like a variable declared with the var keyword.
However, the difference is that if they are declared (instead of assigned to a variable) their definition is hoisted, so they will be usable everywhere in the scope even if the declaration comes in the end of the code. See also var functionName = function() {} vs function functionName() {}.
Noteworthy distinction taking implicit globals into account:
var foo = function() {
// Variables
var myVar1 = 42; // Local variable
myVar2 = 69; // Implicit global (no 'var')
// Functional Expressions
var myFn1 = function() { ... } // Local
myFn2 = function() { ... } // Implicit global
function sayHi() {
// I am a function declaration. Always local.
}
}
Hopefully that clarifies a little. Implicit globals are defined if you forget a var before your assignment. Its a dangerous hazard that applies to variable declarations and functional expressions.
Your first example (var foo = function() {...}) is called an anonymous function. It is dynamically declared at runtime, and doesn't follow the same rules as a normal function, but follows the rules of variables.

Javascript: Global Context and Function Context Declarations

I have noticed something while playing around which has sparked a quick question.
When code is executed in the global/window context, any function declarations get added as methods to the window object.
But when I am in the context of another object, writing a function declaration does not add the method to my objects methods.
function functionInGlobalCtx() { // This will be added as a function to the window object
// code...
}
var myObject = {};
myObject.myObjectFunction = function () {
var $this = this; // The context here is the 'myObject' object
function functionHopefullyInMyObjectCtx() {
// code...
}
}
myObject.myObjectFunction();
Why does the function declaration exist as part of the window object but not the one for the object?
Is this simply 'how JavaScript works' (special rules apply to the global context?) or am I missing something?
Thanks.
Its actually understandable. Function is object. Myobject and myobjectfunction are two different objects. So are 'this' and function itself.
In your example, you define the hopefullyfunction in myobjfunction, not in myobject.
All functions declared globally will be attached to the global window object. That's how JavaScript works.
JavaScript only has function scope. So any function declared inside another function is private to the outer function.
The function functionHopefullyInMyObjectCtx can not be accessed from outside yet.
myObject.myObjectFunction = function () {
var $this = this;
function functionHopefullyInMyObjectCtx() {
// code...
}
}
Declaring a function inside a function does not attach it to the this automatically. However the function remains private and is only accessible within the scope it was declared in.
If you wanted to access the functionHopefullyInMyObjectCtx function from myObject.myObjectFunction then here is a way:
var myObject = {};
myObject.myObjectFunction = function () {
return {
functionHopefullyInMyObjectCtx: function() {
console.log('I got called');
}
}
}
obj = myObject.myObjectFunction();
//obj now has ref to the inner function
obj.functionHopefullyInMyObjectCtx();
I got called
Here is a good read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
I believe I have found the answer I was looking for.
Why are variables automatically assigned to the global/window object?
Because the JavaScript engine keeps a 'global environment record' of the items declared within the global scope (just like all scopes have an environment record holding all declaration information), but the difference between the global environment record and normal scope environment records is that the engine makes this record accessible within code (not just for engine internal use), through the window object!
If I am wrong or it's not quite right, please go ahead and correct me.
Thank you for your help.
https://es5.github.io/x10.html#x10.2.3
Difference between variable declaration syntaxes in Javascript (including global variables)?

Can I explicitly reference a JavaScript global variable that's been shadowed by a local with the same name?

I've seen tons of posts about the difference between global and function scope in JavaScript, far too many to link here. I've also seen my exact question asked about Python. So what I want to know is, how can I access a global variable when a "closer" scope also has a variable with the same name?
var a = "global";
function b(){
var a = "local";
var magic = ...; // somehow put "global" in magic
console.log(magic); // should print "global"
}
In browser only, I figured out that you can use window.a to specify the global. Is there anything that works server-side as well?
If it's really a global (i.e. not just in the outer scope), you can do this :
var magic = (function(name){return this[name]}).call(null, "a");
From ECMAScript's documentation (10.4.3) :
Entering Function Code
The following steps are performed when control enters the execution
context for function code contained in function object F, a caller
provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the
global object.
Note that you can't test this in jsFiddle, as the global scope isn't where you define your a.
Demonstration:
var a = "global";
function b(){
var a = "local";
var magic = (function(name){return this[name]}).call(null, "a");
document.body.textContent = 'a: '+JSON.stringify(magic); // print "global"
}
b();
There is no standard to do that. For my own purposes, I always put my variables in a container where the name resembles the project name:
var PROJECT = {}; // Put all "globals" for "project" into this container
That way, I can collect all variables in one place and if I need to pass the "globals" around, I have to pass at most one reference.
Using your code as the basis of the answer, you could use the following to actually pull the global version of "a" as opposed to the local version:
var a = "global";
function b(){
var a = "local";
var magic = this.a; // somehow put "global" in magic
console.log(magic); // should print "global"
}
b();
This will only work if you are calling the b() function in the form above. An example in which this will not work is as follows:
var a = "global";
function b(){
var a = "local";
var magic = this.a; // somehow put "global" in magic
console.log(magic); // should print "global"
}
b.call({a : "other context"});
In the case above, "other context" will be printed to the console.
You can access the global through the global object. In this case window and the local by name inside the current context.
Here is an fiddle example.
well I donĀ“t have a direct answer, but a workaround is to use a well established container, window for example
window['a'] = "global";
This isn't a very clean solution but it does the job

No need to define a variable twice

If a variable could be defined in a function, even if no value is assigned, it becomes a local variable
so, is testB() better programming?
var test = 'SNAP!'
function testA(boolean) {
if (boolean) var test = 'OK';
else var test = null;
alert(test);
}
function testB(boolean) {
if (boolean) var test = 'OK';
alert(test);
}
testA(true); // 'OK'
testB(true); // 'OK'
testA(false); // null
testB(false); // undefined, no error
In my specific case test's global value ('SNAP!') is neither expected nor required.
You can't declare variables conditionally.
Why?
The variable instantiation process occurs before the actual code execution, at the time the function is executed, those variables will be already bound to the local scope, for example:
function foo () {
if (false) {
var test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
When the function is about to be executed, identifiers of formal parameters, identifiers from variable declarations, and identifiers from function declarations within the function's body are bound to the local variable environment.
Variables are initialized with undefined.
Also, identifiers in the local scope shadow the others with the same name, higher in the scope chain, for example:
var test = 'global';
function bar () {
alert(test); // undefined, not "global", the local variable already declared
var test = 'xxx';
}
bar();
If the test variable were not declared anywhere, a ReferenceError will be thrown:
function foo () {
return test;
}
try {
foo(); // ReferenceError!!
} catch (e) {
alert(e);
}
That's one of the reasons about why for example, JSLint recommends only one var statement at the top of functions, because for example, the first snippet, will actually resemble this when executed:
function foo () {
var test; // var statement was "hoisted"
if (false) {
test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
Another reason is because blocks don't introduce a new lexical scope, only functions do it, so having a var statement within a look might make you think that the life of the variable is constrained to that block only, but that's not the case.
Nested function declarations will have a similar behavior of hoisting, they will be declared before the code execution, but they are initialized in that moment also:
function foo () {
return typeof bar;
// unreachable code:
function bar() {
//..
}
}
foo(); // "function"
If the variable does not need to be manipulated by any other functions, keep the variable inside a function with var foo;.
Otherwise, if it does need to be accessed and read in multiple scopes, keep it outside. But remember that when you keep it outside, it becomes global. That is, unless you wrap everything in a self executing function, which is the best way:
(function() {
var president='bush';
function blah() {
president='reagan';
}
function meh() {
president= 'carter';
}
document.getElementById('reagan').onclick=blah;
document.getElementById('carter').onclick=meh;
})();
alert( president ) // undefined
The above is perfect for a variable accessed by functions defined inside of that scope. Since there are 2 elements i click to set the president, it makes sense to define it outside both functions because they set the same variable.
So, If you are not dealing with multiple functions changing the exact same variable, keep them local to the function.
Is testB better programming? No, because it gives an unexpected result of "undefined" (at least, I was surprised by that) and it is hard to read.
Generally, variables should be limited to the scope that requires them, so if the "test" variable is not needed outside the function it should be declared local. To avoid confusion, declare your variable before using it:
function testC(boolean) {
var test;
if (boolean) {
test = "OK";
}
else {
test = null;
}
alert(test);
}
Unless you genuinely want to change the global scope version of "test", in which case don't use the var keyword inside the function.
If you ever find yourself using the same name for a local variable and a global variable you might consider renaming one of them.

Javascript question: about variable definition

I have no idea why it didn't work if I specify a variable with 'var':
like this:
var mytool = function(){
return {
method: function(){}
}
}();
And later I use it in the same template: mytool.method. This will output mytool was not defined.
But if I define it like this:
mytool = function(){
return {
method: function(){}
}
}();
Then it works.
Javascript has function scope. A variable is in scope within the function it was declared in, which also includes any functions you may define within that function.
function () {
var x;
function () {
// x is in scope here
x = 42;
y = 'foo';
}
// x is in scope here
}
// x is out of scope here
// y is in scope here
When declaring a variable, you use the var keyword.
If you don't use the var keyword, Javascript will traverse up the scope chain, expecting to find the variable declared somewhere in a higher function. That's why the x = 42 assignment above assigns to the x that was declared with var x one level higher.
If you did not declare the variable at all before, Javascript will traverse all the way to the global object and make that variable there for you. The y variable above got attached to the global object as window.y and is therefore in scope outside the function is was declared in.
This is bad and you need to avoid it. Properly declare variables in the right scope, using var.
The code you have doesn't show enough to demonstrate the problem. var makes the variable being defined 'local' so it will only be available within the same function (javascript has function level scoping). Not using var makes it global, this is almost always not what you want. You might need to rearrange your code to fix the scope issues.
I take it that you're using this inside of some function:
function setup_mytool() {
var mytool = function(){
return {
method: function(){}
}
}();
}
This creates the variable mytool in the function's scope; when the function setup_mytool exits, the variable is destroyed.
Saying window.mytool or window.my_global_collection.mytool will leave the variable mytool intact when the function exits.
Or:
var mytool;
function setup_mytool() {
mytool = function(){
return {
method: function(){}
}
}();
}
will also do what I think it is you're intending.
The reason why you are getting variable undefined errors is because when you use var, the declared variable is scoped to the surrounding context, meaning that the variable's lifetime is limited to the lifetime of the surrounding context (function, block, whathaveyou).
If you don't use var however, you are effectively declaring a variable tied to global scope. (Usually a Very Bad Idea).
So, in your code, the reason why you are able to access the mytool variable somewhere else in your template is because you tied it to global scope, where in the case of using var, the variable went out of scope because it must have been declared within a function.

Categories