How to check that ES6 "variable" is constant? - javascript

Does anyone know some tricks how to do it? I tried to use try-catch:
"use strict";
const a = 20;
var isConst = false;
try {
var temp = a; a = a+1; a = temp;
} catch (e) {
isConst = true;
}
But unfortunately it works only in "strict" mode. Without "use strict" it perform all statements silently, without modification of a. Also I cannot wrap this code into some handy function (isConstant(someConst) for example) as any argument I'll pass to that function will be a new variable. So anyone know how to create isConstant() function?

I don't think there is, but I also don't think this is a big issue. I think it might be useful to have the ability to know if a variable is const, and this exists in some other languages, but in reality since you (or someone on a team) will be defining these variables, you'd know the scope and the type of the variables. In other words, no you can't, but it's also not an issue.
The only case where it might be useful is if you could change the mutable property during runtime, and if changing this property had actual performance benefits; let, const, and var are treated roughly equally to the compiler, the only difference is that the compiler keeps track of const and will check assignments before it even compiles.
Another thing to note is that just like let, const is scoped to the current scope, so if you have something like this:
'use strict';
const a = 12;
// another scope
{
const a = 13;
}
it's valid. Just be careful that it will look up in higher scopes if you don't explicitly state const a = 13 in that new scope, and it will give a Read Only or Assignment error:
'use strict';
const a = 12;
{
a = 13; // will result in error
}

Based on some of the answers here I wrote this code snippet (for client side JS) that will tell you how a "variable" was last declared--I hope it's useful.
Use the following to find out what x was last declared as (uncomment the declarations of x to test it):
// x = 0
// var x = 0
// let x = 0
// const x = 0
const varName = "x"
console.log(`Declaration of ${varName} was...`)
try {
eval(`${varName}`)
try {
eval(`var ${varName}`);
console.log("... last made with var")
} catch (error) {
try {
eval(`${varName} = ${varName}`)
console.log("... last made with let")
} catch (error) {
console.log("... last made with const")
}
}
} catch (error) {
console.log("... not found. Undeclared.")
}
Interestingly, declaring without var, let or const, i.e x = 0, results in var getting used by default. Also, function arguments are re-declared in the function scope using var.

Just check if your reassignment actually did something:
var isConst = function(name, context) {
// does this thing even exist in context?
context = context || this;
if(typeof context[name] === "undefined") return false;
// if it does exist, a reassignment should fail, either
// because of a throw, or because reassignment does nothing.
try {
var _a = context[name];
context[name] = !context[name];
if (context[name] === _a) return true;
// make sure to restore after testing!
context[name] = _a;
} catch(e) { return true; }
return false;
}.bind(this);
You need the try/catch because reassign Could throw an exception (like in Firefox), but when it doesn't (like in Chrome), you just check whether your "this always changes the value" reassignment actually did anything.
A simple test:
const a = 4;
var b = "lol";
isConst('a'); // -> true
isConst('b'); // -> false
And if you declare the consts in different context, pass that context in to force resolution on the correct object.
downside: this won't work on vars declared outside of object scopes. upside: it makes absolutely no sense to declare them anywhere else. For instance, declaring them in function scope makes the const keyword mostly useless:
function add(a) {
return ++a;
}
function test() {
const a = 4;
console.log(add(a));
}
test(); // -> 5
Even though a is constant inside test(), if you pass it to anything else, it's passed as a regular mutable value because it's now just "a thing" in the arguments list.
In addition, the only reason to have a const is because it does not change. As such, constantly recreating it because you're calling a function that makes use of it more than once, means your const should live outside the function instead, so again, we're forced to put the variable in an object scope.

The question refers to incompliant behaviour in earlier ES6 implementations, notably V8 (Node.js 4 and legacy Chrome versions). The problem doesn't exist in modern ES6 implementations, both in strict and sloppy modes. const reassignment should always result in TypeError, it can be caught with try..catch.
There can't be isConstant function because const variable cannot be identified as such by its value.
It's preferable to run a script in strict mode and thus avoid problems that are specific to sloppy mode.
Even if a variable was defined in sloppy mode, it's possible to enable strict mode in nested function scope:
const foo = 1;
// ...
let isConst = false;
(() => {
'use strict';
try {
const oldValue = foo;
foo = 'new value';
foo = oldValue;
} catch (err) {
isConst = true;
}
})();
It's beneficial to use UPPERCASE_CONSTANT convention which is used in JavaScript and other languages. It allows to unambiguously identify a variable as a constant without aid from IDE and avoid most problems with accidental reassignments.

