JavaScript Puzzlers !
Curreted list of puzzlers to make you think, and write fewer bugs
lahdiouiouadie
ouadie-lahdioui
Charles Bihis - Computer Scientist at Adobe Systems
@charlesbihis
github.com/charlesbihis
Inspired by
- Maximus the confused
- Block party
- Let's print some zip-codes
- Loopy loop
- Why are we bankrupt ?
- A case of mistaken identity
We will deal with only pure JavaScript (No 3rd partyl libraries or framworks)
What can i expect ?
We are going to talk about :
A puzzler is a very simple program that demonstrates or exploits weird behaviours and quirky edge-cases of a given programming language
What is a Puzzler ?
I introduce the code, I pose a multiple-choice question and you guess what the answer is ... think hard !
Walkthrought : I walk through a reasonable explanation
Answer : I tell you the real answer
Moral : How can you avoid making mistakes line in your own code
How does this work ?
Do you really
know
JavaScript ?
JavaScript Puzzler #1 : Maximus the confused !
var commonusRule = 'thumbsUp';
console.log('Maximus the' + (commonusRule === 'thumbsUp') ? 'Gladiator' : 'Merciful');
What does this print ?
A) Maximus the Gladiator
B) Maximus the Merciful
C) Error
D) It varies
E) None of the above
Print only Gladiator
Order of operations dictates that the binary '+' operator takes precedence over the conditional '?' operator
var commonusRule = 'thumbsUp';
console.log('Maximus the' + (commonusRule === 'thumbsUp') ? 'Gladiator' : 'Merciful');
'Maximus the true' ? 'Gladiator' : 'Merciful';
But why ?
var commonusRule = 'thumbsUp';
console.log('Maximus the' + (commonusRule === 'thumbsUp' ? 'Gladiator' : 'Merciful'));
Maximus the confused ... FIXED !
var commonusRule = 'thumbsUp';
console.log('Maximus the' + (commonusRule === 'thumbsUp') ? 'Gladiator' : 'Merciful');
Old :
New :
- Be aware of order-of-operations !
- Be explicit and place paranthesis accordingly to ensure correct and predictable order of execution
Moral
// Global var
var name = 'World!';
(function() {
// Check if 'name' defined
if (typeof name === 'undefined') {
// local "shadow" var
var name = 'Mr. Bond.';
console.log('Goodbye, ' + name);
} else {
console.log('Hello, ' + name);
}
})();
A) 'Hello, World!'
B) 'Goodbye, Mr.Bond.'
C) 'Hello,'
D) 'Hello, undefined'
E) Error
F) It varies
G) Non of the above
JavaScript Puzzler #2: Block party !
What does this print ?
No block scope !
Hoisting
for (var i = 0; i < MAX; i++) {
// do something
}
console.log(i); // Note: 'i' exists here !
console.log(i); // Note: 'i' exists here too !
for (var i = 0; i < MAX; i++) {
// do something
}
But why ?
var i;
// Global var
var name = 'World!';
(function() {
var name; // declaration hoisted here
// Check if 'name' defined
if (typeof name === 'undefined') {
name = 'Mr. Bond.'; // assignment remains here
console.log('Goodbye, ' + name);
} else {
console.log('Hello, ' + name);
}
})();
Block party ... FIXED !
// Global var
var name = 'World!';
(function() {
// Check if 'name' defined
if (typeof name === 'undefined') {
// local "shadow" var
var name = 'Mr. Bond.';
console.log('Goodbye, ' + name);
} else {
console.log('Hello, ' + name);
}
})();
Old :
New :
- There is no block-level scoping in JavaScript
- Declare all of your variables at the top of your function
Moral
var zipCodes = new Array('15','015', 015, '0X15');
// let's display each zip-code
for (var i = 0; i < zipCodes.length; i++) {
// sanity check
if (!isNaN(parseInt(zipCodes[i])) &&
parseInt(zipCodes[i]) > 0) {
console.log(parseInt(zipCodes[i]));
}
}
What does this print ?
B) 15
15
13
21
C) 15
015
15
D) Error
E) It varies
F) None of the above
JavaScript Puzzler #3 : Let's print some zip codes !
A) 15
015
15
Nan
parseInt(string, radix);
- If the input string begins with "0x" or "0X", radix is 16 (hexadecimal) and the remainder of the string is parsed.
- If the input string begins with "0", radix is 8 (octal) or 10 (decimal).
- If the input string begins with any other value, the radix is 10 (decimal).
But why ?
If radix is undefined, 0 or absent, JS assumes the following:
A closer look ...
parseInt('15') = 15
parseInt(15) = 15
parseInt(015) = 13
parseInt(0x15) = 21
But why ?
// it's another zipCodes
var zipCodes = new Array('93021','02392','20341','08163','32959');
// let's display each zip-code
for (var i = 0; i < zipCodes.length; i++) {
// sanity check
if (!isNaN(parseInt(zipCodes[i], 10)) && // radix value added
parseInt(zipCodes[i], 10) > 0) { // here too
console.log(parseInt(zipCodes[i], 10)); // and here too
}
}
Let's print some zip codes ... FIXED!
// it's another zipCodes
var zipCodes = new Array('93021','02392','20341','08163','32959');
// let's display each zip-code
for (var i = 0; i < zipCodes.length; i++) {
// sanity check
if (!isNaN(parseInt(zipCodes[i])) &&
parseInt(zipCodes[i]) > 0) {
console.log(parseInt(zipCodes[i]));
}
}
Old :
New :
- parseInt() takes an optional radix parameter
- Omitting this optional parameter will cause unpredictable behavior across browsers
- Be explicit and always include the radix parameter
Moral
var END = 9007199254740992; // Math.pow(2, 53)
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
What does this print ?
A) 0
B) 100
C) 101
D) Error
E) It varies
F) None of the above
JavaScript Puzzler #4 : Loopty Loop !
Enters infinite loop
- 9007199254740992 is a special number. Particularity, it is 2^53
- All numbers in JavaScript are represented by double-pricision 64-bit floating point values (IEEE 754)
- 2^53 is the largest exact integral value that can be represented in JavaScript
var numA = Math.pow(2, 53);
var numB = numA + 1;
console.log(numA === numB);
- what does this mean ?
But why ?
var END = 9007199254740992; // Math.pow(2, 53)
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
var END = 100;
var START = 0;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
Loopty Loop ... FIXED !
Old :
New :
- Be aware of your number representations and number ranges !
- There are REAL limitations imposed by your computer, when dealing with large numbers, know them !
Moral
var costOfCandy = 0.60; // 60 cents
function calculateChange(cost, paid) {
return paid - cost;
}
// pay fod candy with 80 cents
console.log(calculateChange(costOfCandy, 0.80))
A) 0
B) 0.2
C) 0.20
D) None of the above
What does this print ?
JavaScript Puzzler #5 : Why are we bankrupt ?
0.20000000000000007
- As we learned from the previous Puzzler, all JavaScript numbers use the IEEE 754 floating-point arthmetic specification
- Because of this, values are not represented exactly, but rather as a fraction
- Some non-integer values simply CANNOT be expressed exactly in this way, they must be approximated :
123.45 = 12345 * 10^2 // exact
1/3 = 0.3333333333333333 // approximation
But why ?
var costOfCandy = 0.60; // 60 cents
function calculateChange(cost, paid) {
return paid - cost;
}
// pay fod candy with 80 cents
console.log(calculateChange(costOfCandy, 0.80))
var costOfCandy = 60;
function calculateChange(cost, paid) {
return paid - cost;
}
// pay fod candy with 80 cents
console.log(calculateChange(costOfCandy, 80))
Use only integer math when dealing with money : 60 instead of 0.60 to represent 0.60 cents
Why are we bankrupt ... FIXED ?
Old :
New :
- Floating-point arithmetic can be inaccurate when representing fractions
- when dealing with money, deal in terms of cents ! this makes att of your calculations integer-calculations, wich are exact
- BUT, is not completely exact, thought ...
- Remamber from our last puzzler, it is exact only up until the largest representable integer value 2^53
Moral
function showCase(value) {
switch(value) {
case "A":
console.log("Case A was selected.");
break;
case "B":
console.log("Case B was selected.");
break;
case "C":
console.log("Case C was selected.");
break;
default
console.log("Don't know what happened.");
break;
}
}
showCase(new String("A"));
A) Case A was selected.
B) Case B was selected.
C) Case C was selected.
D) Don't know what happened.
E) Error
F) None of the above
What does this print ?
JavaScript Puzzler #6 : A case of mistaken identity !
- The switch statement in JavaScript internally uses the stric equality operator (===)
- Notice, we invoked showCase() with a new String object :
console.log(typeof 'A') // string
console.log(typeof new String('A')) // object
But why ?
function showCase(value) {
switch(value) {
case "A":
console.log("Case A was selected.");
break;
case "B":
console.log("Case B was selected.");
break;
case "C":
console.log("Case C was selected.");
break;
default
console.log("Don't know what happened.");
break;
}
}
// showCase(new String("A"));
showCase("A");
A case of mistaken identity ... FIXED!
function showCase(value) {
switch(value) {
case "A":
console.log("Case A was selected.");
break;
case "B":
console.log("Case B was selected.");
break;
case "C":
console.log("Case C was selected.");
break;
default
console.log("Don't know what happened.");
break;
}
}
showCase(new String("A"));
Old :
New :
- Use the strict equality operator when possible. it will make you more aware of type conversions and true equalities
JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=.
The good ones work the way you would expect. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values.
Moral
That's it !
Thank you for coming !
lahdiouiouadie
ouadie-lahdioui
JavaScript Puzzlers
By Ouadie LAHDIOUI
JavaScript Puzzlers
Curreted list of puzzlers to make you think, and write fewer bugs
- 1,480