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

  1. Maximus the confused
  2. Block party
  3. Let's print some zip-codes
  4. Loopy loop
  5. Why are we bankrupt ?
  6. 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

Made with Slides.com