What's the proper way to implement constant variable? - javascript

I found the following in the MDN
// THIS WILL CAUSE AN ERROR
function f() {};
const f = 5;
// THIS WILL CAUSE AN ERROR ALSO
function f() {
const g = 5;
var g;
//statements
}
But there is no description of proper way to do. So how should I implement?

Quoting from the const's MDN Docs,
The current implementation of const is a Mozilla-specific extension and is not part of ECMAScript 5. It is supported in Firefox & Chrome (V8). As of Safari 5.1.7 and Opera 12.00, if you define a variable with const in these browsers, you can still change its value later. It is not supported in Internet Explorer 6-10, but is included in Internet Explorer 11. The const keyword currently declares the constant in the function scope (like variables declared with var).
Firefox, at least since version 13, throws a TypeError if you redeclare a constant. None of the major browsers produce any notices or errors if you assign another value to a constant. The return value of such an operation is that of the new value assigned, but the reassignment is unsuccessful only in Firefox and Chrome (at least since version 20).
const is going to be defined by ECMAScript 6, but with different semantics. Similar to variables declared with the let statement, constants declared with const will be block-scoped.
const is not part of ECMA Script 5, so it may not have to supported by all the environments and browsers.
To have the similar behavior, you can see macek's answer.

ECMAScript 5 doesn't really support this. There's a couple tricks you could use though.
You could try something like this
var f = (function() {
var g = 5;
return function() {
return g;
};
})();
Now, every time you run f(), you will also get 5, and it's impossible to change the value of g
Another thing you could do is use Object.defineProperty
var f = {};
Object.defineProperty(f, "g", {
configurable: false,
value: 5
});
f.g; // 5
Even if you try to change the property, f.g will stay set to 5
f.g = 10;
f.g; // 5
If you're working with CommonJS style modules, you sort of get this functionality for "free". Again, g is non-configurable and always guaranteed to be 5.
a.js
var g = 5;
function foo() {
return g;
}
module.exports = foo;
b.js
var a = require("./a");
a(); // 5

Related

Can I prevent accidental overwrite of local variables in TypeScript / JavaScript?

Today I wasted an hour debugging a trivial issue, where a local variable named server was being initialized and configured - then, on one of the last lines in the same file, accidentally it was being redeclared, e.g. by another var server = ... statement, effectively creating a new variable named server, thus causing the previous variable to fall out of scope; yet, because these were the same type of variable, with the same name, everything else continued to work, making this fairly hard to debug.
Is there a TypeScript or JavaScript language feature, that prevents this sort of thing?
My thinking is that, declaring two variables with the same name, in the same scope, ought not to be allowed at all.
Perhaps there's a linter or some quality assurance tool that has the ability to check for and prevent this sort of thing? (and perhaps other "bad" patterns?)
Use let everywhere possible.
A let variable cannot be used before its declaration:
var x = 3;
function f() {
console.log(x); // ReferenceError, x is not defined
let x = 5;
}
Two options:
Use ECMA Script 6 and let.
Use jslint with var.
There is a closed issue about this on the GitHub/Microsoft/Typescript page. The recommendation is to target ECMA Script 6 and use let.
ECMA Script 6 with let
In ECMA Script 6 this would create an error:
let x = "foo";
let x = "bar"; // TypeScript with ECMA 6 will complain here
console.log(x);
Duplicate declaration, x
JSLint with var
Also, though the following won't throw a TypeScript error, the jslint tool will complain about it, even if you aren't using strict.
(function () {
var x, y;
x = "foo";
y = "foo";
function sayMsg() {
// jslint will complain here
var y = "bar";
}
sayMsg();
// jslint will also complain here
var x = "bar" + y;
}());
This is what jslint will tell you:
Redefinition of 'y' from line 3.
Combine this with the previous 'var' statement.

When should I use try/catch instead of functions to declare non global variables in JavaScript?

