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();
Related
here i am taking a input from a drop down form on a html page. And i am putting it in to a var called AM.
var e = document.getElementById("UserTimeAM");
function onChangeAM() {
var AM = e.value;
var text = e.options[e.selectedIndex].text;
console.log(AM, text);
}
e.onchangeAM = onChangeAM;
onChangeAM();
From there a fetch is called to an api and i am using the AM variable in it. But the issue is that when the site is being served it comes back with an error saying AM is not defined.
lowHigh.forEach(d =>{
let wDate = new Date(d.time).getUTCDate();
let AM_Hour = AM;
let PM_Hour = PM;
AM_Hour = ("0" + AM_Hour);
if(wDate == i+1)
{
if(tidedata1.innerHTML == "")
{
tidedata1.innerHTML = `${AM_Hour}: - ${d.value}m`
tidedata1Full.innerHTML = `${AM_Hour}:am - ${d.value}m`
}
else
{
tidedata2.innerHTML = `${PM_Hour}: - ${d.value}m`
tidedata2Full.innerHTML = `${PM_Hour}:pm - ${d.value}m`
}
}
})
I thought using var would mean it was a global so the variable could be passed in to different functions.
You just need to initialize the AM variable in the scope outside of your two functions to make it possible to use it in both
var AM;
var e = document.getElementById("UserTimeAM");
function onChangeAM() {
AM = e.value;
var text = e.options[e.selectedIndex].text;
console.log(AM, text);
}
e.onchangeAM = onChangeAM;
onChangeAM();
lowHigh.forEach(d =>{
let wDate = new Date(d.time).getUTCDate();
let AM_Hour = AM;
let PM_Hour = PM;
AM_Hour = ("0" + AM_Hour);
if(wDate == i+1)
{
if(tidedata1.innerHTML == "")
{
tidedata1.innerHTML = `${AM_Hour}: - ${d.value}m`
tidedata1Full.innerHTML = `${AM_Hour}:am - ${d.value}m`
}
else
{
tidedata2.innerHTML = `${PM_Hour}: - ${d.value}m`
tidedata2Full.innerHTML = `${PM_Hour}:pm - ${d.value}m`
}
}
})
The problem
In order to fully understand the differences between each of the identifiers (let, var, const) we first need to understand the concept of scope.
What is Scope?
Scope determines the accessibility or visibility of variables to JavaScript. There are three types of scope in JavaScript:
Global scope
Function (local) scope
Block scope (new with ES6)
- Global Scope
Variables declared outside a function are in the global scope.
Global variables can be accessed and changed in any other scope.
- Local Scope
Variables defined within a function are in local scope and are not accessible in other functions.
Each function, when invoked, creates a new scope, therefore variables with the same name can be used in different functions.
Reference: JavaScript: Var, Let, or Const? Which One Should you Use?
Example
var first = 123;
if(true) {
var second = 456;
}
function abc(){
var third = 789;
}
abc();
Note that first and second are inside global scope, and third is in function (local) scope, because its declared inside a function
Important note
Variables declared using the var keyword are either globally or functionally scoped, they do not support block-level scope. This means that if a variable is defined in a loop or in an if statement it can be accessed outside the block and accidentally redefined leading to a buggy program. As a general rule, you should avoid using the var keyword.
Example:
function DoSomething(){
if(true){ //Some condition [only example]
var example = 123;
}
console.log({example})
}
DoSomething()
If it had been declared as a let, it would not be visible, and would belong to the ({}) block of the if, example:
function DoSomething(){
if(true){ //Some condition [only example]
let example = 123;
}
console.log({example})
}
DoSomething()
In this example above, i'm using let, with will generate an error, that is a good thing to alert the programmer.
How to solve
Just as #HaimAbeles said, you just need to initialize the AM variable in the scope outside of your two functions to make it possible to use it in both
Example:
var AM;
var e = document.getElementById("UserTimeAM");
function onChangeAM() {
AM = e.value;
var text = e.options[e.selectedIndex].text;
console.log(AM, text);
}
e.onchangeAM = onChangeAM;
onChangeAM();
...
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;} }
Hey so I have a function that takes a string from an input box and splits it up to numbers and letters, seen here:
function sepNsLs() {
"use strict";
var letterArray = [];
var numberArray = [];
separatorSpacerator();
var L = 0;
var listResult = document.getElementById("listInput").value;
var splitResult = listResult.split(separator.sep);
for (; L < splitResult.length; L++) {
if (isNaN(splitResult[L])) {
letterArray.push(splitResult[L]);
} else if (Number(splitResult[L])) {
numberArray.push(splitResult[L]);
}
}
}
My program has to pass through JSLint perfectly, meaning I need to use my functions in strict mode. I've only put them in strict mode now, meaning that my later functions that try to call the letterArray and numberArray that I declared and filled in the SepNsLs function no longer call those arrays and the arrays come up undeclared. Here's the code for one of them:
function addNumbers() {
"use strict";
var sum = 0;
var i = 0;
sepNsLs();
while (i < numberArray.length) {
sum = sum + Number(numberArray[i]);
i++;
}
As you can see, I call the sepNsLs function in the addNumbers function, but because of strict mode, I can't use the arrays sepNsLs creates. How do I fix this? Also, is there a website like the javascript beautifier that will fix my current code to fit strict mode conventions?
EDIT: Separator is declared a global variable here:
var separator = {
sep: 0
};
separatorSpacerator makes it so that if I choose to split my input strings at a space, the input box to tell my program to split at the spaces declares the word "Space" so I can see it is a space I'm splitting my string at.
function separatorSpacerator() {
"use strict";
var list = document.getElementById("listInput").value;
if (document.getElementById("separatorInput").value === "space") {
separator.sep = " ";
} else if (document.getElementById("separatorInput").value === " ") {
separator.sep = " ";
document.getElementById("separatorInput").value = "space";
} else {
separator.sep = document.getElementById("separatorInput").value;
}
if (list.indexOf(separator.sep) === -1) {
alert("Separator not found in list!");
clearResults();
}
}
I can't use the arrays sepNsLs creates. How do I fix this?
One way of fixing this would be to return arrays sepNsLs creates with e.g. a tuple - return [numberArray, letterArray]; , and then use it like:
a) es6 syntax:
var [numberArray, letterArray] = sepNsLs();
b) pre-es6 syntax:
var split = sepNsLs(),
numberArray = split[0],
letterArray = split[1];
Your addNumbers function should also probably return sum - otherwise, it doesn't produce any meaningful results as it stands.
Although not relevant to the question and is more of a matter of naming convention preference - you might want to explore on Hungarian notation and its' drawbacks.
Your problem is one of scope. When you try to access numberArray inside of addNumbers it doesn't exist.
You have a couple of options:
Make all the variables that need to be accessed in each function global.
Wrap all of your functions in an outer function and place the 'global' variables into that outer scope.
The better option is #2, because you won't actually be polluting the global scope with variables. And you can declare "use strict" at the top of the outer function and it will force everything in it into strict mode.
Something like this:
(function() {
"use strict";
// These are now in-scope for all the inner functions, unless redclared
var letterArray = [], numberArray = [], separator = {sep: 0};
function sepNsLs() {
// code goes here
}
function addNumbers(){
// code goes here
}
function separatorSpacerator(){
//code goes here
}
// ...more functions and stuff
// and then call...
theFunctionThatKicksOffTheWholeProgram();
}());
The variables letterArray and numberArray are declared local to the function sepNsLs, they are only accessed in that scope (strict mode or not). Here is an example:
function foo() {
var fooVar = 5;
console.log(fooVar);
}// fooVar get destroyed here
function bar() {
console.log(fooVar); // undefined because fooVar is not defined
}
foo();
bar();
A scope usually is from an open brace { to it's matching close brace }. Any thing declared inside a scope is only used withing that scope. Example 2:
var globalVar = 5; // belongs to the global scope
function foo() {
var fooVar = 6; // belongs to the foo scope
function bar1() {
console.log(globalVar); // will look if there is a globalVar inside the scope of bar1, if not it will look if there is globalVar in the upper scope (foo's scope), if not it will in the global scope.
console.log(fooVar); // same here
var bar1Var = 7; // belongs to bar1 scope
}
function bar2() {
var globalVar = 9; // belongs to the bar2 scope (shadows the global globalVar but not overriding it)
console.log(globalVar); // will look if there is a globalVar in this scope, if not it will look in one-up scope (foo's scope) .... untill the global scope
console.log(bar1Var); // undefined because bar1Var doesn't belong to this scope, neither to foo's scope nor to the global scope
}
bar1();
bar2();
console.log(globalVar); // prints 5 not 9 because the serach will be in foo's and the global scope in that order
}
foo();
What you need to do is to decalre the variables letterArray and numberArray where they can be access by both sepNsLs and addNumbers (one scope above both of them). Or return the value from sepNsLs and store it in a variable inside addNumbers. Like this:
function sepNsLs() {
// logic here
return numberArray; // return the array to be used inside addNumbers
}
function addNumbers() {
var arr = sepNsLs(); // store the returned value inside arr
for(var i = 0; i < arr.length; ... // work with arr
}
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.
I know that you can write following
var obj = {
test: 'something'
}
But in this code, the inner function does not refer to a variable, but to a function.
Is there any other way to write / call the inner function?
function outer(){
var a = "Outerfunction";
console.log(a)
innerFct: function InnerFct() {
var c = "Inner";
console.log(c)
} innerFct();
}
window.outer();
There are a couple of different things going on here.
In this code:
var obj = {
test: 'something'
}
you are using "literal object notation" to create -- well, an object with one property test and that property has a value of something
In your second case, you are creating a code block (yes, it is fun that both objects and code blocks use the same syntax {...} to define them.
Inside of a code block, the innerFct: becomes a label. Labels are used with some control flow statements to jump around. Forget about them, you really are better off not using them.
function outer(){
var a = "Outerfunction";
console.log(a)
function innerFct() {
var c = "Inner";
console.log(c)
}
innerFct();
}
outer();
or even
function outer(){
var a = "Outerfunction";
console.log(a)
var innerFct = function () {
var c = "Inner";
console.log(c)
}
innerFct();
}
outer();
You are confusing functions with objects.
When using an object, the colon is used to show key-value pairs.
var object = {
innerFct: function(){
console.log('rawr');
},
someVariable: 7
}
object.innerFct(); //logs rawr
object.someVariable = 4; //just changed from 7 to 4
Using a colon how you have it in your example is incorrect syntax. Also when you are creating a function within an object like that, you don't name the function again because you are already assigning it to a name on the object.
Then you can edit the function anytime by doing something like this:
object.innerFct = function(){
//new code
}
Doing object.innerFct() will call the function.
Other answers have sufficiently covered the object syntax and calling the function in scope. As I mentioned in the comment, you can just do this:
function outer(){
(function () {
var c = "inner";
console.log(c)
})();
}
window.outer();
And it logs inner just fine.
Edit: Private/hidden variables like innerFct in the original code sample can be captured in closures as well.
outer = function() {
var innerFct = function () { console.log("inner"); }
// innerFct is captured in the closure of the following functions
// so it is defined within the scope of those functions, when they are called
// even though it isn't defined before or after they complete
window.wrapper = function() { innerFct(); }
return function() { innerFct(); }
}
outer();
// each of these next three lines logs "inner"
window.wrapper(); // assigned to global variable
outer()(); // calling the returned function
var innerFctBackFromTheDead = outer(); // saving the returned function
innerFctBackFromTheDead();
There is also the object constructor/prototype syntax.
function Outer() {
this.inner = function() {
this.c = "inner";
console.log(this.c);
}
}
var out = new Outer();
out.c; // undefined
out.inner(); // logs "inner"
out.c; // "inner"
More information on the new keyword and prototypes: http://pivotallabs.com/javascript-constructors-prototypes-and-the-new-keyword/