├── README.md └── ANSWERS.md /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Quiz 2 | 3 | **Note:** 4 | 5 | The purpose of this quiz isn't to be a brain-buster. It is meant for recruiters to present to front-end developer candidates. 6 | 7 | The intent is to weed out some "Just use jQuery" applicants, but allow those that know JavaScript to pass fairly easily. 8 | 9 | ## Intro Questions 10 | 11 | 01. When might comparative type coercion occur? How would you avoid it? How would you change a "falsy" or "truthy" value into a real boolean? 12 | 13 | 02. Describe how variable scope works. Explain how to create a closure using a self-executing anonymous function (also called IIFE: immediately-invoked function expression). 14 | 15 | 03. Explain briefly how prototypal inheritance differs from class-based, "classical" inheritance. 16 | 17 | 04. Describe how the "module pattern" works. Explain how the "revealing module pattern" expands upon it. 18 | 19 | 05. How does a client-side MVC (or MVVM) approach work? What is your preferred MV* JS framework? 20 | 21 | ## Additional Questions 22 | 23 | 06. Why do these yield different results? 24 | 25 | ```js 26 | '1' + 2 + 3 ; // Equals '123' 27 | 3 + 2 + '1'; // Equals '51' 28 | 3 + 2 + 1 ; // Equals 6 29 | ``` 30 | 31 | 07. Why is `0.3` *not* the result of the following addition? How do you work around this peculiarity? 32 | 33 | ```js 34 | 0.1 + 0.2; // Equals 0.30000000000000004 35 | ``` 36 | 37 | 08. Describe how variable hoisting works, and how to avoid bugs that may arise from it. 38 | 39 | 09. How do these differ? 40 | 41 | ```js 42 | function foo() {} 43 | 44 | // versus 45 | 46 | var foo = function() {}; 47 | ``` 48 | 49 | 10. When might you use a function's `call()` method, or its `apply()` method? 50 | 51 | 11. Explain how to determine if a variable is an array or an object. (*Hint:* `typeof` lies!) 52 | 53 | 12. In the following example, what is foo aliased to? (*Hint:* It is what `this` means.) 54 | 55 | ```js 56 | (function(foo) { 57 | // What is 'foo' aliased to? 58 | })(this); 59 | ``` 60 | 61 | 13. In JavaScript (and the DOM), some global variables are actually mutable, such as: `window`, `document`, and `undefined`. How would you write code to ensure these were predictably available for use? Assuming someone had injected this code, how would you work around it? (*Hint:* See the previous question.) 62 | 63 | ```js 64 | var window = ''; 65 | var document = 0; 66 | var undefined = true; 67 | ``` 68 | 69 | 14. In one line of code, how you would make a copy of an array? 70 | 71 | 15. What is the difference between `setInterval` and `setTimeout`? *Bonus:* What is the lowest cross-browser increment that each can accurately use? 72 | 73 | 16. Explain how `delete` works. What types of things cannot be deleted? 74 | 75 | 17. Describe how event delegation works, and when you should use it to handle UI interaction. Example markup… 76 | 77 | ```html 78 | 89 | ``` 90 | 91 | 18. What does this snippet of code do? 92 | 93 | ```js 94 | var foo = bar ? bar : 0; 95 | ``` 96 | 97 | 19. When might you write something like this, and what is it shorthand for? 98 | 99 | ```js 100 | foo && foo.bar(); 101 | ``` 102 | 103 | 20. How do `parseInt` and `parseFloat` differ? When would you use a number's `toFixed()` method? In what instance might the following code snippet actually make sense to use? 104 | 105 | ```js 106 | var my_number = my_string - 0; 107 | ``` 108 | 109 | 21. Write a function named `sum` that returns the total of any number of parameters. Example… 110 | 111 | ```js 112 | // Should equal 15 113 | sum(1, 2, 3, 4, 5); 114 | 115 | // Should equal 0 116 | sum(5, null, -5); 117 | 118 | // Should equal 10 119 | sum('1.0', false, 1, true, 1, 'A', 1, 'B', 1, 'C', 1, 'D', 1, 'E', 1, 'F', 1, 'G', 1); 120 | 121 | // Should equal 0.3, not 0.30000000000000004 122 | sum(0.1, 0.2); 123 | ``` 124 | 125 | ## BONUS Question 126 | 127 | When the following code is pasted into a browser's console, what does it output? 128 | 129 | ```js 130 | (function(window) { 131 | 132 | var hello = 'Hello World'; 133 | 134 | var arr = [ 135 | '\x21', 136 | '\x6E', 137 | '\x61', 138 | '\x6D', 139 | '\x74', 140 | '\x61', 141 | '\x42' 142 | ]; 143 | 144 | var str = ''; 145 | var i = 16; 146 | 147 | while (i--) { 148 | str += 1 * hello; 149 | str += i % 2 === 0 ? '\x2C\x20' : ''; 150 | } 151 | 152 | str = str.replace(/\x4E+/g, '\x6E'); 153 | str = str.replace(/\x6E\x2C/g, '\x2C'); 154 | str = str.slice(0, 1).toUpperCase() + str.slice(1, str.length); 155 | 156 | str += arr.reverse().join(''); 157 | 158 | window.console.log(str); 159 | 160 | })(this); 161 | ``` -------------------------------------------------------------------------------- /ANSWERS.md: -------------------------------------------------------------------------------- 1 | # JavaScript Quiz — with Answers 2 | 3 | **Note:** 4 | 5 | If you've skipped the questions and come straight here, or have found this page because you're trying to cheat: Tisk, tisk! 6 | 7 | Joking aside though, if you're a job applicant skimming these answers so that you can make (fake?) it through an interview, bear in mind that a cheat sheet isn't the same as knowledge. You should still strive to understand these concepts. 8 | 9 | **:)** 10 | 11 | ## Intro Questions 12 | 13 | 01. When might comparative type coercion occur? How would you avoid it? How would you change a "falsy" or "truthy" value into a real boolean? 14 | 15 | **Answer:** 16 | 17 | Type coercion occurs when comparing values of different types. For instance, if you're comparing a number with a string, they will be changed on the fly (coerced) into a loose "truthy" or "falsy" comparison. To avoid type coercion, to ensure you are comparing values strictly and taking their potentially differing types into account, always use triple-equals `===` instead of the more commonly used (in other languages) double-equals `==`. Likewise, when comparing if two values are *not* the same, use `!==` instead of `!=`. 18 | 19 | ```js 20 | // This is good 21 | if (a === b) {} 22 | if (a !== b) {} 23 | 24 | // This is bad 25 | if (a == b) {} 26 | if (a != b) {} 27 | ``` 28 | 29 | Sometimes you will have a variable that may be "truthy" or "falsy" itself, but need to infer a real `true` or `false` boolean from it. In such cases, you can use double negation `!!`, to say "not not something," the result of which is either `true` or `false`. 30 | 31 | ```js 32 | // Is always a real boolean 33 | var real_boolean = !!something; 34 | ``` 35 | 36 | 02. Describe how variable scope works. Explain how to create a closure using a self-executing anonymous function (also called IIFE: immediately-invoked function expression). 37 | 38 | **Answer:** 39 | 40 | Variables in JavaScript are implicitly global (a very bad thing) unless you declare them with a preceding `var` keyword. Doing so ensures that the variable is scoped no higher than the nearest containing function. A self-executing anonymous function (aka IIFE) is a function that immediately runs, keeping the variables inside it safely quarantined away from the global scope. 41 | 42 | For more on this topic: 43 | 44 | http://benalman.com/news/2010/11/immediately-invoked-function-expression 45 | 46 | ```js 47 | // These are functions 48 | // used as closures... 49 | 50 | // This is good 51 | (function() { 52 | var foo = 'bar'; 53 | })(); 54 | 55 | // This is bad 56 | (function() { 57 | foo = 'bar'; 58 | })(); 59 | ``` 60 | 61 | 03. Explain briefly how prototypal inheritance differs from class-based, "classical" inheritance. 62 | 63 | **Answer:** 64 | 65 | Classical and prototypal inheritance both involve having a base template from which other things are created. For example, one might have a class named `Dog`, from which you could create an instance of a dog, named `sparky`. If you changed something related to `Dog`, then `sparky` would immediately receive those attributes as well. 66 | 67 | Class based languages distinguish between templates (classes) and instances created from classes, whereas prototypal languages do not. The "super class" of an object in JavaScript is simply another instance. 68 | 69 | ```js 70 | // Creating a base Dog, which is alive. 71 | function Dog() {} 72 | Dog.prototype.is_alive = true; 73 | 74 | // All instances of Dog will have 4 legs. 75 | Dog.prototype.legs = 4; 76 | 77 | // Sparky is alive, has 4 legs, and is brown. 78 | var sparky = new Dog(); 79 | sparky.color = 'brown'; 80 | 81 | // Now sparky has 2 wings, as do all other dogs! 82 | Dog.prototype.wings = 2; 83 | 84 | // We just killed all dogs, and sparky too. Oh no! 85 | Dog.prototype.is_alive = false; 86 | ``` 87 | 88 | 04. Describe how the "module pattern" works. Explain how the "revealing module pattern" expands upon it. 89 | 90 | **Answer:** 91 | 92 | The "module pattern" expands upon the concept of a self-executing anonymous function, by having a `return` at the end, which exposes some properties/methods as publicly accessible (and therefore, mutable). The result of this `return` is assigned to a variable, which serves as an app/module "namespace." 93 | 94 | The "revealing module pattern" works in much the same way, except instead of bundling up all properties/methods into a single object literal, local variables are assigned to properties of an object literal at the end of the closure. 95 | 96 | ```js 97 | // Module Pattern 98 | var MY_APP_ONE = (function() { 99 | // Private variables 100 | var secret_1 = 'Hello'; 101 | var secret_2 = 'World'; 102 | 103 | // Publicly exposed 104 | return { 105 | // MY_APP_ONE.foo 106 | foo: function() { 107 | // Do stuff 108 | }, 109 | // MY_APP_ONE.bar 110 | bar: function() { 111 | // Do stuff 112 | } 113 | } 114 | })(); 115 | 116 | // "Revealing" Module Pattern 117 | var MY_APP_TWO = (function() { 118 | // Private variables 119 | var secret_1 = 'Hello'; 120 | var secret_2 = 'World'; 121 | 122 | // MY_APP_TWO.foo 123 | function foo() { 124 | // Do stuff 125 | } 126 | 127 | // MY_APP_TWO.bar 128 | function bar() { 129 | // Do stuff 130 | } 131 | 132 | // "Revealed" here, 133 | // otherwise private 134 | return { 135 | foo: foo, 136 | bar: bar 137 | } 138 | })(); 139 | ``` 140 | 141 | 05. How does a client-side MVC (or MVVM) approach work? What is your preferred MV* JS framework? 142 | 143 | **Answer:** 144 | 145 | A client-side MVC (model, view, controller) approach enforces "separation of concerns" across the front-end code of an app. You have *models* that handle the domain-specific information of your app, *controllers* that handle the user interactions (or programmatically triggered events), and *views* present information from the *model* in a palatable way to the user. A *view* also contains elements which are interacted with, that a *controller* observes (and if need be, informs a *model*). 146 | 147 | MVVM differs slightly from MVC, in that it has "models," "views," and "view models." A *view model* handles the observation of events (handled by a *controller* in MVC), as well as the data-binding between a *model* and a *view*. 148 | 149 | Answers for preferred MV* frameworks may include: Backbone.js, Ember.js, or Knockout.js. There are also MVC aspects to larger JS libraires, such as YUI and Dojo. 150 | 151 | Backbone.js (Model View Router)
152 | http://backbonejs.org 153 | 154 | Ember.js (MVC)
155 | http://emberjs.com 156 | 157 | Knockout.js (MVVM)
158 | http://knockoutjs.com 159 | 160 | YUI App Framework (MVC)
161 | http://yuilibrary.com/yui/docs/app 162 | 163 | Dojo MVC
164 | http://dojotoolkit.org/reference-guide/1.8/dojox/mvc.html 165 | 166 | **Note:** 167 | 168 | MVC and/or MVVM in JavaScript differ from frameworks such as .NET MVC or Ruby on Rails, in that, the entirety of a client-side MVC is housed in the "V" of a server-side framework. That is, HTML/CSS/JS, sitting atop a server-side framework — which itself may (or may not) be MVC in nature. 169 | 170 | ## Additional Questions 171 | 172 | 06. Why do these yield different results? 173 | 174 | ```js 175 | '1' + 2 + 3 ; // Equals '123' 176 | 3 + 2 + '1'; // Equals '51' 177 | 3 + 2 + 1 ; // Equals 6 178 | ``` 179 | 180 | **Answer:** 181 | 182 | When the JavaScript interpreter sees a string value (contained within quotes), it immediately begins type coercing all the following values into strings. In the first line, `'1'` is a string, thus `2` becomes `'2'` and `3` becomes `'3'`. They are simply appended to one another, the same way this might work… 183 | 184 | ```js 185 | 'A' + 'B' + 'C'; // Equals 'ABC' 186 | ``` 187 | 188 | On the second line, because `3` and `2` are real numbers, so those are first added together, yielding `5`. The next value `'1'` is actually a string, so `5` is converted to `'5'`, and it is string concatenated with `'1'`, yielding `'51'` as a string. 189 | 190 | On the third line, all the values are real numbers, so they are added together as expected, yielding the number `6`. 191 | 192 | 07. Why is `0.3` *not* the result of the following addition? How do you work around this peculiarity? 193 | 194 | ```js 195 | 0.1 + 0.2; // Equals 0.30000000000000004 196 | ``` 197 | 198 | **Answer:** 199 | 200 | Believe it or not, this is by design. It is simply how floating-point calculations work, but does not make sense when you encounter it in practical usage. In order to do math with any kind of decimal precision, you need to multiply values by `10`, do addition or subtraction as full integers, and then divide back by `10`. It is a laborious, but necessary step, especially when calculating anything related to money in the browser. 201 | 202 | More on floating-point arithmetic here… 203 | 204 | http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html 205 | 206 | 08. Describe how variable hoisting works, and how to avoid bugs that may arise from it. 207 | 208 | Regardless of where you declare your variables within the scope of a function, the JavaScript interpreter will always move them to the beginning of the function in its internal understanding of the code. This phenomenon is called "hoisting." Since the process itself cannot be avoided, it is best to declare your variables at the top of a function's scope. Though, in practicality, it tends not to be an issue as long as you don't attempt to use variables before they are defined. 209 | 210 | 09. How do these differ? 211 | 212 | ```js 213 | function foo() {} 214 | 215 | // versus 216 | 217 | var foo = function() {}; 218 | ``` 219 | 220 | In the first case, a function named `foo` is being created. The interpreter parses function names before attempting to execute any code, so that function can be able called before it's declared. In the second case, an *anonymous* function is being defined with an expression and is assigned to a variable. 221 | 222 | Variables cannot be referenced before they are declared, and that function can't be called because it has no name. In practice, because of variable hoisting, one should always declare a function before using it, regardless of syntax. 223 | 224 | ```js 225 | // This breaks 226 | my_function_one(); 227 | var my_function_one = function() {}; 228 | 229 | // This works 230 | my_function_two(); 231 | function my_function_two() {} 232 | ``` 233 | 234 | 10. When might you use a function's `call()` method, or its `apply()` method? 235 | 236 | The `call()` method takes any number of parameters, the first of which is the context of `this`. Let's say you wanted to set properties on a new instance of a constructor. Example… 237 | 238 | ```js 239 | function Human(first_name, last_name) { 240 | // Default to "John Doe" 241 | this.first_name = first_name || 'John'; 242 | this.last_name = last_name || 'Doe'; 243 | } 244 | 245 | // Pass in a new name 246 | function Person(first_name, last_name, gender) { 247 | Human.call(this, first_name, last_name); 248 | this.gender = gender || 'male'; 249 | } 250 | 251 | // logs "John Doe is male" 252 | var john = new Person(); 253 | console.log(john.first_name, john.last_name, 'is', john.gender); 254 | 255 | // logs "Pam Jones is female" 256 | var pam = new Person('Pam', 'Jones', 'female'); 257 | console.log(pam.first_name, pam.last_name, 'is', pam.gender); 258 | ``` 259 | 260 | The `apply()` method is similar to `call()`, except that it takes a single array as its second parameter (instead of an arbitrary number of parameters). It is typically used to run built-in functions from object prototypes. The following example can take any number of values and concatenate them into a single string… 261 | 262 | ```js 263 | // Take any number of parameters, stringify them 264 | function concatenate() { 265 | return String.prototype.concat.apply('', arguments); 266 | } 267 | 268 | // Outputs 'false1hello' 269 | concatenate(false, 1, 'hello'); 270 | ``` 271 | 272 | 11. Explain how to determine if a variable is an array or an object. (*Hint:* `typeof` lies!) 273 | 274 | **Answer:** 275 | 276 | If you only checked a variable's `typeof`, regardless if it were an object or an array, it would reply `'object'`. 277 | 278 | One possible answer to this question would be to check if it's an object, *and* determine if it has a numeric `.length` (which may be `0` if it's an empty array). However, this would also work for the `arguments` object (all the parameters passed into any given function), which is technically *not* an array. Additionally, this falls down if an object should have a `.length` property. 279 | 280 | ```js 281 | // Real array 282 | var my_array = []; 283 | 284 | // Imposter! 285 | var my_object = {}; 286 | my_object.length = 0; 287 | 288 | // Potentially faulty 289 | function is_this_an_array(param) { 290 | if (typeof param === 'object' && !isNaN(param.length)) { 291 | console.log('Congrats, you have an array!'); 292 | } 293 | else { 294 | console.log('Bummer, not an array'); 295 | } 296 | } 297 | 298 | // Works 299 | is_this_an_array(my_array); 300 | 301 | // Works, but is incorrect 302 | is_this_an_array(my_object); 303 | ``` 304 | 305 | Another way to answer this question would be to use a more obscure method, calling `toString()` to convert the variable in question to a string representation of its type. This will work for a real array, but fail for the `arguments` object, which when converted into a string is `[object Arguments]`. It also won't be fooled by an object with a numeric `.length` attribute. 306 | 307 | ```js 308 | // Real array 309 | var my_array = []; 310 | 311 | // Imposter! 312 | var my_object = {}; 313 | my_object.length = 0; 314 | 315 | // Rock solid 316 | function is_this_an_array(param) { 317 | if (Object.prototype.toString.call(param) === '[object Array]') { 318 | console.log('Congrats, you have an array!'); 319 | } 320 | else { 321 | console.log('Bummer, not an array'); 322 | } 323 | } 324 | 325 | // Works 326 | is_this_an_array(my_array); 327 | 328 | // Not an array, yay! 329 | is_this_an_array(my_object); 330 | ``` 331 | 332 | Also, though potentially unreliable in multi-frame DOM environments, `instanceof` is a perfectly suited operator. 333 | 334 | Further reading: *"Instanceof Considered Harmful…"* 335 | 336 | http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray 337 | 338 | ```js 339 | var my_array = []; 340 | 341 | if (my_array instanceof Array) { 342 | console.log('Congrats, you have an array!'); 343 | } 344 | ``` 345 | 346 | As of JavaScript 1.8.5 (ECMAScript 5), the aptly named `.isArray()` method serves this purpose… 347 | 348 | https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray 349 | 350 | ```js 351 | var my_array = []; 352 | 353 | if (Array.isArray(my_array)) { 354 | console.log('Congrats, you have an array!'); 355 | } 356 | ``` 357 | 358 | 12. In the following example, what is foo aliased to? (*Hint:* It is what `this` means.) 359 | 360 | ```js 361 | (function(foo) { 362 | // What is 'foo' aliased to? 363 | })(this); 364 | ``` 365 | 366 | **Answer:** 367 | 368 | In a browser, it is `window`. In Node.js, it is `global`. 369 | 370 | The context of the `this` keyword is always equal to the present scope in which it is used. At the global level (in a browser), `this` is the same as `window`, because there is no higher level of scope. In Node.js (running on a server), since there isn't actually a `window`, the highest level scope is simply called `global`. 371 | 372 | 13. In JavaScript (and the DOM), some global variables are actually mutable, such as: `window`, `document`, and `undefined`. How would you write code to ensure these were predictably available for use? Assuming someone had injected this code, how would you work around it? (*Hint:* See the previous question.) 373 | 374 | ```js 375 | var window = ''; 376 | var document = 0; 377 | var undefined = true; 378 | ``` 379 | 380 | Building upon the concept from the previous question, in order to protect against the overwriting of otherwise reliably available variables, we have to pass them back into the scope of a closure, re-aliased to their correct values. Note that we're not actually passing in a value for `undefined` because the browser's concept of nothingness is what we want it to be re-aliased to. Since a third parameter is not specified, `undefined` now means what it should: Nothingness. 381 | 382 | ```js 383 | (function(window, document, undefined) { 384 | // Now window, document, and undefined 385 | // are back to their original meanings. 386 | })(this, this.document); 387 | ``` 388 | 389 | 14. In one line of code, how you would make a copy of an array? 390 | 391 | Acceptable answers would include using the `slice()` method, or concatenating with an empty array. Calling `slice()` with no parameters simply returns a copy of the original array (rather than a reference to it, like the last example below). Concatenating the contents of the `old_array` to an empty array `[]` freshly populates the new array with the values from the old one. 392 | 393 | ```js 394 | var old_array = [1, 2, 3]; 395 | 396 | // This works 397 | var new_array_1 = old_array.slice(); 398 | 399 | // This works too 400 | var new_array_2 = [].concat(old_array); 401 | 402 | // This does NOT work, because any edits to new_array_3 403 | // will alter the original, old_array. NOT what we want! 404 | var new_array_3 = old_array; 405 | ``` 406 | 407 | 15. What is the difference between `setTimeout` and `setInterval`? *Bonus:* What is the lowest cross-browser increment that each can accurately use? 408 | 409 | Using `setTimeout` calls a function after a certain amount of time has elapsed. Using `setInterval` calls the function continuously, each time a specified duration has passed. 410 | 411 | ```js 412 | // In milliseconds 413 | var five_seconds = 5000; 414 | 415 | function call_me(str) { 416 | console.log('I was called from ' + str); 417 | } 418 | 419 | // This runs once, after 5 seconds 420 | setTimeout(function() { 421 | call_me('setTimeout'); 422 | }, five_seconds); 423 | 424 | // This runs every 5 seconds 425 | setInterval(function() { 426 | call_me('setInterval'); 427 | }, five_seconds); 428 | ``` 429 | 430 | *Bonus:* The lowest increment that can be used reliably across various browsers is `15.6` milliseconds. This is due to an idiosyncrasy in Windows. More on that here… 431 | 432 | http://www.nczonline.net/blog/2011/12/14/timer-resolution-in-browsers 433 | 434 | 16. Explain how `delete` works. What types of things cannot be deleted? 435 | 436 | **Answer:** 437 | 438 | Using `delete` will destroy variables and properties, making them `undefined` when you try to access them. Things that cannot be deleted include properties of objects created from a prototype (but you can delete properties of the prototype itself). Additionally, if you call `delete` on an item in an array, the array's `.length` is unaffected. 439 | 440 | ```js 441 | // 442 | // Prototype example 443 | // 444 | 445 | function Automobile() {} 446 | Automobile.prototype.wheels = 4; 447 | 448 | var my_car = new Automobile(); 449 | 450 | // Outputs 4 451 | delete my_car.wheels; 452 | console.log(my_car.wheels); 453 | 454 | // Outputs undefined 455 | delete Automobile.prototype.wheels; 456 | console.log(my_car.wheels); 457 | 458 | // 459 | // Array example 460 | // 461 | 462 | var my_array = ['zero', 'one', 'two']; 463 | 464 | // Outputs 3 465 | console.log(my_array.length); 466 | 467 | // Outputs 'one' 468 | console.log(my_array[1]); 469 | 470 | delete my_array[1]; 471 | 472 | // Outputs undefined 473 | console.log(my_array[1]); 474 | 475 | // Outputs 3 476 | console.log(my_array.length); 477 | ``` 478 | 479 | 17. Describe how event delegation works, and when you should use it to handle UI interaction. Example markup… 480 | 481 | ```html 482 | 493 | ``` 494 | 495 | **Answer:** 496 | 497 | While you would typically use a library for DOM events, it's worth understanding how this works. Essentially, event delegation involves attaching an event listener to a higher-level element, and then determining if the event took place atop one of its children elements. The ability to watch for these events firing upwards to their parent is called "event bubbling." Here's how it would work… 498 | 499 | ```js 500 | (function(d) { 501 | function handle_special_clicks(ev) { 502 | var tag = ev.target.tagName.toLowerCase(); 503 | 504 | if (tag === 'a') { 505 | // Don't follow the link 506 | ev.preventDefault(); 507 | 508 | console.log('You clicked a special link!') 509 | } 510 | } 511 | 512 | var special = d.getElementById('special'); 513 | 514 | // For modern browsers 515 | if (special.addEventListener) { 516 | special.addEventListener('click', handle_special_clicks, false); 517 | } 518 | // For older versions of IE 519 | else if (special.attachEvent) { 520 | special.attachEvent('click', handle_special_clicks); 521 | } 522 | })(this.document); 523 | ``` 524 | 525 | 18. What does this snippet of code do? 526 | 527 | ```js 528 | var foo = bar ? bar : 0; 529 | ``` 530 | 531 | **Answer:** 532 | 533 | This is called a *ternary* and is shorthand for a quick if/else statement. That could be written out more verbosely as… 534 | 535 | ```js 536 | var foo; // undefined 537 | 538 | if (bar) { 539 | foo = bar; 540 | } 541 | else { 542 | foo = 0; 543 | } 544 | ``` 545 | 546 | 19. When might you write something like this, and what is it shorthand for? 547 | 548 | ```js 549 | foo && foo.bar(); 550 | ``` 551 | 552 | **Answer:** 553 | 554 | This is sometimes referred to as "short circuiting" a logical operator. You can think of it as a ternary with no `else` condition. Basically, the part before `&&` is evaluated, and if it is "truthy" (in this case, ensuring the existence of `foo`), the part after the `&&` is evaluated. Since `foo.bar()` is a function, it is executed. 555 | 556 | This could be written out more verbosely as… 557 | 558 | ```js 559 | if (foo) { 560 | foo.bar(); 561 | } 562 | ``` 563 | 564 | This is helpful when logging things to the `console`, which may not be present and available in every browser. For testing purposes, it can be useful to have a `log` function, which proxies for `console.log`… 565 | 566 | ```js 567 | // This allows you to log things, if console 568 | // is available, but takes no action, if not. 569 | function log() { 570 | console && console.log(arguments); 571 | } 572 | 573 | // Use like this 574 | log(1, 2, 3, 4, 5); 575 | ``` 576 | 577 | 20. How do `parseInt` and `parseFloat` differ? When would you use a number's `toFixed()` method? In what instance might the following code snippet actually make sense to use? 578 | 579 | ```js 580 | var my_number = my_string - 0; 581 | ``` 582 | 583 | **Answer:** 584 | 585 | The `parseInt` function will take a number or string, and convert it into a whole number. This is not the same as `Math.round()`, because it simply removes everything after a decimal point. It will also remove any string values that follow a number. 586 | 587 | The `parseFloat` function will convert any number or string into a decimal representation, also culling any string values that follow. Also, `parseInt` requires a "radix" (usually `10`) to be passed in as the second parameter. Otherwise, values such as `09` change to `0` instead of `9`. 588 | 589 | Using `parseFloat` alone will not retain (nor add) a certain number decimal points. For instance, `'1.50'` (a string representing one dollar and fifty cents) would simply become `1.5`. To ensure a string representation of the correct number of decimal points, `toFixed()` must be used. 590 | 591 | ```js 592 | var radix = 10; 593 | 594 | // Equals 1 595 | parseInt('1.5', radix); 596 | 597 | // Equals 1 598 | parseInt('1.5 Hello World!', radix); 599 | 600 | // Equals NaN (not a number) 601 | parseInt('Hello World! 1.5', radix); 602 | 603 | // Equals 1.5 604 | parseFloat('1.50'); 605 | 606 | // Equals '1.50' 607 | parseFloat('1.5000').toFixed(2); 608 | ``` 609 | 610 | Occasionally, you may want to check to ensure that a user really has typed in a real number, and not one that consists of numbers *and* non-numeric characters. You could write a regular expression, but a simple way to check is to subtract zero. If this yields `NaN`, you know the user didn't enter what can be considered a "real" number. 611 | 612 | ```js 613 | // Assuming this is what a user typed... 614 | // dollars 615 | 616 | (function(d) { 617 | var value = d.getElementById('amount').value - 0; 618 | 619 | // Is it not a number? 620 | if (isNaN(value)) { 621 | // Tell the user: 622 | // "You must enter a value number" 623 | } 624 | else { 625 | // Process the value 626 | } 627 | })(this.document); 628 | ``` 629 | 630 | 21. Write a function named `sum` that returns the total of any number of parameters. Example… 631 | 632 | ```js 633 | // Should equal 15 634 | sum(1, 2, 3, 4, 5); 635 | 636 | // Should equal 0 637 | sum(5, null, -5); 638 | 639 | // Should equal 10 640 | sum('1.0', false, 1, true, 1, 'A', 1, 'B', 1, 'C', 1, 'D', 1, 'E', 1, 'F', 1, 'G', 1); 641 | 642 | // Should equal 0.3, not 0.30000000000000004 643 | sum(0.1, 0.2); 644 | ``` 645 | 646 | **Answer:** 647 | 648 | To handle an arbitrary number of parameters passed into a function, JavaScript gives us the handy, built-in variable called `arguments`. Here, we can use `parseFloat`, to ensure we filter out any non-numeric values that might be passed in, while still allowing string representations of numbers to be converted. Also, decimal values are multiplied by `1e12`, totaled, then divided by `1e12`, to account for any oddities due to floating-point arithmetic. 649 | 650 | ```js 651 | function sum() { 652 | 'use strict'; 653 | 654 | // Start from zero 655 | var integer_total = 0; // Integer total 656 | var decimal_total = 0; // Decimal total 657 | 658 | // Floating-points, bah! 659 | // 1e12 = 1000000000000. 660 | var factor = 1e12; 661 | 662 | // Undefined, set in the loop 663 | var value; 664 | var value_split; 665 | var integer; 666 | var decimal; 667 | 668 | // Something to iterate on 669 | var i = arguments.length; 670 | 671 | // Loop through all the parameters 672 | while (i--) { 673 | // Ensure real number 674 | value = parseFloat(arguments[i]); 675 | 676 | // Is it not, not a number? 677 | // Then hey, it's a number! 678 | if (!isNaN(value)) { 679 | value_split = value.toString().split('.'); 680 | integer = parseFloat(value_split[0]); 681 | 682 | // Multiply by 1e12, to account for peculiarities 683 | // of doing addition with floating-point numbers. 684 | decimal = parseFloat('.' + value_split[1]) * factor || 0; 685 | 686 | integer_total += integer; 687 | decimal_total += value < 0 ? decimal * -1 : decimal; 688 | } 689 | } 690 | 691 | // Divide back by 1e12, because we multiplied by 692 | // 1e12 to account for floating-point weirdness. 693 | return integer_total + decimal_total/factor; 694 | } 695 | ``` 696 | 697 | ## BONUS Question 698 | 699 | When the following code is pasted into a browser's console, what does it output? 700 | 701 | ```js 702 | (function(window) { 703 | 'use strict'; 704 | 705 | var hello = 'Hello World'; 706 | 707 | var arr = [ 708 | '\x21', 709 | '\x6E', 710 | '\x61', 711 | '\x6D', 712 | '\x74', 713 | '\x61', 714 | '\x42' 715 | ]; 716 | 717 | var str = ''; 718 | var i = 16; 719 | 720 | while (i--) { 721 | str += 1 * hello; 722 | str += i % 2 === 0 ? '\x2C\x20' : ''; 723 | } 724 | 725 | str = str.replace(/\x4E+/g, '\x6E'); 726 | str = str.replace(/\x6E\x2C/g, '\x2C'); 727 | str = str.slice(0, 1).toUpperCase() + str.slice(1, str.length); 728 | 729 | str += arr.reverse().join(''); 730 | 731 | window.console.log(str); 732 | 733 | })(this); 734 | ``` 735 | 736 | The preceding code outputs: 737 | 738 | Nana, nana, nana, nana, nana, nana, nana, nana, Batman! 739 | 740 | The "Hello World" bit is a red herring. That simply provides a string that, when multiplied by `1` returns `NaN` (not a number). We then manipulate our string of `NaN`, and concatenate it with the result of a reversed array `!namtaB` that becomes `Batman!`. 741 | 742 | The point of this question is just to put a smile on the face of anyone who ventures to try it out. We wouldn't expect anyone to actually be fluent in hexadecimal representations of letters and common characters. 743 | --------------------------------------------------------------------------------