Javascript commenting practice - javascript

Recently, using comments in javascript I've run into a few questions about the commenting system. I wanted to comment on the name of a variable, so I put it right after declaring the name, on the same line, like this:
var wk /* (website key) */ = 1;
Now I think this is perfectly valid and works fine, right?
So a little while later, I wanted to comment out the whole block of code that line was on, like this:
/*
~ more code ~
var wk /* (website key) */ = 1;
~ more code ~
*/
But this doesn't work, because when the interior comment closes, it closes the whole comment. That seems kind of dumb to me. Is there any way to do nested comments in javascript?

You can't nest block comments, but you can do this:
var wk = 1; // website key
Or
// website key
var wk = 1;
It looks less awkward, and block comments are only really supposed to be used for... well, blocks. It's just better coding style in general.
Or, even better, make your code self-documenting and eliminate the need for a comment at all:
var websiteKey = 1;

I ran into a problem recently where I had written some JavaScript for a friend on their wordpress blog. Anyway long story short they used the WYSIWYG editor, witch reformatted the page source.
so
<script type="test/javascript">
$(function() {
// this is a button click handler
$('#button').click(function () {
// did some stuff
});
})();
</script>
turned into
<script type="test/javascript">
$(function() {// this is a button click handler $('#button').click(function() {
// did some stuff }); })(); </script>
The point of the story is always be aware of your environment and the needs of your users, even when commenting

Do it like this:
var wk = 1; //Website Key - Additional Info Here
then if you need to comment the block, you can do
/*
Comments and stuff go here...
var wk = 1; //Website Key - Additional Info Here
*/

Avoid nested block comments. They are forbidden in the ECMA Script specification. If any interpreters allow this now, they may not in the future.
From Section 7.4,
Comments can be either single or multi-line. Multi-line comments cannot nest.

You never put single-line comment between the code. Reasons are that it is not readable enough and not being followed in the community as a best practice.
Putting away code via commenting it out is easily done with multi-line comment tags /* ... */ or single line ones //.
PS: For commenting I suggest to avoid doing magic stuff. I would not encourage nested commented at all! For having a consistent commenting style in Javascript try follow a pattern like JSDoc. In JSdoc, while commenting a block of code, you can do something like this:
/***
*
* #param param1
*/
var aFunction = function(param1) {
};
Readable, clean and no magic tric. It is great since it also embeds some sort of static type checking (I know JS is dynamically typed). Also check out this video from one of the guys behind integrating JSDoc into Intellij & Webstorm: Dmitry Jemerov: Static types in JavaScript: what, how and why

Related

How to cut off function from global scope

I have an idea for a game where people can type in some simple instructions for their character like player.goLeft() or player.attackInFront() and for that I have people type their code into a text box and then I parse it into eval(). This works well but it also allows people to change their own character object by typing things like player.health = Infinity; or something similar. I have a list of functions I want to allow people to use, but I am unsure how to restrict it to only use them.
I understand that the whole point of not letting people use eval is to avoid accidental cross-site scripting but I am unsure on how else to do this. If you have a suggestion please leave a comment about that.
I asked some people around on what to do and most suggested somehow changing scope(which is something I was not able to figure out) or to add some odd parameter to each function in my code that would be required to be a specific string to execute any function, but that seems hacky and since I am making the game in browser with p5js it would be easy to just inspect element and see what the password is.
basically every character has variable called "instruction" which is just a string of javascript. Then every frame of the game I execute it by doing eval(playerList[i].instruction);
tl;dr, how can I only allow specific function to be executed and not allow any others?
EDIT: I forgot to mention that I also am planning to provide player with information so that people can made code that would adapt to the situation. For example there will be parameter called vision that has vision.front and vision.left etc. These variables would just say if there is an enemy, wall, flower, etc around them in a grid. Some people suggested that I just replace some functions with key words but then it compromises the idea of using if statements and making it act differently.
EDIT 2: Sorry for lack of code in this post, but because of the way I am making it, half of the logic is written on server side and half of it works on client side. It will be a little large and to be completely honest I am not sure how readable my code is, still so far I am getting great help and I am very thankful for it. Thank you to everybody who is answering
Do NOT use eval() to execute arbitrary user input as code! There's no way to allow your code to run a function but prevent eval() from doing the same.
Instead, what you should do is make a map of commands the player can use, mapping them to functions. That way, you run the function based on the map lookup, but if it's not in the map, it can't be run. You can even allow arguments by splitting the string at spaces and spreading the array over the function parameters. Something like this:
const instructions = {
goLeft: player.goLeft.bind(player),
goRight: player.goRight.bind(player),
attackInFront: player.attackInFront.bind(player)
};
function processInstruction(instruction_string) {
const pieces = instruction_string.split(' ');
const command = pieces[0];
const args = pieces.slice(1);
if (instructions[command]) {
instructions[command](...args);
} else {
// Notify the user their command is not recognized.
}
};
With that, the player can enter things like goLeft 5 6 and it will call player.goLeft(5,6), but if they try to enter otherFunction 20 40 it will just say it's unrecognized, since otherFunction isn't in the map.
This issue sounds similar to the SQL Injection problem. I suggest you use a similar solution. Create an abstraction layer between the users input and your execution, similar to using parameters with stored procedures.
Let the users type keywords such as 'ATTACK FRONT', then pass that input to a function which parses the string, looks for keywords, then passes back 'player.attackInFront()' to be evaluated.
With this approach you simplify the syntax for the users, and limit the possible actions to those you allow.
I hope this isn't too vague. Good luck!
From your edit, it sounds like you're looking for an object-oriented approach to players. I'm not sure of your existing implementation needs, but it would look like this.
function Player() {
this.vision = {
left: '',
// and so on
}
}
Player.prototype.updateVisibilities = function() {
// to modify the values of this.visibility for each player
}
Player.prototype.moveLeft = function() {
}
Don't give the user an arbitrary interface (such as an input textfield that uses eval) to modify their attributes. Make a UI layer to control this logic. Things like buttons, inputs which explicitly run functions/methods that operate on the player. It shouldn't be up to the player as to what attributes they should have.

Javascript Find User

I have the below javascript to get the UserID from a online form. This script will go through IE DOM Explorer to find the valued. But when I run the script, it is totally ignoring my "If" statement. It is just providing a value for "NewAuthUserID", without considering the "if".
(function () {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"') {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
Firstly, I'd suggest to check out how the if statement works: https://www.w3schools.com/js/js_if_else.asp
You need the if statement conditional to return true or false. Right now you're TRYING to use jquery to select things but even that has a syntax issues. Not only that but once the syntax is fixed it STILL won't do what you're attempting to do because you're putting something that will always evaluate to true as the conditional. That jquery selector just returns a function, not a boolean like it looks like you're intending to do. Try this:
(function(){
var NewAuthUserID = "";
var UserId=$('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if($('tr.background-highlight').text() == "NEW AUTHORIZED INDIVIDUAL PROFILE:")){
var NewAuthUserID=$('td:contains("User ID:")+td:eq(2)').text();
}
alert(UserId);
alert(NewAuthUserID)
})();
Notice how I'm snagging the text that you're trying to test against with jquery and expressing it with a conditional instead? In this manner, it will return the boolean: true/false which is what you need to get the if statement to trigger.
Also if you check your syntax, you were missing the $() wrapper around your if statement, but you have a string that looked like it was trying to snag text via jquery.
I suggest formatting your code a bit, this always helps to debug.
The problem is you are trying to use a jQuery selector in your if statement, but you didn't include the $ to evaluate jQuery. It's just evaluating a string, wich results in TRUE (basically doing this: if(true)), so the code block is executed.
Try this instead:
javascript: (function() {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ($('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"').length > 0) {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
EDIT: I added the length > 0 check on the returned object. It's possible to accomplish this with OP's code, he was just missing those two pieces. :contains is not the same as .text() ==.
Off topic response:
The way you manage/select your nodes may require a lot of maintanance in the future and is prone to errors.
For example: tr.background-highlight:contains("REQUESTER PROFILE") + tr
In words: Get me the table-row after a table-row with hilighted background, that contains "REQUESTER PROFILE".
What if you'll have to add a row in between them? what if you'll need to select the row, wether it is hilighted or not? what if further rows will be hilighted in the future, so that this selector ain't uniqu anymore? what if the label changes? maybe even the language? ...
In each of these cases you'll have to revisit (potentially all) your jquery selectors, just because some minor layout changed.
That's not very reliable.
Will you remember that when you'll get asked to do these changes? Maybe someone else will have to do these changes, will he/she know what to look for?
Tell me, do you remember the details/implications/quirks of the work you've done a week ago? not to speak about your work from a few months ago.
Better:
Use "unique" identifier to, well, identify your nodes by their role; and I'm not talking about IDs. Unique within their specific context.
The easiest way would be to use css-classes. Annotating the rows/cells so you can select the very same field as $('.ref-requester-provile .ref-user-id')
This is way more reliable and future-proof than your bulky $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td') where your JS needs to know every little detail of your template/markup, and needs to be adapted with every little change.
Why did I prepend these classes with ref-? to distinct them from classes that are meant for styling
If you don't need to style these nodes and need these identifyer solely to reference them in your JS, I'd rather use a data-attribute. Why? Let's sum it up with:
performance: when you need to add/remove these marker; avoid unnecessary render-cycles
A cleaner seperation between style and code: classes are primarily for styling, but we don't style here.

Using MVC resource in Jquery - function stops working

I have a Jquery function in MVC View that check if at least one checkbox is clicked. Function is working properly if I use hardcoded string. But when I add
#Resources.myString into, it stops working, I can't figure out why
$('.form-horizontal').on('submit', function (e) {
if ($("input[type=checkbox]:checked").length === 0) {
e.preventDefault();
alert("This is working");
alert(#Resources.myString); //with this the function is not working anymore
return false;
}
});
I need to add the the string for multilingual purpose.
I tried diferent aproches
alert(#Resources.myString);
alert(#Html.Raw(Resources.myString))
var aaa = { #Html.Raw(Resources.myString)} //and calling the aaa
I think I am missing some basic knowlage of how this should work together
During page rendering, #Resources.myString will be injected as is in the code. For instance, if myString == "abc";, you'll end up with alert(abc); which is not what you want.
Just try to enclose your string in quotes:
alert("#Resources.myString");
As an aside, putting Razor code in Javascript logic is usually considered bad practice, as it prevents you from putting Javascript code in separate files (and therefore caching), and makes the code less readable.
Take a look as this question and the provided answer which gives a simple way to deal with that.
As ASP.NET dynamically generates HTML, CSS, JS code, the best way to find the error is to read the generated sources (Ctrl + U in most modern browsers).
You will see that your code
alert(#Resources.myString);
produces
alert(yourStringContent);
and should result in a console error yourStringContent is not defined.
You need to use quotes as you are working with a JavaScript string:
alert('#Resources.myString');
It will produce a correct JavaScript code like:
alert('yourStringContent');

Javascript else on a new line

When creating an if block, I was wondering if there was any reason beyond personal preference to use the standard bracket formatting vs the second one I listed.
I've run code in the second format without any obvious issues (no ASI or unexpected errors), just looking for some clarification or insight on if there could be any possible issues in the future if I permanently switch to this style.
// Standard formatting
if (true) {
} else {
}
// Other formatting
if (true) {
}
else {
}
Spaces and tabs are not considered to be significant in Javascript in most cases. (I believe all, but I can't find a source for that)
You can technically put all of your code on one line (as most minification algorithms do), but that won't be very readable. In your own code, it comes down to solely personal preference, it will not cause any errors or cause the code to run slower if there are spaces (though more spaces will take longer to load if the JS is not minified).
Best practice is to keep your code style consistent throughout your projects.
None, they are equivalent if you wanted you could put the code in one line as well and it would work. Usually people have personal preferences, as well as some companies require you to write the code in a specific way in order to standardize it and make it so anyone taking over your project knows what to expect.
You don't even need the brackets, it wil just work:
http://jsfiddle.net/4ywahnof/1/
(function () {
var t = 1;
if (t == 1) alert("hi");
else alert("no hi");
})();

My Javascript works on IE FF & Safari, but not Chrome

I have a simple rotating banner javascript on the site I'm developing. It works perfectly locally, on IE, Firefox and Safari but not on Chrome. Is there something wrong with my code? I'm new to Javascript so this problem is really baffling me. Here's the relevant code:
<script language="Javascript" type="text/javascript">
adImages = new Array
("images/AMSite18.png","images/AMSite19.png","images/AMSite09b.png")
thisAd = 0
imgCt = adImages.length
function rotate() {
if (document.images) {
thisAd++
if (thisAd == imgCt) {
thisAd = 0
}
document.adBanner.src=adImages[thisAd]
setTimeout("rotate()", 3 * 1000)
}
}
</script>
</head>
<body onload="rotate()">
<div id="banner">
<img src="images/banner1.gif" name="adBanner" alt="Ad Banner" />
</div><!-- end banner -->
</body>
It looks like the reason it isn't working in Chrome is this line:
document.adBanner.src=adImages[thisAd]
You're referring to the element with the name "adBanner" via document.adBanner, but Chrome doesn't support that. You'll have to use something like this:
document.getElementsByName('adBanner')[0].src = adImages[thisAd];
Some others things that could improve code quality:
Don't use the language attribute. Not necessary.
Use the format var x = [...]; to create a new array. There's no reason to use the constructor format. None at all. Zippo. No one could possibly comment on this answer with a reason you'd use new Array(...) instead. No one.
Use the var keyword to create variables, and the semi-colon to end your statements. Although it isn't hurting anything here, if you don't use var, then JavaScript assumes you're creating/changing a global variable, when that may not necessarily be the case. (Also, the semi-colon rules may be a little convoluted, but it really helps with readability.)
Why are you checking for document.images? It's unnecessary. You don't refer to it anywhere else.
Crockford suggests using x += 1 instead of x++. Not a big deal, and a lot of people disagree, just something I noticed.
Always use strict equality (===). The kind you're using (==) doesn't take into account types; 2 == "2" will return true, but 2 === "2" will not. Again, not a big deal, and some people don't really care, but it could bite you later on, in a different project.
Never pass strings to setTimeout. The browser just evals the string, and nobody hangs out with people who eval stuff. You don't even need to pass a string, because you're using a function that doesn't need any arguments! Just use this:
setTimeout(rotate, 3 * 1000);
Try to put script tags at the bottom of the body. There are two reasons for this. First, performance. When the browser gets to your script, it stops everything to parse and execute the code. If you put it at the bottom of the body instead of the head, the page will at least appear to load faster. The second point is addressed next:
Try to avoid using onload. It's just gauche. The reason you need to is because your script is in the head, and has no access to the DOM just yet. But if that script was moved to the bottom of the body (which, for some reason, you might not be able to; no big deal), you wouldn't have to mess with onload at all:
<body>
<div id="banner">
<img ... />
</div>
<script>
// Copy all of your code exactly the same,
// and then:
rotate();
</script>
</body>
For the love of god, don't use the name attribute. For forms, who cares? But when you're manipulating elements with JavaScript, use the id. It's immediately obvious what you're doing, and document.getElementById('adBanner') is way faster than document.getElementsByName('adBanner')[0].
You should start by fixing the syntax problems.
Lines should end with a semi-colon ;, variable should be declared with var and you should use [] rather than new array, pass a function to setTimeout rather than a string
var adImages = ['images/AMSite18.png','images/AMSite19.png','images/AMSite09b.png'];
var thisAd = 0;
var imgCt = adImages.length;
function rotate() {
if (document.images) {
thisAd++;
if (thisAd == imgCt) {
thisAd = 0;
}
document.adBanner.src=adImages[thisAd];
setTimeout(function(){
rotate();
}, 3 * 1000);
}
}
This may not fix it, but you you should do it anyway :)
I just ran you code on Chrome 11 on a Mac and it worked. Even with the syntax errors. But Paul is right you should always write valid JavaScript.
Also this is a better way of passing functions
setTimeout(rotate, 3 * 1000);

Categories