Related

javascript, let function if not defined

Every time a modal is opened I load via ajax some html + js, like this:
var loadModalJs = function() {
...
}
$(function() { loadModalJs(); });
It works with var, but if I replace var with let, when the modal is closed and then opened again I get Identifier 'loadTabJs' has already been declared
I tried something like this, without success:
if(typeof loadModalJs === 'undefined') {
let loadModalJs = function() {
...
};
}
loadModalJs(); // ReferenceError: loadModalJs is not defined
I don't like using var because my IDE complains about "'var' used instead of 'let' or 'const'"... But none of them seems to work... How could I do?
The error you placed into the comment is correct: at no point do you define the variable in scope -- you define it within the the if clause, and it is not available outside of that clause, your reference to it in the if statement is to an undefined global variable.
Also, if your variable is only ever going to be undefined or a function, then no need to test its type, as undefined evaluates to false.
let loadModalJs;
// ...your other code here...
// This bit within the function/scope run multiple times:
if (!loadModalJs) {
loadModalJs = function() {
...
};
}
if (loadModalJs) {
loadModalJs();
}
In addition to the current answer:
Your second snippet could work if you use var instead of let.
As if creates its own block {}:
let is limited to the immediate block {}.
var is limited to the immediate function block {} (or global if there are no functions).
E.g.:
if (true) {
var x = "It'll work outside the if.";
}
console.log(x);
Edit:
As an additional tip, you might be careful with the way you 'create' functions in javascript:
Functions defined, e.g. let myFunction = function() { }, are under top-to-bottom flow of control (i.e. first define, then use).
Functions declared, e.g. function myFunction() { }, are hoisted (i.e. declare and use where you want, in the same scope).
In addition to Lee Goddard's answer. You may consider learning about the difference between let and var. The most key difference is:
let allows you to declare variables that are limited to the scope of a block statement or expression on which it is used, unlike the var keyword, which declares a variable globally, or locally to an entire function regardless of block scope. - MDN Docs
This looks like a good opportunity to use guard/default clauses:
let f = function(){
console.log('f is defined');
}
let g;
g = f || function() {
console.log('f was NOT defined');
}
g();
f = null;
g = f || function() {
console.log('f was NOT defined');
}
g();
with a twist:
let f, g;
for (let i=0; i<8; i++){
g = f || function() {
console.log('f was NOT defined');
}
g();
f = (f)?undefined:function(){console.log('f is defined');};
}
So in your case this would be:
let guardedModal = loadModalJs || function() {
// your code here
}
guardedModal();

How can I find out if an ES6 value is var / const / let after it is declared? [duplicate]

This question already has answers here:
How to check that ES6 "variable" is constant?
(4 answers)
Closed 3 years ago.
I know I can find out if a value is var const or let by looking at where it is declared. However I am wondering - mainly for debugging, developing JS compilers, and academic interest - if it is possible to find out the immutability / scope of a variable (var / const / let-ness) after it has been created.
ie
doThing(something)
Would return
let
Or equivalent. Like we can determine types with typeof or something.constructor.name for constructors.
You can distinguish let and const from var with direct eval in the same function as the variable:
let a;
try {
eval('var a');
// it was undeclared or declared using var
} catch (error) {
// it was let/const
}
As #JonasWilms had written earlier, you can distinguish let from const by attempting assignment:
{
let x = 5;
const original = x;
let isConst = false;
try {
x = 'anything';
x = original;
} catch (err) {
isConst = true;
}
console.log(isConst ? 'const x' : 'let x');
}
{
const x = 5;
const original = x;
let isConst = false;
try {
x = 'anything';
x = original;
} catch (err) {
isConst = true;
}
console.log(isConst ? 'const x' : 'let x');
}
let and var are only about the scope, so it's not possible. Because JavaScript does not pass by reference, if you were to pass a variable to a function you would lose the " constness" and also the scope - on that function the variable would just be a locally scoped one. So my thoughts are: it's not possible.
You can't directly access the environment record chain in any way, and therefore you can't tell were a variable was declared. The with statement might enable you to narrow down the scope were a variable was declared as it allows you to add a Proxy as an environment record, but that only gives some insight and it is generally a very dangerous thing to do.
I used to do this but its a little hacky...
I create a variable and try to change it and if it throws an error then it's a const variable,.
I create a variable and try to change it on the {} braces, if the value is different then it was initially created then it's a var, if not then it's a let variable.