Consider following codes:
try{
throw undefined;
}
catch(someVariable){
someVariable = 10;
// do whatever you want with someVariable
// someVariable will not be a global object at all. (without help of any function scope)
}
// someVariable is no longer valid here
Why some people use this syntax instead of functions when they don't want to declare global variables?
Additional notes:
I've seen this syntax a lot, but the most important one is Google traceur
try {
throw undefined;
} catch (a) {
a = 10;
}
that it is generated because of the following ecma script 6 syntax:
{
let a = 10;
}
Google traceur is a ECMA Script 6 parser on older browsers that currently have no support for new JavaScript features.
Well, there is a difference: Why do catch clauses have their own lexical environment?
try {
throw undefined;
} catch(someVariable) {
someVariable = 10; // someVariable will not be global
var someOtherVariable = 5; // but any declared 'var' will be
}
// someVariable is no longer valid here
console.log(someOtherVariable); // still 5
As you can see, this is just the same behaviour as you would have with
{
let someVariable = 10;
var someOtherVariable = 5;
}
console.log(someVariable, someOtherVariable);
It's basically a hack by the Traceur transpiler to create lexical environments for single variables in EcmaScript 5. Probably Google also optimized their browser to recognize this pattern, which explains why it's quite fast on Chrome.
In manually written code, this definitely is to be a bad practise - and without comments, no one knows what this does. Use an IEFE for readability.
It seems a really bad practice. Exceptions usually slow the execution, and making the system to raise an error an then re-use the error variable is overkill.
UPDATE: For me this is a bit shocking: it seems the code is faster when using a try-catch in Chrome, but slower in Firefox and faaaar slower in IE 10:
Here is a test I've just created.
Anyway, I think the proper way is using IIEFs like this:
(function () {
var someVariable=...
...
})();
which it's more elegant. Another difference is only the variable with the error is local, any other variable created on the catch block will be global.
You should use try/catch when it is possible that the operations will cause some error (IOException,...). If there is an error in try it will do what it is in catch. If there is no possibility of error use function.

How JavaScript closures are garbage collected

