JavaScript Puzzlers!

5,697
-1

Published on

Stay alert and try to solve these "simple" JavaScript puzzles, designed to demonstrate some of the more obscure "features" of the language. Try your best to answer them! But, be careful — the solutions aren't as easy as they look.

*Presented at the Adobe MAX Conference, 2013

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
5,697
On Slideshare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
37
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

JavaScript Puzzlers!

  1. 1. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzlers: Puzzles to Make You Think (and write fewer bugs)Charles Bihis | Computer Scientist1
  2. 2. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Who am I? Charles Bihis Computer Scientist Adobe Identity Team Blog: blogs.adobe.com/charles Twitter: @charlesbihis GitHub: github.com/charlesbihis2
  3. 3. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.What can I expect? What are we going to talk about? Puzzlers! Maximus the Confused! Block Party! Let’s Print Some ZIP-Codes! Loopty Loop! Why Are We Bankrupt?! A Case of Mistaken Identity Will deal with only pure JavaScript (i.e. no libraries!)3 What are we NOT going to talk about? 3rd- party libraries or frameworks e.g. jQuery, Node.js, etc. Bugs
  4. 4. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.What is a Puzzler?A Puzzler is a very simple programming puzzle that demonstrates or exploitsweird behaviours and quirky edge-cases of a given programming language.4
  5. 5. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.How does this work?1. Code – I introduce the code.2. Question – I pose a multiple-choice question and you guess what theanswer is…think hard!3. Walkthrough – I walk through a reasonable explanation.4. Answer – I tell you the real answer.5. Moral – How can you avoid making mistakes like this in your own code.5
  6. 6. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Let’s start!6
  7. 7. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Maximus the Confused!7var commodusRule = thumbsUp;alert(Maximus the + (commodusRule === thumbsUp) ? Gladiator : Merciful);What does this print?a) Maximus the Gladiatorb) Maximus the Mercifulc) Errord) It variese) None of the above prints only "Gladiator"
  8. 8. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Order of operations dictates that the binary “+” operator takes precedence over the conditional “?”operator.8*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_PrecedenceMaximus the + (commodusRule === thumbsUp) ? Gladiator : Merciful;
  9. 9. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Order of operations dictates that the binary “+” operator takes precedence over the conditional “?”operator.9*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_PrecedenceMaximus the true ? Gladiator : Merciful;
  10. 10. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Order of operations dictates that the binary “+” operator takes precedence over the conditional “?”operator.10*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_PrecedenceGladiator;
  11. 11. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Order of operations dictates that the binary “+” operator takes precedence over the conditional “?”operator. According to the MDN (Mozilla Developer Network), the binary “+” operator has a precedence of 6while the conditional “?” operator has a precedence of 15. Note: This is below MOST commonly used operators (i.e. “*”, “/”, “%”, “<“, “>>” “!=”, “===“, etc).11*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_PrecedenceGladiator;
  12. 12. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Maximus the Confused…FIXED!12var commodusRule = thumbsUp;alert(Maximus the + (commodusRule === thumbsUp) ? Gladiator : Merciful);
  13. 13. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Maximus the Confused…FIXED!13var commodusRule = thumbsUp;alert(Maximus the + (commodusRule === thumbsUp ? Gladiator : Merciful));
  14. 14. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral Be aware of order-of-operations! Be explicit and place parenthesis accordingly to ensure correctand predictable order of execution.14
  15. 15. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Block Party!// global varvar name = "World!";(function() {// check if "name" definedif (typeof name === "undefined") {// local "shadow" varvar name = "Mr. Bond.";alert("Goodbye, " + name);} else {alert("Hello, " + name);}})();15What does this print?a) “Hello, World!”b) “Goodbye, Mr. Bond.”c) “Hello, ”d) “Hello, undefined”e) Errorf) It variesg) None of the above
  16. 16. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why?1. No block scope!2. “Hoisting”16for (var i = 0; i < MAX; i++){// do something}alert(i); // Note: "i" exists here!alert(i);for (var i = 0; i < MAX; i++){// do something}// Note: "i" exists here too!
  17. 17. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why?1. No block scope!2. “Hoisting”17for (var i = 0; i < MAX; i++){// do something}alert(i); // Note: "i" exists here!var i;alert(i); // Note: "i" exists here too!for (i = 0; i < MAX; i++){// do something}
  18. 18. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Block Party…FIXED!// global varvar name = "World!";(function() {// check if "name" definedif (typeof name === "undefined") {// local "shadow" varvar name = "Mr. Bond.";alert("Goodbye, " + name);} else {alert("Hello, " + name);}})();18
  19. 19. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Block Party…FIXED!// global varvar name = "World!";(function() {var name;// check if "name" definedif (typeof name === "undefined") {// local "shadow" varname = "Mr. Bond.";alert("Goodbye, " + name);} else {alert("Hello, " + name);}})();19// declaration hoisted here// assignment remains here
  20. 20. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Block Party…FIXED!// global varvar name = "World!";(function() {var name = "Mr. Bond.";// check if "name" definedif (typeof name === "undefined") {alert("Goodbye, " + name);} else {alert("Hello, " + name);}})();20
  21. 21. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral There is no block-level scoping in JavaScript Declare ALL of your variables at the top of your function21
  22. 22. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.What does this print?a) 93021192034132959b) 93021239220341816332959c) 930212034132959d) Errore) It variesf) None of theaboveJavaScript Puzzler – Let’s Print Some ZIP Codes!22// array of 5 valid zip-codesvar zipCodes = new Array("93021","02392","20341","08163","32959");// lets do something with each zip-code// for now, display themfor (var i = 0; i < zipCodes.length; i++) {// sanity checkif (!isNaN(parseInt(zipCodes[i])) &&parseInt(zipCodes[i]) > 0) {alert(parseInt(zipCodes[i]));}}Firefox Chrome 
  23. 23. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Syntax When you omit the optional “radix” parameter, the following behavior takes place: If the input string begins with “0x” or “0X”, radix of 16 is used (i.e. hexadecimal) If the input string begins with “0”, radix 8 is used (i.e. octal) OR radix 10 is used (i.e. decimal) If the input string begins with any other values, radix 10 is used (i.e. decimal) Particularly when dealing with string values with leading 0’s, Mozilla had this to say…23var num = parseInt(string, radix); // "radix" is optionalExactly which radix is chosen is implementation-dependent.For this reason ALWAYS SPECIFY A RADIX WHEN USING parseInt.*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt
  24. 24. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Another important note about the parseInt() API… A closer look…24// behaviour in Firefox v20.0parseInt("93021") = 93021 // displaysparseInt("02392") = (2 * 8) + (3 * 1) = 19 // displaysparseInt("20341") = 20341 // displaysparseInt("08163") = 0 // does NOT displayparseInt("32959") = 32959 // displays*Reference: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseIntIf parseInt encounters a character that is not a numeral in thespecified radix, it ignores it and all succeeding charactersand returns the integer value parsed up to that point.
  25. 25. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Let’s Print Some ZIP Codes…FIXED!25// array of 5 valid zip-codesvar zipCodes = new Array("93021","02392","20341","08163","32959");// lets do something with each zip-code// for now, display themfor (var i = 0; i < zipCodes.length; i++) {// sanity checkif (!isNaN(parseInt(zipCodes[i])) &&parseInt(zipCodes[i]) > 0) {alert(parseInt(zipCodes[i]));}}
  26. 26. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Let’s Print Some ZIP Codes…FIXED!26// array of 5 valid zip-codesvar zipCodes = new Array("93021","02392","20341","08163","32959");// lets do something with each zip-code// for now, display themfor (var i = 0; i < zipCodes.length; i++) {// sanity checkif (!isNaN(parseInt(zipCodes[i], 10)) && // radix value addedparseInt(zipCodes[i], 10) > 0) { // here tooalert(parseInt(zipCodes[i], 10)); // and here too}}
  27. 27. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral parseInt() takes an optional radix parameter. Omitting this optional parameter will cause unpredictablebehavior across browsers. Be explicit and ALWAYS include the radix parameter.27
  28. 28. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Loopty Loop!28var END = 9007199254740992;var START = END - 100;var count = 0;for (var i = START; i <= END; i++) {count++;}alert(count);// Math.pow(2, 53) What does this print?a) 0b) 100c) 101d) Errore) It variesf) None of theabove enters infinite loop
  29. 29. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? 9007199254740992 is a special number. Particularly, it is 2^53. Why is this special? First, we need to know something about how JavaScript represents numbers.29*Reference: http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
  30. 30. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? JavaScript numbers abide by the IEEE Standard for Floating-Point Arithmetic (IEEE 754). As such, all numbers in JavaScript are represented by double-precision 64-bit floating pointvalues… In binary…30*Reference: http://ecma262-5.com/ELS5_HTML.htm#Section_8.51.2345 = 12345 10x -4mantissaexponent1 11...111 11111111111...11163 62 53 52 0exponent mantissasign
  31. 31. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? 2^53 is the largest exact integral value that can be represented in JavaScript! From the ECMA specification… What does this mean?31*Reference: http://ecma262-5.com/ELS5_HTML.htm#Section_8.5Note that all the positive and negative integers whose magnitudeis no greater than 2^53 are representable in the Number type.var numA = Math.pow(2, 53);var numB = numA + 1;alert(numA === numB); // true!
  32. 32. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Loopty Loop…FIXED!32var END = 9007199254740992; // Math.pow(2, 53)var START = END - 100;var count = 0;for (var i = START; i <= END; i++) {count++;}alert(count);
  33. 33. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Loopty Loop…FIXED!33var START = 0;var END = 100;var count = 0;for (var i = START; i <= END; i++) {count++;}alert(count);
  34. 34. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral Be aware of your number representations and number ranges! There are REAL limitations imposed by your computer. Whendealing with large (or important) numbers, know them!34
  35. 35. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Why Are We Bankrupt?!35What does this print?a) 0b) 0.2c) 0.20d) None of the abovevar costOfCandy = 0.60; // 60 centsfunction calculateChange(cost, paid) {return paid - cost;}// pay for candy with 80 centsalert(calculateChange(costOfCandy, 0.80)); 0.20000000000000007
  36. 36. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? As we learned from the previous Puzzler, all JavaScript numbers use the IEEE 754 floating-pointarithmetic 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 beapproximated.36Example:123.45 = 12345 * 10^-2 // exact1 / 3 = 0.333333333333333 * 10^0 // approximation!
  37. 37. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Why Are We Bankrupt?!...FIXED!37var costOfCandy = 0.60; // 60 centsfunction calculateChange(cost, paid) {return paid - cost;}// pay for candy with 80 centsalert(calculateChange(costOfCandy, 0.80));
  38. 38. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Why Are We Bankrupt?!...FIXED!38var costOfCandy = 60; // 60 centsfunction calculateChange(cost, paid) {return paid - cost;}// pay for candy with 80 centsalert(calculateChange(costOfCandy, 80));// Use only integer math when dealing with money! To do this,// represent your money in terms of cents to begin with!//// e.g. use 1599 instead of 15.99 to represent $15.99
  39. 39. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral Floating-point arithmetic can be inaccurate when representing fractions. When dealing with money, deal in terms of cents! This makes all of your calculations integer-calculations, which are exact! BUT, not completely exact, though… Remember from our last Puzzler, it is exact only up until the largest representable integer value… 9007199254740992 (i.e. 2^53) So, as long as you are dealing with less than $9 quintillion, you’re fine using integer arithmetic inJavaScript :)39*Reference: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
  40. 40. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – A Case of Mistaken Identity!function showCase(value) {switch(value) {case "A":alert("Case A was selected.");break;case "B":alert("Case B here!");break;case "C":alert("This is Case C.");break;default:alert("Dont know what happened.");break;}}showCase(new String("A"));40What does this print?a) Case A was selected.b) Case B here!c) This is Case C.d) Don’t know whathappened.e) Errorf) It variesg) None of the above
  41. 41. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? The switch statement in JavaScript internally uses the strict equality operator (i.e. ===) as opposedto the non-strict equality operator (i.e. ==). The strict equality operator behaves exactly as the non-strict version, except that no type-conversions are done. So, when the switch statement evaluates equality, it checks that the following are true… Their types are equal Their uncast values are equal Notice, we invoked showCase() with a new String object.41alert(typeof "A"); // "string"alert(typeof new String("A")); // "object"
  42. 42. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – A Case of Mistaken Identity…FIXED!function showCase(value) {switch(value) {case "A":alert("Case A was selected.");break;case "B":alert("Case B here!");break;case "C":alert("This is Case C.");break;default:alert("Dont know what happened.");break;}}showCase(new String("A"));42
  43. 43. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – A Case of Mistaken Identity…FIXED!function showCase(value) {switch(value) {case "A":alert("Case A was selected.");break;case "B":alert("Case B here!");break;case "C":alert("This is Case C.");break;default:alert("Dont know what happened.");break;}}showCase("A");43
  44. 44. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral Get used to using the strict equality operator when possible. It will make you more aware of typeconversions and true equalities. From Douglas Crockford’s book “JavaScript: The Good Parts”…44JavaScript 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 theoperands are of the same type, but if they are of different types, they attempt to coerce thevalues, the rules by which they do that are complicated and unmemorable.
  45. 45. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.That’s it!Questions?45
  46. 46. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Thanks for coming! Charles Bihis Computer Scientist Adobe Identity Team Blog: blogs.adobe.com/charles Twitter: @charlesbihis GitHub: github.com/charlesbihis46
  47. 47. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.
  48. 48. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – That’s Odd!(function(){var values = [7, 4, 13, Infinity, -9];for (var i = 0; i < values.length; i++) {if (isOdd(values[i])) {alert(values[i]);}}})();function isOdd(num) {return num % 2 == 1;}48What does this print?a) 7, 13b) 7, 13, Infinity, -9c) 7, -9d) 7, 13, -9
  49. 49. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Let’s take a closer look…497 % 2 = 1 // displays4 % 2 = 0 // does NOT display13 % 2 = 1 // displaysInfinity % 2 = NaN // does NOT display-9 % 2 = -1 // does NOT display
  50. 50. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? -9 % 2 = -1? Really? JavaScript shares the same behavior as the Java implementation of the modulus (%) operator.That is, it must satisfy the following identity function for all integer values a and non-zero integervalues b. A side-implication of this behavior is that the result will have the same sign as the left operand!50(a / b) * b + (a % b) == a
  51. 51. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – That’s Odd…FIXED!(function(){var values = [7, 4, 13, Infinity, -9];for (var i = 0; i < values.length; i++) {if (isOdd(values[i])) {alert(values[i]);}}})();function isOdd(num) {return num % 2 == 1;}51
  52. 52. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – That’s Odd…FIXED!(function(){var values = [7, 4, 13, Infinity, -9];for (var i = 0; i < values.length; i++) {if (isOdd(values[i])) {alert(values[i]);}}})();function isOdd(num) {return num % 2 != 0;}52
  53. 53. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral Be careful about the signs of operands when using the modulusoperator.53
  54. 54. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Say What?!54What does this print?a) alert("BOOM!“);b) Hello, </script><script>alert("BOOM!");</script>c) BOOM!d) Errorfunction sayHello(name) {alert(Hello, + name);}sayHello(</script><script>alert("BOOM!");</script>);
  55. 55. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Let’s take a look at the code again…55function sayHello(name) {alert(Hello, + name);}sayHello(</script><script>alert("BOOM!");</script>);
  56. 56. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Let’s take a look at the code again… When a browser renders a page, first the HTML parser will parse the page and tokenize out all ofthe tags. Only after this is done, will it then allow the JavaScript parser to tokenize and execute whatevertokens the HTML parser believes are JavaScript scripts!56<script>function sayHello(name) {alert(Hello, + name);}sayHello(</script><script>alert("BOOM!");</script>);</script>
  57. 57. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.But why? Let’s take a look at the code again… Armed with this knowledge, we can see that the HTML parser will send 2 scripts to the JavaScriptparser to tokenize and execute… <script>function sayHello(name) { alert(Hello, + name); } sayHello(</script> <script>alert("BOOM!");</script>57<script>function sayHello(name) {alert(Hello, + name);}sayHello(</script><script>alert("BOOM!");</script>);</script>
  58. 58. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.A closer look Again, the HTML parser will send these two script tags to the JavaScript parser… <script>function sayHello(name) { alert(Hello, + name); } sayHello(</script> <script>alert("BOOM!");</script> If that name parameter is user-controlled, perhaps taken as input from the browser, or pulled froma datasource, whatever, then this is an open invitation for XSS attacks! Errors like this can expose huge security holes which may allow an attacker to potentially takeover a user’s browser!58
  59. 59. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Say What?!...FIXED! In this particular case, the fix must be done on the server-side. We want to eliminate the <script></script> tags from appearing in the source in the first place. Suggested solution is to use the OWASP ESAPI APIs… Stands for “The Open Web Application Security Project” “Enterprise Security API” https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API Have API bindings in all major languages including… Java Dot NET PHP JavaScript Python PHP59
  60. 60. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.JavaScript Puzzler – Say What?!...FIXED! For this particular Puzzler, we want to use ESAPI.encoder().encodeForJavaScript() Doing this on the server to JavaScript-encode the user-inputted variable, name, we get what weexpect…60
  61. 61. © 2013 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.Moral NEVER . TRUST . THE . USER Validate your input. Encode your output appropriately. i.e. HTML-encode for HTMLURL-encode for URLsJavaScript-encode for JavaScriptetc. Use standard libraries (i.e. don’t reinvent the wheel).61
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×