I have a JS function that is called on load that spilts some variables, this all works well, but when I call the function from another function, I get this error Cannot call method 'split' of undefined:
function loadInAttachmentsIntoSquads(){
// eg: 5000,5000,5000,5000 > [5000][5000][5000]
myAttachmentArray = currentAttachments.split(',');
//eg: [5000][5000][5000] > [5][0][0][0]
//myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
setupWeaponAttachments();
}
function setupWeaponAttachments(){
myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
//if(mySquadsIndex == 0){
if(myAttachmentForWeapon[1] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.silencer = true;
}
else{
weaponAttachments.silencer = false;
}
if(myAttachmentForWeapon[2] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.grip = true;
}
else{
weaponAttachments.grip = false;
}
if(myAttachmentForWeapon[3] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.redDot = true;
}
else{
weaponAttachments.redDot = false;
}
// -- applies visuals -- \\
applyWeaponAttachments();
}
If I call setupWeaponAttachments() from another function, I get that error ... why?
In the following:
> function loadInAttachmentsIntoSquads(){
>
> myAttachmentArray = currentAttachments.split(',');
>
> setupWeaponAttachments();
> }
The identifier currentAttachments is used as if it's a global variable. If it hasn't been assigned value, or its value isn't a string, at the time that the function is called, then an error will result.
So the fix is to make sure it has a string value:
function loadInAttachmentsIntoSquads(){
if (typeof currentAttachments != 'string') return;
...
}
or deal with the error some other way.
Also, where you are doing all those if..else blocks, consider:
weaponAttachments.silencer = myAttachmentForWeapon[1] == 1;
weaponAttachments.grip = myAttachmentForWeapon[2] == 1;
weaponAttachments.redDot = myAttachmentForWeapon[3] == 1;
It won't be any faster, but it is a lot less code to write and read.
You are misunderstanding/misusing the scope rules of JavaScript.
Try passing the array you're splitting explicitly and consistently, and it should solve your problem, as well as keeping the global namespace less cluttered:
First, pass the attachments in your first function explicitly:
function loadInAttachmentsIntoSquads(currentAttachments) {
var myAttachmentArray = currentAttachments.split(',');
setupWeaponAttachments(myAttachmentArray);
}
Note several things I'm doing above. First, I'm adding a currentAttachments argument to the function rather than just relying on a previously-declared global variable. Second, I'm declaring myAttachmentArray as a local variable by using the var keyword. Declaring variables with var declares them in local scope; failing to do so declares them in global scope. Third, I'm manually passing the array to the setupWeaponAttachments function, in which I will also receive the argument:
function setupWeaponAttachments(myAttachmentArray) {
var myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
// [...]
}
Notice that I again properly declare the myAttachmentForWeapon variable in local scope.
If you are more careful with managing scope and properly define functions to receive the arguments they need and operate on them, you'll save yourself lots of headache in the future, and you'll get drastically fewer problems like these.
Related
I'm trying to avoid the use of global variables in my code, so I'm trying to use a work around by declaring them inside of $(document).ready and passing them as parameters to functions outside of $(document).ready, updating them, and then returning the updated value from those functions to manipulate the variables inside of $(document).ready.
Another way around this is to use hidden input fields to store variables but I also heard that was bad practice.
I'm wondering if I should just use global variables, do it the way I'm currently doing it, or use hidden input fields?
Below is a brief example of what I'm trying to accomplish. The variable validations is the variable I want to be able to use and update.
$(document).ready(function(){
var validations = [];
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
scanValidation(validations, function(valid){
validations = valid;
});
}
});
}):
function scanValidation(valid, cb){
var scanval = $('#inp').val();
if(valid.includes(scanval)){
//display error
}
else{
var validarr = valid.slice();
validarr.push(scanval);
var myData=JSON.stringify({ "name":"user1", "validations":validarr});
//Makes an ajax call to see if the sent array validarr is a valid request
apiCall(myData,'scanValidation',function(decoded) {
if (decoded.Status!="ERROR") {
valid = validarr;
}
else {
//display error
}
return(cb(valid));
});
}
}
Any variables declared within the immediately executed function below will NOT be in the global scope.
(function () {
var someVar = 'someValue';
$(document).ready(function() {
});
})();
Is it bad practice to instantiate variables inside of $(document).ready as opposed to globally declaring them?
No, not at all! Variables should always be declared in the scope they're needed in, nowhere else.
Another way around this is to use hidden input fields to store variables but I also heard that was bad practice.
I've never heard of that, but yes it definitely sounds like a bad practise. That's just the same as a global variable, but a global variable stored in the DOM for some odd reason.
I'm trying to avoid the use of global variables in my code, so I'm trying to use a work around by declaring them inside of $(document).ready and passing them as parameters to functions outside of $(document).ready, updating them, and then returning the updated value from those functions to manipulate the variables inside of $(document).ready.
That, admittedly, is a bit weird.
The easiest way to improve this is to move the function declaration inside the ready handler as well, and just access the variable there directly - with the additional bonus of not having a scanValidation global variable:
$(document).ready(function() {
var validations = [];
function scanValidation() {
var scanval = $('#inp').val();
if (validations.includes(scanval)) {
//display error
} else {
var validarr = validations.slice();
validarr.push(scanval);
var myData = JSON.stringify({"name": "user1", "validations": validarr});
// Makes an ajax call to see if the sent array validarr is a valid request
apiCall(myData, 'scanValidation', function(decoded) {
if (decoded.Status!="ERROR") {
validations = validarr;
} else {
//display error
}
});
}
}
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
scanValidation();
}
});
});
If you want to make scanValidation reusable, so that it could be called from other places as well with its own array, I would suggest to create a factory function that creates validators, each of which is a closure over its own array. That way, the array is declared where it belongs, so that the user of the function does not have to store the state for them:
function makeScanValidator(display) { // any other configuration
var validations = [];
// returns closure
return function scanValidation(scanval) { // take it as an argument
if (validations.includes(scanval)) {
display(/* error */);
} else {
var validarr = validations.concat([scanval]);
var myData = JSON.stringify({"name": "user1", "validations": validarr});
apiCall(myData, 'scanValidation', function(decoded) {
if (decoded.Status!="ERROR") {
validations = validarr;
} else {
display(/* error */);
}
});
}
}
$(document).ready(function() {
var validate = makeScanValidator(function display() { … });
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
validate(this.value);
}
});
});
I'm not sure how to make this function into a self-invoking function. My code is looping through an array of zip codes from a JS file and sorting it from smallest to biggest and outputting it. I've found online that adding "())" at the end of the newZipCodeArray function, is supposed to self-invoke the function. However, it's not working. What am I doing wrong?
[enter image description here][1]
// Global variable
var zipCodeArray = [];
(function newZipCodeArray(currentZipCode) {
var valueFound = false;
for (zipCodeIndex = 0; zipCodeIndex <= zipCodeArray.length; zipCodeIndex++) {
if (currentZipCode === zipCodeArray[zipCodeIndex]) {
valueFound = true;
}
}
if (valueFound === false) {
zipCodeArray.push(currentZipCode);
}
}());
function newZipCodeArrayssignment12_3() {
// Instantiate variables.
var output;
var zipCodeRecords;
// Get the element.
output = document.getElementById('outputDiv');
zipCodeRecords = openZipCodeStudyRecordSet();
// Call the function to read the next record.
while (zipCodeRecords.readNextRecord()) {
currentZipCode = zipCodeRecords.getSampleZipCode();
newZipCodeArray(currentZipCode);
}
// Sort the zip code array.
zipCodeArray.sort();
}
The syntax involved in immediately-invoked (or self-invoked) functions doesn't allow it to be invoked from elsewhere. The IIFE pattern is intended to be used when the function only needs to be invoked once.
Note:
The grouping parenthesis wrapping the function change it from a declaration to an expression. And, as an expression, its name won't be added to the surrounding scope for other code to reference.
To invoke newZipCodeArray once right away and allow for it again later, you'll want to remove the parenthesis from around it and call it by name in a separate statement:
newZipCodeArray(); // first call
function newZipCodeArray(currentZipCode) {
// ...
}
function newZipCodeArrayssignment12_3() {
// ...
while (zipCodeRecords.readNextRecord()) {
// ...
newZipCodeArray(currentZipCode); // additional calls
}
// ...
}
In Secrets of the JavaScript Ninja the author's propose the below scheme for memoizing function results without a closure. They do this by exploiting the fact that functions are objects and defining a property on the function that stores the results of past calls to the function.
function isPrime(value) {
if (!isPrime.answers) isPrime.answers = {};
if (isPrime.answers[value] != null) {
return isPrime.answers[value];
}
var prime = value != 1
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
return isPrime.answers[value] = prime;
}
I have two questions
Their logic makes sense to me, but when I run the code all that happens is the answers property is created but nothing gets added to it - why?
The line return isPrime.answers[value] = prime; is funny to me, and repl.it warns when I use it. Is assigning and returning all in the same line frowned upon?
1. Their logic makes sense to me, but when I run the code all that happens is the answers property is created but nothing gets added to it - why?
It seems to work just fine for me.
console.log(isPrime(42));
console.log(isPrime.answers)
shows that answers is a non-empty object:
function isPrime(value) {
if (!isPrime.answers) isPrime.answers = {};
if (isPrime.answers[value] != null) {
return isPrime.answers[value];
}
var prime = value != 1
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
return isPrime.answers[value] = prime;
}
console.log(isPrime(42));
console.log(isPrime.answers)
2. [...] Is assigning and returning all in the same line frowned upon?
In this situation, the assignment is basically a side effect, and side effects are frowned upon. That said, assignments being expressions and returning the assigned value is a feature of the language and as long as it's used responsibly, why not.
Their logic makes sense to me
It should not. The scheme still relies on closure, in particular over the isPrime variable in the scope that the function resides in. They could equally have used var isPrimeAnswers = {}; instead of isPrime.answers = {}; (both of which should be put outside of the function body).
Is assigning and returning all in the same line frowned upon?
Depends on whom you ask, but it's not unanimously condemned as a bad practise. You say it's funny, others find it confusing, I personally think it's crystal clear. If you're not into code golf and want to be on the safe side, it's better to simply split it in two statements:
isPrime.answers[value] = prime;
return prime;
I have a jQuery function which is called on several events (button click, change etc.)
This function is called in the documentReadyFunction and is feeded with start values..
everytime I call this function, parameters will be passed to the function.
My problem is: I don't want to create a new Object each time I call the function, because if I set a variable which decides if a part of the function is beeing executed or not, will be always overwritten..
What do I have to do, to access the first created instance instead of creating always a new one with every function call..
Down below is a simplyfied version of my function.. Maybe you understand my problem better then.
$.fn.doSomething = function(param1) {
var localParam = param1;
var amIcalledMoreThanOnce = parseInt(0, 10);
if (param1 == 1) {
amIcalledMoreThanOnce = amIcalledMoreThanOnce + 1;
if (amIcalledMoreThanOnce == 1) {
$('#run').val(amIcalledMoreThanOnce);
// fill form fields with URL parameters
// This shall be executed only once after getting the URL vals
} else {
// set the localParam to 0 to exit this loop and reach the outter else..
localParam = 0;
$.fn.doSomething(localParam);
}
} else {
$('#run').val(amIcalledMoreThanOnce);
// use the User Input Data not the URL Params
}
};
$.fn.doSomething(1);
$.fn.doSomething(1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<input type="number" id="run">
you can use this pattern:
var nameOfYourFunction = (function() {
var initializedOnlyOnce = {};
return function() {
//use initializedOnlyOnce here.
}
})();
here what you see is; you create and run a function immediately when the code is run. The outer function immediately returns the inner function and it's assigned to nameOfYourFunction. Then you can use the nameOfYourFunction(); just as any other function. However any varible declared in the outer function will be available to the nameOfYourFunction() and initializedOnlyOnce will never be initialized again.
just look at this question: What is the scope of variables in JavaScript?
in the answer which is accepted look at the point 3
according to him a will be 4 but now look at my function:
function JIO_compiler(bpo){ // bpo means that " behave property object " and from here i will now start saying behave property to behave object
var bobj = bpo, // bobj = behave object
bobj_keys = Object.keys(bobj), // bobj_keys = behave object keys. This willl return an array
Jcc_keys = Object.keys(JIO_compiler_components), // Jcc = JIO compiler components
function_code = ""; // get every thing written on every index
if (bobj.hasOwnProperty('target') === false) { // see if there is no target defined
console.log("No target is found"); // tell if there is no target property
throw("there is no target set on which JIO-ASC will act"); // throw an error
};
if (bobj.hasOwnProperty('target') === true) {
console.log("target has been set on "+ bobj['target']+" element"); //tell if the JIO-ASC has got target
function x(){
var target_ = document.getElementById(bobj['target']);
if (target_ === null) {throw('defined target should be ID of some element');};
function_code = "var target="+target_+";";
console.log("target has successfully been translated to javascript");//tell if done
};
};
for(var i = 0; i < bobj_keys.length; i++){
if(bobj_keys[i] === "$alert"){ // find if there is $alert on any index
var strToDisplay = bobj[bobj_keys[i]];
var _alert = JIO_compiler_components.$alert(strToDisplay);
function_code = function_code+ _alert;
};
};// end of main for loop
alert(function_code);
new Function(function_code)();
};
well it is big... but my problem is in the second if statement. now according to the accepted answer the value of function_code should change according to what is instructed. but when at last i alert the function code then it alert blank. i mean it should alert at least var target = something ; and the last console.log statement of this if statement is not showing text in the console.
so what is wrong in this ?
You're setting function_code inside the definition for function x(), but x() is never called. function_call won't change until you make a call to x.
thats because your variable is inside the function scope, you need to define your variable outside of it, as
function_code = "";
function JIO_compiler(bpo){
....
};// end of main for loop
//call the function
JIO_compiler(some_parameter);
//alert the variable
alert(function_code);
you need to call the function JIO_compiler() first so that the appropriate value is set to function_code variable from JIO_compiler() function, as