javascript: scoping with let vs none

I have a forEach loop and a for loop nested inside of it. Why is that outside of the for loop , but still inside the forEach loop, I have word = foo. Then that value of word can be logged outside of the entire forEach loop however, when I make it let word = "foo" , the console log statement fails and says word is not defined?
function mainFunction() {
array.forEach(function (element, index) {
for (var key in list) {
//do something
}
word = "foo"
}
console.log(word)
}
If you don't use let, var or const to define your variable, then JavaScript implicitly adds your word variable to the global object.
Basically, this:
word = "foo"
Is the same as this:
window.word = "foo"
This is widely considered to be bad practice and is generally indicative of a mistake. This is why most linters will mark this as an error.
Let satement
The let statement declares a block scope local variable, optionally initializing it to a value.
function test(){
let x = 1;
if(x === 1) {
let x = 2;
console.log(x);// output: 2
}
console.log(x); //output: 1
}
test();
console.log(x); // Uncaught ReferenceError
No statement
Variables should be declared with let, const, or var.
Omitting them is widely considered a mistake, because the variable ends in the global scope, generating global scope pollution, and difficulty to trace or debug.
It may also incur in variables overriding ( bugs, unexpected behavior...)
If your code runs in "strict mode" (or inside a module), an error will be triggered.
function test(){
x = 1; // becomes window.x
if(x === 1) {
let x = 2;
console.log(x);// output: 2
}
console.log(x); //output: 1
}
test();
console.log(x); // output: 1
console.log(window.x); // output: 1
Solution to your problem
You should declare the variable at the top of your function.
function mainFunction(array) {
let word; // declare 'word' here
array.forEach(function (element, index) {
word = "foo";
})
console.log(word)
}
mainFunction([1,2,3,4])
There are only two scopes in javascript: Global and local. The only thing that can create a scope is the function keyword.
Variables are fetched by first looking into the local scope and if not found are then searched in the parent scope above the chain until found. If not found and use strict mode is not set, they are auto created for you in the global scope.
With that said you can see that what would happen is that the variable word would simply not be found in the scope of forEach. In this case JS does what many people do not want it to do and the reason why many people use use strict mode ... it would add it for you in the global scope since it was not able to located in anywhere in the scope chain.
This causes many issues overall and it is not the behavior many people want. To stop that you can add use strict for tell JS to be in strict mode
'use strict';
var v = "Hi! I'm a strict mode script!"; // this is ok
b = 1 // ReferenceError: b is not defined"
Here is the same without use strict
var v = "Hi! I'm a NOT strict mode script!"; // this is ok
b = 1
console.log(b) // returns 1
function mainFunction() {
array.forEach(function (element, index) {
for (var key in list) {
//do something
}
let word = "foo"
//word is defined here.
//word can call in {array.forEarch(...)}
}
//word is undefined here.
console.log(word)
}
Always let called in {...}.
For example: { let a; ... {let b;} {let c;} }

Setting this to a var causes possible strict violation: [duplicate]

I think this may be a duplicate of Strict Violation using this keyword and revealing module pattern
I have this code:
function gotoPage(s){
if(s<=this.d&&s>0){this.g=s; this.page((s-1)*this.p.size);}
}
function pageChange(event, sorter) {
var dd = event.currentTarget;
gotoPage.call(sorter, dd[dd.selectedIndex].value);
}
And JSHINT (JSLINT) is complaining. It says "Strict violation." for the highlighted line:
Is my use of Function.call() and then referencing the instance, somehow inappropriate?
Is this considered to be bad style?
JSHint says "Possible strict violation" because you are using this inside something that, as far as it can tell, is not a method.
In non-strict mode, calling gotoPage(5) would bind this to the global object (window in the browser). In strict mode, this would be undefined, and you would get in trouble.
Presumably, you mean to call this function with a bound this context, e.g. gotoPage.bind(myObj)(5) or gotoPage.call(myObj, 5). If so, you can ignore JSHint, as you will not generate any errors. But, it is telling you that your code is unclear to anyone reading it, because using this inside of something that is not obviously a method is quite confusing. It would be better to simply pass the object as a parameter:
function gotoPage(sorter, s) {
if (s <= sorter.d && s > 0) {
sorter.g = s;
sorter.page((s - 1) * sorter.p.size);
}
}
function pageChange(event, sorter) {
var dd = event.currentTarget;
gotoPage(sorter, dd[dd.selectedIndex].value);
}
I've had this message for a function that did not start with a capital letter.
"use strict";
// ---> strict violation
function something() {
this.test = "";
}
// ---> just fine (note the capital S in Something)
function Something() {
this.test = "";
}
If you declare the function as a variable instead of using the standard function declaration, jshint will not flag this as a strict violation. So you may do the following -
var gotoPage = function (s){
if(s<=this.d&&s>0){this.g=s; this.page((s-1)*this.p.size);}
};
var pageChange = function (event, sorter) {
var dd = event.currentTarget;
gotoPage.call(sorter, dd[dd.selectedIndex].value);
};
If you're trying to implement a method, you might want to assign to the prototype instead:
ExampleClassName.protytpe.gotoPage = function gotoPage(s){
// code using this
};
JSHint won't warn when the function is being assigned.

Getting rid of eval

I have a name of a method as a string in javascript variable and I would like to get a result of its call to variable:
var myMethod = "methodToBeCalled";
var result;
eval("result = "+myMethod+"();")
This works and there are no problems. But this code is inacceptable for Google Closure Compiler. How can I modify it to work with it? Thanks!
EDIT:
It seems the proposed solutions does not work when the name of the method is inside of some object, for instance:
var myFunction = function () { return "foo!" }
var myObject = {
itsMethod: function() { return "foo!" }
};
...
var fstMethodToCall = "myFunction"
var sndMethodToCall = "myObject.itsMethod";
...
window[fstMethodToCall](); // foo!
window[sndMethodToCall](); // undefined
Assuming you are not in a nested scope of some kind, try:
var result = window['methodToBeCalled']();
or
var myMethod = 'methodToBeCalled';
var result = window[myMethod]();
To execute an arbitrary function of arbitrary depth based on a string specification, while not executing eval:
var SomeObject = {
level1: {
level2: {
someFunc: function () {
console.log('hello');
}
}
}
};
var target = 'SomeObject.level1.level2.someFunc';
var obj;
var split = target.split('.');
for (var i = 0; i < split.length; i++) {
obj = (obj || window)[split[i]];
}
obj();
You can use indexer notation:
result = window[myMethod]();
The Closure Compiler doesn't prohibit 'eval', you can continue to use it if you find it convenient but you have to understand that the compiler doesn't try to understand what is going on in your eval statement and assumes your eval is "safe":
function f(x, y) {
alert(eval("y")); // fails: hidden reference to "y"
alert(eval('"'+x+'"')); // might be valid
}
f('me', 'you');
When the compiler optimizes this function it tries to remove "y" and renamed the remain parameter. This will the first eval to fail as "y" no longer exists. The second eval would correct display the alert "me".
So with SIMPLE optimizations, you can use eval to reference global variables and object properties as these are not renamed or removed (but not local ones).
With ADVANCED optimizations, it is a little trickier, as the compiler tries to remove and rename global as well as local variables. So you need to export the values you need to have preserved. This is also true if you use a string to try to reference a name by other means:
var methodName = "myMethod";
(window[methodName])()
or
var methodName = "myMethod";
eval(methodName+"()")
the compiler simply doesn't try to determine if "methodName" is a reference to a function. Here is a simply example of an ADVANCED mode export:
window['myMethod'] = myMethod;
The assignment does two things: it preserves the myMethod function if it would otherwise be removed and it gives it a fixed name by assigning it to a property using a string. If you do need to reference local values, you need to be a little trickier and use a Function constructor. A definition of "f" from my first example, that can eval locals:
var f = new Function("x", "y", "alert(eval('y')); alert(eval('\"' + x + '\"'));");
You may find this page useful:
https://developers.google.com/closure/compiler/docs/limitations

Categories

Resources