I've logged the following Chrome bug, which has led to many serious and non-obvious memory leaks in my code:
(These results use Chrome Dev Tools' memory profiler, which runs the GC, and then takes a heap snapshot of everything not garbaged collected.)
In the code below, the someClass instance is garbage collected (good):
var someClass = function() {};
function f() {
var some = new someClass();
return function() {};
}
window.f_ = f();
But it won't be garbage collected in this case (bad):
var someClass = function() {};
function f() {
var some = new someClass();
function unreachable() { some; }
return function() {};
}
window.f_ = f();
And the corresponding screenshot:
It seems that a closure (in this case, function() {}) keeps all objects "alive" if the object is referenced by any other closure in the same context, whether or not if that closure itself is even reachable.
My question is about garbage collection of closure in other browsers (IE 9+ and Firefox). I am quite familiar with webkit's tools, such as the JavaScript heap profiler, but I know little of other browsers' tools, so I haven't been able to test this.
In which of these three cases will IE9+ and Firefox garbage collect the someClass instance?
As far as I can tell, this is not a bug but the expected behavior.
From Mozilla's Memory management page: "As of 2012, all modern browsers ship a mark-and-sweep garbage-collector." "Limitation: objects need to be made explicitly unreachable".
In your examples where it fails some is still reachable in the closure. I tried two ways to make it unreachable and both work. Either you set some=null when you don't need it anymore, or you set window.f_ = null; and it will be gone.
Update
I have tried it in Chrome 30, FF25, Opera 12 and IE10 on Windows.
The standard doesn't say anything about garbage collection, but gives some clues of what should happen.
Section 13 Function definition, step 4: "Let closure be the result of creating a new Function object as specified in 13.2"
Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)
Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically
surrounds the inner Lexical Environment.
An outer Lexical Environment may, of course, have its own outer
Lexical Environment. A Lexical Environment may serve as the outer environment for multiple inner Lexical
Environments. For example, if a Function Declaration contains two nested Function Declarations then the Lexical
Environments of each of the nested functions will have as their outer Lexical Environment the Lexical
Environment of the current execution of the surrounding function."
So, a function will have access to the environment of the parent.
So, some should be available in the closure of the returning function.
Then why isn't it always available?
It seems that Chrome and FF is smart enough to eliminate the variable in some cases, but in both Opera and IE the some variable is available in the closure (NB: to view this set a breakpoint on return null and check the debugger).
The GC could be improved to detect if some is used or not in the functions, but it will be complicated.
A bad example:
var someClass = function() {};
function f() {
var some = new someClass();
return function(code) {
console.log(eval(code));
};
}
window.f_ = f();
window.f_('some');
In example above the GC has no way of knowing if the variable is used or not (code tested and works in Chrome30, FF25, Opera 12 and IE10).
The memory is released if the reference to the object is broken by assigning another value to window.f_.
In my opinion this isn't a bug.
I tested this in IE9+ and Firefox.
function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; } //removing this fixes a massive memory leak
return function() {}; //or removing this
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
Live site here.
I hoped to wind up with an array of 500 function() {}'s, using minimal memory.
Unfortunately, that was not the case. Each empty function holds on to an (forever unreachable, but not GC'ed) array of a million numbers.
Chrome eventually halts and dies, Firefox finishes the whole thing after using nearly 4GB of RAM, and IE grows asymptotically slower until it shows "Out of memory".
Removing either one of the commented lines fixes everything.
It seems that all three of these browsers (Chrome, Firefox, and IE) keep an environment record per context, not per closure. Boris hypothesizes the reason behind this decision is performance, and that seems likely, though I'm not sure how performant it can be called in light of the above experiment.
If a need a closure referencing some (granted I didn't use it here, but imagine I did), if instead of
function g() { some; }
I use
var g = (function(some) { return function() { some; }; )(some);
it will fix the memory problems by moving the closure to a different context than my other function.
This will make my life much more tedious.
P.S. Out of curiousity, I tried this in Java (using its ability to define classes inside of functions). GC works as I had originally hoped for Javascript.
Heuristics vary, but a common way to implement this sort of thing is to create an environment record for each call to f() in your case, and only store the locals of f that are actually closed over (by some closure) in that environment record. Then any closure created in the call to f keeps alive the environment record. I believe this is how Firefox implements closures, at least.
This has the benefits of fast access to closed-over variables and simplicity of implementation. It has the drawback of the observed effect, where a short-lived closure closing over some variable causes it to be kept alive by long-lived closures.
One could try creating multiple environment records for different closures, depending on what they actually close over, but that can get very complicated very quickly and can cause performance and memory problems of its own...
Maintain State between function calls
Let’s say you have function add() and you would like it to add all the values passed to it in several calls and return the sum.
like
add(5); // returns 5
add(20); // returns 25 (5+20)
add(3); // returns 28 (25 + 3)
two way you can do this first is normal define a global variable
Of course, you can use a global variable in order to hold the total. But keep in mind that this dude will eat you alive if you (ab)use globals.
now latest way using closure with out define global variable
(function(){
var addFn = function addFn(){
var total = 0;
return function(val){
total += val;
return total;
}
};
var add = addFn();
console.log(add(5));
console.log(add(20));
console.log(add(3));
}());
function Country(){
console.log("makesure country call");
return function State(){
var totalstate = 0;
if(totalstate==0){
console.log("makesure statecall");
return function(val){
totalstate += val;
console.log("hello:"+totalstate);
return totalstate;
}
}else{
console.log("hey:"+totalstate);
}
};
};
var CA=Country();
var ST=CA();
ST(5); //we have add 5 state
ST(6); //after few year we requare have add new 6 state so total now 11
ST(4); // 15
var CB=Country();
var STB=CB();
STB(5); //5
STB(8); //13
STB(3); //16
var CX=Country;
var d=Country();
console.log(CX); //store as copy of country in CA
console.log(d); //store as return in country function in d
(function(){
function addFn(){
var total = 0;
if(total==0){
return function(val){
total += val;
console.log("hello:"+total);
return total+9;
}
}else{
console.log("hey:"+total);
}
};
var add = addFn();
console.log(add);
var r= add(5); //5
console.log("r:"+r); //14
var r= add(20); //25
console.log("r:"+r); //34
var r= add(10); //35
console.log("r:"+r); //44
var addB = addFn();
var r= addB(6); //6
var r= addB(4); //10
var r= addB(19); //29
}());

const keyword scope in Javascript

1. >>> const a = 2
2. >>> var a = 3
3. >>> a = 4
4. >>> a // print 2
Why the operation line 3 is allowed? const seems more "global" than without any keyword...
const scope is defined as 'block scoped' (the scope of which, is restricted to the block in which it is declared).
MDN documentation:
Constants are block-scoped, much like variables defined using the let
statement. The value of a constant cannot change through
re-assignment, and it can't be redeclared.
Regarding your specific issue:
First as comments said const is relevant in ES6. I don't know about you but i get (typing your line 2: var a = 3;): SyntaxError: Identifier 'a' has already been declared
so your example is not quite possible.
This is is just how const works (or doesn't work):
Creates a constant1 that can be global or local to the function in which it is declared. Constants follow the same scope rules as variables [.. and cannot share a name] with a function or a variable in the same scope.
Firefox [..] throws a TypeError if you redeclare2 [which is different than re-assigning] a constant. None of the major browsers produce any notices or errors2,3 if you assign another value to a constant [..] but the reassignment is unsuccessful (only) in Firefox and Chrome (at least since version 20).
Note that const is not part of the ECMAScript 5 specification and the JavaScript 1.5 semantics will be re-defined in ECMAScript 6.
Behavior will vary across browser implementations with respect to support and re-declaration/re-assignments semantics.
1 In IE 9, using const a = 2 results in
"Syntax error"
2 In FF 14, const a = 2; var a = 3; a = 4; a, when evaluated as a single program, results in
TypeError: redeclaration of const a
which is different than executing each line one-at-a-time in the REPL. I suspect this is because var is hoisted above the const and because a const "cannot share a name with a function or variable in the same scope".
3 In Chrome 21, const a = 2; var a = 3; a = 4; a evaluates to 2 with no warning or message.

Javascript function works with FF and Chrome, but not uncle Bill's browser

I have the following function, which I use (admitedly, as a hack, since I still havent understood javascript's bizzare variable scoping rules), to fetch all global variables with a known prefix, from within a script.
The function works well with FF and Google Chrome (presumbaly it would work with all Moz derivative browsers). However, I just tested it in IE8 (aka Uncle Bill [as in Bill Gates] browser), and (perhaps unsuprisingly), the function did not work. I debugged the function and it appears that global variables are stored in another object (I could be wrong, I've only been reading on JS for a couple of days now). In any case, here is the function, which works correctly in 'Moz bazed browsers:
function getGlobalProperties(prefix) {
var keyValues = [], global = window; // window for browser environments
for (var prop in global) {
if (prop.indexOf(prefix) == 0) // check the prefix
keyValues.push(prop + "=" + global[prop]);
}
return keyValues.join('&'); // build the string
}
Do I need a conditional branch (and a test to see if running under IE)?
It's known issue, IE does not expose global variables for for-in loop over window object (inspite that fact that accessing global variables directly like window.globalVar works).
The possible workaround is to declare global variables explictly as members of window object, like:
window.globalVar = 1;
...
alert(globalVar); // = 1
globalVar = 'xxx';
alert(window.globalVar); // = xxx
By declaring variables in such way in the beginning of script you'll make them accessible for for-in loop.
The better solution of course is to avoid global vars :-)
Or at least keeping all them in separate variable which you can later traverse without hacks.
it looks like it should work, is global a keyword? try using something else or just the windows variable directly.
In IE be sure to also store your properties where you want them to be upon read.
global is actually not a JavaScript reserved word.
Try this instead of what you have (commented for potential problems your code is likely running into on IE):
function getGlobalProperties(prefix) {
var keyValues = [], global = window; // window for browser environments
for (var prop in global) {
if (prop.indexOf(prefix) == 0) { // check the prefix
try {
// this implicitly converts a window property to a string, which might fail
keyValues.push(prop + "=" + global[prop]);
} catch (e) {
// in case string conversion blows up
// do something about it here
}
}
}
return keyValues.join('&'); // build the string
}

Categories

Resources