Javascript "with" operator removal - javascript

I recently ran into some issues with a plugin and outlined the issue in this post: With operator & dashes in object keys and wanted to know if the modifications I've made below cover the scenarios that the with scope blocks would have covered.
I've modified some code to remove the with operator and I'm wondering if I've replicated everything properly in doing so.
Here is the original code:
var test = new Function('$f','$c','with($f){with($c){return{'+ declarations +'}}}'));
Where $f and $c are passed objects (From what I could tell, $f shouldn't ever have a property of $c). The declarations variable is a string that has a colon in it (EX: "value:color") and available within the scope.
Here is my modified code:
var test = function($f, $c, declarations) {
var result = {};
var value = "";
var split = declarations.split(":");
if (split.length < 2) {
throw new Error("Declaration is in an invalid format");
}
if ($f[$c] !== undefined && $f[$c][split[1]]) {
value = $f[$c][split[1]];
}
else if ($c[split[1]]) {
value = $c[split[1]];
}
else if ($f[split[1]]) {
value = $f[split[1]];
}
else {
value = "" + split[1];
}
var key = split[0];
result[key] = value;
return result;
};
Everything appears to work as it did previously, but this modification now handles the use case where the declarations variable could have a dash in it (EX: "value:background-color"). Additionally the declarations variable is passed into the function, to ensure it's defined.

Related

call for loop through function with parameters

I have the below for loops, I have it manytime in my code, with different variables name, I would put it in a function with parameters and call it but it didn't work
for(let i = 0; i < inputs.length - 1; i++){
if(!nputs[i].value){
error += inputs[i].getAttribute('test') + " is blank";
isTrue = false;
}
}
Here what I did
let y = "";
let z = true;
function test(x,y,z){
for(let i = 0; i < x.length - 1; i++){
if(!x[i].value){
y += x[i].getAttribute('name') + " is blank !";
z = false;
}
}
}
let error = "";
let isTrue = true;
test(inputs,error,isTrue);
shall I do return in the function? if yes which return should I do?
Scope: when you define y and z outside the function (in the global scope presumably) they are different than the y and z defined in the parameter list of the function. The ones in the parameter list only exist within the body of the function. Changing the value of the parameter named y within the function does not change the value of the global variable y. So the simple answer to your question is, yes, you need to return something, since the value of the parameter y is lost when the function is done executing.
Give your variables descriptive names. Let the obfuscator do it's thing later.
function test(x,y,z) -> function valueTest(arr, err, success)
The boolean status and error string are redundant bits of information. If the error string is not empty, then the status is failure. So you don't need to return both a boolean and the string.
The status of the previous test is of no relevance to the next test. Therefore, z or success doesn't have to be passed in to the function each time, as it (or something like it) is really the desired output of the function, and each call of the function can be treated separately. If you want to combine the results from different tests then that should be the concern of whatever is calling this function - see separation of concerns and coupling
The only parameter the function actually needs is the value that is under test (the array).
When you write the function you define the return value, and thus you define how other code can decipher those results. The function itself doesn't have to do all the work of interpreting the results and building the error string. If your return value was just an array of name attribute values (of the elements of the test array that failed), the calling code could still process "success" or "failure". If the return value has one or more elements, or a length > 0 that would indicate failure.
Removing the redundant/unnecessary parameters and information, you'll have a function that looks something like this:
function valueTest(arr) {
let results = [];
for (let i = 0; i < arr.length - 1; i++){
if (!arr[i].value) {
results.push(arr[i].getAttribute('name'));
}
}
return results;
}
The caller can decipher and build an error message from that. It might make sense for the function to handle some of the additional work by returning <name> is blank! instead of just <name>, and then you just need to join the elements of the array.
...so within the function...
results.push(arr[i].getAttribute('name') + ' is blank!');
...and back in the global scope...
const error = valueTest(inputs).join(" ");
let success = error.length > 0;
5.If you want a running status indicator from different tests, evaluate an individual test's result, then logically AND that with the previous result.
const result1 = valueTest(inputs1).join(' ');
let success = results1.length > 0;
const result2 = valueTest(inputs2).join(' ');
success &= (results2.length > 0);
Seeing the issues with your code are handled in the comments, I present you a simpler method.
If you count the elements that have the attribute and are not empty and compare it to the length of all the inputs passed you will have a better test
const test = (inputs,attr) => [...inputs]
.filter(inp => inp.getAttribute(attr) && inp.value.trim() !== "").length === inputs.length;
const istrue = test(document.querySelectorAll("input"),"name")
isTrue will be true if all passed inputs has an attribute called name
You can also do
const elems = document.querySelectorAll("input[name]")
const isTrue = elems.filter(inp => inp.value.trim() !== "").length === elems.length

Javascript if statement in function overwriting global variable?

Am attempting to create a static navigation panel which becomes absolute at the bottom before the footer when reaching the end of the page content.
As I am developing for wordpress the page could be of varying height so I have attempted to trigger the absolute positioning when the nav panel “collides” with the footer.
So far I have used this code I found here
function collision($archive, $footer){
var archivexPos = $archive.offset().left;
var archiveyPos = $archive.offset().top;
var archiveHeight = $archive.outerHeight(true);
var archiveWidth = $archive.outerWidth(true);
var archiveb = archiveyPos + archiveHeight;
var archiver = archivexPos + archiveWidth;
var footerxPos = $footer.offset().left;
var footeryPos = $footer.offset().top;
var footerHeight = $footer.outerHeight(true);
var footerWidth = $footer.outerWidth(true);
var footerb = footeryPos + footerHeight;
var footerr = footerxPos + footerWidth;
if (archiveb < footeryPos || archiveyPos > footerb || archiver < footerxPos || archivexPos > footer) return Boolean = false;
return Boolean = true;
And used a global variable of Boolean to pass to this function
$(window).on('scroll', function() {
var scrollmath = Math.round($(window).scrollTop());
var archiveValue = scrollmath + 48;
var archiveBottom = archiveValue + 'px';
console.log('collision boolean', Boolean)
if (Boolean = false) {
$('#archive').css('position', 'fixed');
$('#archive').css('top', '48px');
} else {
$('#archive').css('position', 'absolute');
$('#archive').css('top', archiveBottom);
}
My problem is the if statement seems to be creating another Boolean variable? As when I comment it out I can see that the console reports the Boolean variable as expected. However when I leave it in and they collide this happens
Whats happened here?
The primary thing that's happening is that you're using = for comparison. JavaScript uses == (or ===), not =. = is always assignment.
But when testing the value of a boolean, you don't want == or != anyway, just use the boolean directly:
if (flag) {
// It was true
} else {
// It was false
}
Or if you're just testing for false:
if (!flag) {
// flag was false
}
(Note that because JavaScript does type coercion, that will also work with variables containing values other than booleans: Any truthy value coerces to true, any falsy value coerces to false. The falsy values are 0, "", NaN, null, undefined, and of course, false; all other values are truthy.)
Separately: Boolean is not a good choice for a variable name, as it's part of the JavaScript standard library (a function).
Also, your current collision function does two things:
It sets Boolean to true or false
It returns the value it set
In general, all other things being equal, it's best if a function doesn't have side-effects like that. If the caller wants to set Boolean to the return value of the function, he/she can, there's no need for the function to do it — it's already returning the value.
And finally: Global variables are, in general something to avoid. The global namespace on browsers is incredibly crowded and it's easy to get conflicts (for instance, a global called name may well not work as expected, because there's already a name global [it's the name of the window]).
no, your real Problem is, that you overwrite the constructor for the Boolean Type.
1st. stick to coding conventions: Only classes start with an uppercase-letter.
2nd. local vars have to be declared with the var-Keyword (or let for block-scoped vars, or const).
otherwise you reference a var from a surrounding scope; and in the end, the global scope.
3rd. the equal-sign:
=== means typesafe comprison
3 === 3 //=> true
3 === '3' //=>false
== means simple comparison
3 == '3' //=> also true now
= means assignment, not comparison
var foo = 3;
if it inside of some other code like
var bar = 42 + (foo = 3);
//it works basically like
var bar = 42 + (function(){
foo = 3;
return 3; //NOT FOO!!! not even foo after the assignment
})();
//the same way, that this:
var bar = 42 + foo++;
//works basically like this:
var bar = 42 + (function(){
var result = foo;
foo = foo+1;
return result;
})();

problems with list scope javascript

I trying to make a function that tries to access the first item in a list to make a konami code website. The list is defined and then the function is defined to edit the list. This is code
<html><body>
<script>
var keys = [38,38,40,40,37,39,37,39,66,65];
function keyhandler (e) {
e = e || event;
console.log(e.keyCode);
var key = keys[0];
if (key == e.keyCode) {
console.log("correct key");
if (keys.length == 1){
console.log("konami");
} else {
var keys = keys[1,keys.length];
}
}
else {
var keys = [38,38,40,40,37,39,37,39,66,65];
}
}
document.onkeydown = keyhandler;
</script>
</body></html>
when I trigger the function by a key-press I get this error :
Uncaught TypeError: Cannot read property '0' of undefined the error is caused by the var key = keys[0] line
You're initializing another variable named keys inside of your function (var keys = keys[1, keys.length]). This shadows the outer variable. Remove the var keyword to access the correct variable.
Also, I believe you want to remove the first element from the list. To do so, use shift.
keys.shift();
This will modify the array directly so you don't need to assign the result to anything.
var keys = keys[1,keys.length];
You've created a new (locally scoped) keys variable inside your function and it is masking the one containing the array you are trying to read. Remember: var statements are hoisted.
Use different names for those two variables (or if they are supposed to be the same variable, remove the var statement from the inner one).
NB: In JavaScript, [1,keys.length] is just a comma operator it isn't a slice.
As Quentin said, hoisting is happening in your function.
Other posters here also said it, you are defining a new local variable in your function.
Let me just show your function with hoisting happening when Javascript interprets your code:
var keys = [38,38,40,40,37,39,37,39,66,65];
function keyhandler (e) {
// Javascript defines all the local variables at top.
// This is what hoisting is
var key;
var keys;
e = e || event;
console.log(e.keyCode);
// at this point, local variable keys isn't initialized,
// that's why you are getting the undefined error.
key = keys[0];
if (key == e.keyCode) {
console.log("correct key");
if (keys.length == 1){
console.log("konami");
} else {
// you are referencing a local variable here
keys = keys[1,keys.length];
}
}
else {
// you are referencing a local variable here
keys = [38,38,40,40,37,39,37,39,66,65];
}
}
document.onkeydown = keyhandler;
As other posters mention, simply removing the var from keys may fix your issue, if that's what intended, but I think it's important to understand what Javascript technically does in this situation.
the issue was that
var key = keys[0];
is not a javascript expression as mentioned in the comment it needed to be replaced with
var key = keys.slice(0,2);

Javascript Invalid left-hand side in assignment

In an attempt to add queue type functionality to nodejs's Buffer class, I have constructed the following function:
Buffer.prototype.popleft = function(n) {
tRet = this.slice(0,n);
this = this.slice(n,this.length-1); // error here
return tRet;
};
however, this code yields the following error:
"ReferenceError: Invalid left-hand side in assignment"
I know that the issue is with the assignment of 'this' within the function, what I dont know is better way of accomplishing this same type of logic.
EDIT:
Ended up writing an object around the Buffer as shown below:
sPort.vBuffer = {}
sPort.vBuffer.buff = new Buffer(0);
sPort.vBuffer.off = 0;
sPort.vBuffer.expect = -1;
sPort.vBuffer.ReadChr = function() {
this.off ++;
return this.buff[this.off - 1];
};
sPort.vBuffer.ReadUInt32LE = function() {
this.off += 4;
return this.buff.readUInt32LE(this.off - 4);
}
sPort.vBuffer.ReadInt32LE = function() {
this.off += 4;
return this.buff.readInt32LE(this.off - 4);
}
sPort.vBuffer.Write = function(aData) {
this.buff = Buffer.concat([this.buff.slice(this.off),aData])
this.off = 0;
};
You can't assign to this. You can assign to a copy of it, but in your case it looks like that won't do any good.
According to the Node documentation, Buffer instances cannot be resized. Now, whether that's because Node simply provides no APIs to do that, or because of some set of internal implementation assumptions/dependencies, I don't know. There sure doesn't look like any way to alter the length of a Buffer instance.
You could use .copy to shift the contents, and then fill the last position(s) with some dummy value (null or undefined I guess).

Javascript consuming variables when variable a = variable b

I have a script setup like this (http://jsfiddle.net/YD66s/):
var countFull = new Array(0,1,2,3,4,5,6);
var countActive = new Array(0,1,2,3,4,5,6);
function pickRandom(a) {
if(arguments[1].length == 0) {
arguments[1] = arguments[0];
}
var m = Math.floor(Math.random()*arguments[1].length);
chosen = arguments[1].splice(m,1);
return chosen;
}
setInterval(function() {
pickRandom(countFull,countActive);
}, 1000);
When I run this I want the variable to be set for that function only. Instead it is affecting countFull towards the end because I make arguments[1] = arguments[0]. How in javascript can I just reference a variable but not consume it and ultimately arguments[1] becomes arguments[0].
Hope this makes sense. This is driving me nuts how different javascript variables are compared to other languages like PHP.
Javascript arrays are just pointers so when you do arguments[1] = arguments[0] you actually just set the pointer but the underlying arrays are the same. As a result, every time you modify arguments[1] you also modify arguments[0]. To do what you want, you need to copy the array. You could do it this way:
if (arguments[1].length == 0) {
for(var i = 0; i < arguments[0].length; i++) {
arguments[1][i] = arguments[0][i];
}
}
To copy an array, instead of referencing it, use copy = original.slice(0).

Categories