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
}
Related
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();
From the MDN description of Function:
Note: Functions created with the Function constructor do not create
closures to their creation contexts; they always are created in the
global scope. When running them, they will only be able to access
their own local variables and global ones, not the ones from the scope
in which the Function constructor was called. This is different from
using eval with code for a function expression.
I understand,
var y = 10;
var tester;
function test(){
var x = 5;
tester = new Function("a", "b", "alert(y);");
tester(5, 10);
}
test(); // alerts 10
Replacing the tester = new Function("a", "b", "alert(y);"); with tester = new Function("a", "b", "alert(x);");, I will get
// ReferenceError: x is not defined
But couldn't understand the author's line-
...they always are created in the global scope.
I mean how is the new Function("a", "b", "alert(y);"); nested within the test fn is in global scope?
In fact, accessing it from outside the test fn will simply result in
Uncought TypeError:tester is not a function
Please elucidate.
In your example, "created in the global scope" means that tester will not have closure over x from test:
function test(){
var x = 5;
tester = new Function("a", "b", "alert(x);"); // this will not show x
tester(5, 10);
}
When you new up a Function, it does not automatically capture the current scope like declaring one would. If you were to simply declare and return a function, it will have closure:
function test(){
var x = 5;
tester = function (a, b) {
alert(x); // this will show x
};
tester(5, 10);
}
This is the trade-off you make for having dynamically compiled functions. You can have closure if you write the function in ahead of time or you can have a dynamic body but lose closure over the surrounding scope(s).
This caveat doesn't usually matter, but consider the (slightly contrived) case where you build a function body as a string, then pass it to a function constructor to actually be evaluated:
function addOne(x) {
return compile("return " + x + " + 1");
}
function addTwo(x) {
return compile("return " + x + " + 2");
}
function compile(str) {
return new Function(str);
}
Because the function is instantiated by compile, any closure would grab str rather than x. Since compile does not close over any other function, things get a bit weird and the function returned by compile will always hold a closure-reference to str (which could be awful for garbage collection).
Instead, to simplify all of this, the spec just makes a blanket rule that new Function does not have any closure.
You have to create an object to expose via return inside the test() function for it to be global. In other words, add var pub = {} and name your internal functions as properties and/or methods of pub (for example pub.tester = new func) then just before closing test() say return pub. So, that way it will be publically available (as test.tester). It's Called the Revealing Module Pattern.
What it means is that inside the function you can only refer to global variables, as you've found. However, the reference to the function itself is still in the local scope where it was created.
I'm confused as to where the confusion is.
It says that the function will be in global scope...and therefore will only have access to its own scope and the global scope, not variables local to the scope in which it was created.
You tested it and it has access to its own scope and the global scope, not variables local to the scope in which it was created.
So where's the confusion?
Is it in your assigning of the function to the variable testing? testing is just a local variable with a reference to the function...that has nothing to do with the scope of the creation of the function.
Scope is lexical, and has to do with where the function is created, not what random variables a function reference happens to be assigned to at runtime. And the documentation is telling you that when you make a function this way it acts as if it was created in the global scope...so it's acting completely as expected.
Here's an illustration:
This:
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and errors as not defined
tester = new Function("console.log(y); console.log(x);");
}
Is similar to this:
var y = 10;
var tester;
function test()
{
var x = 5;
// also 10 and errors as not defined
tester = something;
}
function something()
{
console.log(y);
console.log(x);
}
NOT
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and 5...since x is local to the function creation
tester = function()
{
console.log(y);
console.log(x);
}
}
This is a simple script that'll log the content of text from a Photoshop file.
var numOfLayers = app.activeDocument.layers.length;
var resultStr = "";
doAllLayers();
alert(resultStr);
function addToString(alayer)
{
if (alayer.kind == "LayerKind.TEXT")
{
var c = alayer.textItem.contents;
resultStr += c;
}
}
// main loop
function doAllLayers()
{
for (var i = numOfLayers -1; i >= 0; i--)
{
var thisLayer = app.activeDocument.layers[i];
addToString(thisLayer);
}
}
It works fine but I really should be passing a string into the function to add to itself, however it works.
How does the scope of JavaScript allow this to happen? Are declared local variables still accessed by functions or is it another trick I'm missing?
Here are some basic rules to variable scoping in JavaScript:
If defined with the var keyword, the variable is function-scoped. That is, the variable will be scoped to either the closest containing function, or to the global context if there is no containing function.
Example:
// globally-scoped variable (no containing function)
var foo = 'bar';
function test() {
// function-scoped variable
var foo2 = 'bar2';
if (true) {
// still function-scoped variable, regardless of the if-block
var foo3 = 'bar3';
}
// see?
console.log(foo3); // --> 'bar3'
}
If defined with the let keyword (ES6+), then the variable is block-scoped (this behavior more closely resembles most other C-family syntax languages). Example:
// error: top level "let" declarations aren't allowed
let foo = 'bar';
function test() {
// block-scoped variable (function are blocks, too)
let foo2 = 'bar2';
if (true) {
// block-scoped variable (notice the difference
// from the previous example?)
let foo3 = 'bar3';
}
// uh oh?
console.log(foo3); // --> ReferenceError: foo3 is not defined
}
If defined with neither the var or let keywords (e.g., foo = bar), then the variable is scoped to the global context. Example:
// globally-scoped variable
foo = 'bar';
function test() {
// also globally-scoped variable (no var or let declaration)
foo2 = 'bar2';
if (true) {
// still a globally-scoped variable
foo3 = 'bar3';
}
}
test();
console.log(foo, foo2, foo3); // 'bar', 'bar2', 'bar3'
In all of these cases, functions defined within scope of a variable still have access to the variable itself (technically you're creating a closure, as the numOfLayers and resultStr variables are lexically in scope of your addToString and doAllLayers functions).
Please note that the scoping rules are technically a little more nuanced than this, but you're better off reading a more in-depth article at that point.
You have defined var resultStr = ""; outside function which is a global variable. you can access this global variable inside addToString() and start concatenating to it. Note that inside the method you have not declared var resultStr = ""; else it would have been a different variable local to that method.
From what i know, a function say A defined within another function say B has access to local variables of B as well.
function B() {
var x = 10;
function A() {
console.log(x); //will result in 10
var y = 5;
}
console.log(y); //ReferenceError: y is not defined
}
However in the below example y gets printed. I know there is no such thing as block scope in javascript for "if block" but shouldn a declaration atleast be invisible outside of "if" i mean shouldnt var y be limited to if block?
function B() {
var x = 10;
if(1) {
console.log(x); //will result in 10
var y = 5;
}
console.log(y); will result in 5
}
JavaScript isn't truly block scoped. An if or for loop does not have its own scope. All variable declarations in any given scope (that is: global scope, or a function) are hoisted, and this visible anywhere inside that scope.
ECMAScript6 (Harmony) will, in all likelihood, introduce block-scope to JS, through the use of the new let keyword see the wiki:
for (let i=0;i<12;++i)
{
console.log(i);//logs i
}
console.log(i);//reference error
There also seems to be some confusion as far as terminology is concerned here: a closure is not the same as a scope.
The snippet above is an example of block-scoped code. The variable i is created at the start of the loop, and simply ceases to exist once the loop finishes. It is GC'ed (Garbage Collected). i is no more, bereft of life it rests in peace. Closures are something different entirely: they rely on variables that are not GC'ed, but can't be accessed from outside code. They can only be accessed through the return value of the closure:
var someClosure = (function()
{//this function creates a scope
var x = 123;
return {
getX: function()
{
return x;
},
setX: function(val)
{
return x = val;
}
};
}());
console.log(x);//reference error
console.log(someClosure.getX());//returns 123, so "x" still exists
That being said:
You can mimic a block-scoped loop through the use of a closure, though. Functions are scoped, and a closure is a function or object that is returned by a function. That means that the return value of a function has access to the entire function's scope. See this answer for details.
Apply what is explained there, and work out why this loop is "pseudo-scoped":
var foo = [1,2,3,4,5,6],
largerThan3 = (function(arr)
{
var resultArr = [];
for (var i=0;i<arr.length;++i)
{
if (arr[i] > 3)
resultArr.push(arr[i]);
}
return resultArr;
}(foo));
console.log(i);//reference error...
nope.
as you said - if blocks does not have their own closures in JS - which means everything is part of the outer closure (in this case - y is a local variable in B's closure). so it will be completely visible in B's body
Let's say I start up a var for an Object in my code
var base = {x:Infinity, y:Infinity};
and later in the same scope I do
var base = {x:0, y:0}
I get that I can re-use the existing variable, but I'm trying to iterate on a point.
Does this cause any problems for the current scope as far as memory is concerned?
Would there be any problems if these are in different scopes?
Is the general rule to never use var on an already-existing variable?
If I did this several (thousands) of times, would I run into any "funky" issues?
It releases its hold on the old value, so it can be potentially garbage collected. I say potentially, because objects can be referenced by different variables.
If they're in different scopes, the inner base will shadow the outer one (assuming you're talking about nested scopes), so the old value will not be overwritten. Shadowing is usually best avoided.
Generally, within the same scope, yes but that's more of a coding style aspect. It won't make an effective difference in the execution since there's only one declaration no matter how many times you declare the same var in the same scope.
Depends. Are you talking about overwriting in a loop? If you no longer need the current value, it shouldn't be an issue.
To be clear, when you do this:
var base = {x:Infinity, y:Infinity};
var base = {x:0, y:0}
What's really happening is this:
var base; // declaration is hoisted
base = {x:Infinity, y:Infinity}; // new object is referenced by base
base = {x:0, y:0} // previous object is released, and new object is referenced
Note that when I talk about scope and var, I'm talking specifically about function scope. In standard JavaScript/ECMAScript implementations, there is no block scope, so there's no special meaning when using var in a for statement for example.
As mentioned by #Charles Bailey, Mozilla has let, which does allow scoping in blocks, but it would require that specific keyword. The var statement doesn't recognize the block with respect to variable scope.
There is absolutely no problem in doing that. Afterall, variables declared within a function (-context) are just part of the such called Activation Object (ES edition 3). It is a little bit different in ES5, but for this part it is basically the same.
You are just overwritting a property, in an Object (not a Javascript object, but from the underlaying js engine).
So again, that is not a problem at all (even if its unusual und looks confusing).
If you declare a variable in another "scope" (which basically means, another function here), it is also no problem. Each (execution) context is completely on its own and cannot interfer with another context, unless we are talking about closures and stuff. But even then, if you have several functions and parent context which all have the same named variable, the lookup process (name resolution) always begins in your local scope (the Activation object from the current context) and after that, it goes into the "scope chain".
I guess that should answer 3.) and 4.) also. You "can" re-var a variable, but it makes no sense. So I'd just recommend to don't do it anymore.
The var keyword provides scope to a variable. The only scope available to JavaScript at this time is function scope. If the variable is already scoped to this function then setting with the var keyword again does nothing.
JavaScript is lambda language, which means variables declared in a higher scope are available to functions inside that higher scope. Providing a variable from a higher scope with the var keyword in a lower function scope could be harmful, because you have now created a local variable that may be intended as closure. Consider the following two examples:
var a = function () {
var b = 3,
c = function () {
var d = 5;
b = 5;
return d + b; // this is 10
};
return c() + b; // this is 10 + 5, or 15
};
var a = function () {
var b = 3,
c = function () {
var b = 5,
d = 5;
return d + b; // this is 10
};
return c() + b; // this is 10 + 3, or 13
};
I said unintentionally altering variable scope is potentially harmful, because it will confuse how your code works from how you think it should work.
The general rule is one var declaration per function, and that var declaration belongs at the top of the function:
function foo() {
var bar;
}
The reason to do it is to avoid confusion that could be caused by variable hoisting.
An example of where this matters is with closure context for callbacks:
//this looks like it will count up, but it wont
function foo() {
var i;
for (i = 0; i < 10; i++) {
var bar = i;
setTimeout( function () {
console.log(bar);
}, 1000 * i);
}
}
//this is actually what's happening behind the scenes
//hopefully you can see why it wont work
function foo() {
var i, bar;
for (i = 0; i < 10; i++) {
bar = i; //bar is in the same scope as i
setTimeout( function () {
//when this is called `i` and `bar` will both have a value of 9
console.log(bar);
}, 1000 * i);
}
}
If you've got a sub-function, you can declare a new variable of the same name, which will remain for the scope of that sub-function:
function foo() {
var bar;
function baz() {
bar = 2;
}
bar = 1;
baz();
console.log(bar); //output will be 2 because baz operated in the outer scope
}
function foo() {
var bar;
function baz() {
var bar;
bar = 2;
}
bar = 1;
baz();
console.log(bar); //output will stay 1 because baz operated in its own scope
}
If your example code is along the lines of:
function foo() {
var base = {x:Infinity, y:Infinity};
...some code...
var base = {x:0, y:0};
}
It will actually execute as:
function foo() {
var base;
base = {x:Infinity, y:Infinity};
...some code...
base = {x:0, y:0};
}