├── .gitignore ├── README.md ├── helper.js ├── package-lock.json ├── package.json └── start.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced-JavaScript 2 | 3 | This documentation is based on John Resig's website on Advanced JavaScript. If these concepts seem complex to you, i recommend [these resources](https://github.com/micromata/awesome-javascript-learning) and also strongly recommend [FreeCodeCamp - Learn to code and help nonprofits](https://www.freecodecamp.org). If you feel you know most of these concepts, you could just try out the [quizzes](#quizzes) and you're good to go. 4 | 5 | 6 | ## Contents 7 | 8 | - [Goal](#goal) 9 | - [Helper Methods](#helper-methods) 10 | - [Running a Quiz](#running-a-quiz) 11 | - [Lessons](#lessons) 12 | - [Defining Functions](#defining-functions) 13 | - [Order of function definition](#order-of-function-definition) 14 | - [Where can assignment be accessed](#where-can-assignments-be-accessed) 15 | - [Can functions be defined below return statements](#can-functions-be-defined-below-return-statements) 16 | - [Named Functions](#named-functions) 17 | - [What is the name of a function](#what-is-the-name-of-a-function) 18 | - [With anonymous function that's an object property](#with-anonymous-function-thats-an-object-property) 19 | - [What happens when we remove the original object](#what-happens-when-we-remove-the-original-object) 20 | - [Let's give the anonymous function a name](#lets-give-the-anonymous-function-a-name) 21 | - [Functions as Objects](#functions-as-objects) 22 | - [How similar are functions and objects](#how-similar-are-functions-and-objects) 23 | - [Is it possible to cache the return results from a function](#is-it-possible-to-cache-the-return-results-from-a-function) 24 | - [One possible way to cache the results](#one-possible-way-to-cache-the-results) 25 | - [Context](#context) 26 | - [What happens if a function is an object property](#what-happens-if-a-function-is-an-object-property) 27 | - [What exactly does context represent](#what-exactly-does-context-represent) 28 | - [How can we change the context of a function](#how-can-we-change-the-context-of-a-function) 29 | - [Different ways of changing the context](#different-ways-of-changing-the-context) 30 | - [How can we implement looping with a callback](#how-can-we-implement-looping-with-a-callback) 31 | - [A possible solution for function looping](#a-possible-solution-for-function-looping) 32 | - [Instantiation](#instantiation) 33 | - [What does the new operator do](#what-does-the-new-operator-do) 34 | - [We have a 'this' context that is a Ninja object](#we-have-a-this-context-that-is-a-ninja-object) 35 | - [Add a new property and method to the object](#add-a-new-property-and-method-to-the-object) 36 | - [What happens when we forget to use the new operator](#what-happens-when-we-forget-to-use-the-new-operator) 37 | - [Cont: What happens when we forget to use the new operator](#cont-what-happens-when-we-forget-to-use-the-new-operator) 38 | - [We need to make sure that the new operator is always used](#we-need-to-make-sure-that-the-new-operator-is-always-used) 39 | - [A solution using arguments.callee](#a-solution-using-arguments-callee) 40 | - [Flexible Arguments](#flexible-arguments) 41 | - [Using a variable number of arguments to our advantage](#using-a-variable-number-of-arguments-to-our-advantage) 42 | - [How can we find the Min/Max number in an array](#how-can-we-find-the-min-max-number-in-an-array) 43 | - [Another possible solution](#another-possible-solution) 44 | - [Uh oh, what's going wrong here](#uh-oh-whats-going-wrong-here) 45 | - [We can use built-in methods to our advantage](#we-can-use-built-in-methods-to-our-advantage) 46 | - [We can use call and apply to build a solution](#we-can-use-call-and-apply-to-build-a-solution) 47 | - [Closures](#closures) 48 | - [But why doesn't this work](#but-why-doesnt-this-work) 49 | - [Closures are frequently used for callbacks](#closures-are-frequently-used-for-callbacks) 50 | - [They're also useful for timers](#theyre-also-useful-for-timers) 51 | - [And they're also frequently used when attaching event listeners](#and-theyre-also-frequently-used-when-attaching-event-listeners) 52 | - [Private properties, using closures](#private-properties-using-closures) 53 | - [The last one is quite tricky, we'll revisit it](#the-last-one-is-quite-tricky-well-revisit-it) 54 | - [Temporary Scope](#temporary-scope) 55 | - [Self-executing, temporary, function](#self-executing-temporary-function) 56 | - [Now we can handle closures and looping](#now-we-can-handle-closures-and-looping) 57 | - [The anonymous wrapper functions are also useful for wrapping libraries](#the-anonymous-wrapper-functions-are-also-useful-for-wrapping-libraries) 58 | - [Another way to wrap a library](#another-way-to-wrap-a-library) 59 | - [A quick wrapper function will do the trick](#a-quick-wrapper-function-will-do-the-trick) 60 | - [Function Prototypes](#function-prototypes) 61 | - [Adding a prototyped method to a function](#adding-a-prototyped-method-to-a-function) 62 | - [Properties added in the constructor (or later) override prototyped properties](#properties-added-in-the-constructor-or-later-override-prototyped-properties) 63 | - [Prototyped properties affect all objects of the same constructor, simultaneously, even if they already exist](#prototyped-properties-affect-all-objects-of-the-same-constructor-simultaneously-even-if-they-already-exist) 64 | - [The chainable method must return this](#the-chainable-method-must-return-this) 65 | - [Instance Type](#instance-type) 66 | - [Examining the basics of an object](#examining-the-basics-of-an-object) 67 | - [We can still use the constructor to build other instances](#we-can-still-use-the-constructor-to-build-other-instances) 68 | - [Use the .constructor property to dig in](#use-the-constructor-property-to-dig-in) 69 | - [Inheritance](#inheritance) 70 | - [The basics of how prototypal inheritance works](#the-basics-of-how-prototypal-inheritance-works) 71 | - [The result is rather straight-forward](#the-result-is-rather-straight-forward) 72 | - [Built-in Prototypes](#built-in-prototypes) 73 | - [We can also modify built-in object prototypes](#we-can-also-modify-built-in-object-prototypes) 74 | - [Beware: Extending prototypes can be dangerous](#beware-extending-prototypes-can-be-dangerous) 75 | - [Enforcing Function Context](#enforcing-function-context) 76 | - [What happens when we try to bind an object's method to a click handler](#what-happens-when-we-try-to-bind-an-objects-method-to-a-click-handler) 77 | - [We need to keep its context as the original object](#we-need-to-keep-its-context-as-the-original-object) 78 | - [Add a method to all functions to allow context enforcement](#add-a-method-to-all-functions-to-allow-context-enforcement) 79 | - [Our final target (the .bind method from Prototype.js)](#our-final-target-the-bind-method-from-prototype-js) 80 | - [Bonus: Function Length](#bonus-function-length) 81 | - [How does a function's length property work](#how-does-a-functions-length-property-work) 82 | - [We can use it to implement method overloading](#we-can-use-it-to-implement-method-overloading) 83 | - [How method overloading might work, using the function length property](#how-method-overloading-might-work-using-the-function-length-property) 84 | - [Quizzes](#quizzes) 85 | - [QUIZ: Can you cache the results of this function](#quiz-can-you-cache-the-results-of-this-function) 86 | - [QUIZ: Add a method that gives a name to the ninja](#quiz-add-a-method-that-gives-a-name-to-the-ninja) 87 | - [QUIZ: Is there another, more generic, way of doing this](#quiz-is-there-another-more-generic-way-of-doing-this) 88 | - [QUIZ: We must convert array-like objects into actual arrays. Can any built-in methods help](#quiz-we-must-convert-array-like-objects-into-actual-arrays-can-any-built-in-methods-help) 89 | - [QUIZ: Implement a multiplication function (first argument by largest number)](#quiz-implement-a-multiplication-function-first-argument-by-largest-number-) 90 | - [QUIZ: What are the values of the variables](#quiz-what-are-the-values-of-the-variables) 91 | - [QUIZ: Fix the broken closures in this loop](#quiz-fix-the-broken-closures-in-this-loop) 92 | - [QUIZ: Make a chainable Ninja method](#quiz-make-a-chainable-ninja-method) 93 | - [QUIZ: Make another instance of a Ninja](#quiz-make-another-instance-of-a-ninja) 94 | - [QUIZ: Let's try our hand at inheritance](#quiz-lets-try-our-hand-at-inheritance) 95 | - [Credits](#credits) 96 | - [Change Logs](#change-logs) 97 | 98 | ### Goal 99 | 100 | To be able to understand this function: 101 | 102 | ```js 103 | // The .bind method from Prototype.js 104 | Function.prototype.bind = function(){ 105 | var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 106 | return function(){ 107 | return fn.apply(object, 108 | args.concat(Array.prototype.slice.call(arguments))); 109 | }; 110 | }; 111 | ``` 112 | 113 | ### Helper Methods 114 | 115 | ```js 116 | assert( true, "I'll pass." ); //passes 117 | assert( "truey", "So will I." ); //passes 118 | assert( false, "I'll fail." ); //fails 119 | assert( null, "So will I." ); //fails 120 | log( "Just a simple log", "of", "values.", true ); 121 | error( "I'm an error!" ); 122 | ``` 123 | 124 | ### Running a Quiz 125 | 126 | It's not quite hard. 127 | 128 | 1. Fork the Repo. 129 | 2. Clone the Repo to your machine. 130 | 3. `npm install`. 131 | 4. `node start.js` 132 | 133 | ## Lessons 134 | 135 | ### Defining Functions 136 | 137 | Functions can be defined like these: 138 | 139 | ```js 140 | function isNimble(){ return true; } 141 | var canFly = function(){ return true; }; 142 | window.isDeadly = function(){ return true; }; 143 | log(isNimble, canFly, isDeadly); 144 | ``` 145 | 146 | #### Order of function definition 147 | 148 | Order doesn't matter for [functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function) like `isNimble`. 149 | 150 | ```js 151 | var canFly = function(){ return true; }; 152 | window.isDeadly = function(){ return true; }; 153 | assert( isNimble() && canFly() && isDeadly(), "Still works, even though isNimble is moved." ); 154 | function isNimble(){ return true; } 155 | ``` 156 | 157 | #### Where can assignments be accessed 158 | 159 | The Order does matter for `canFly` and `isDeadly`. 160 | 161 | ```js 162 | assert( typeof canFly == "undefined", "canFly doesn't get that benefit." ); 163 | assert( typeof isDeadly == "undefined", "Nor does isDeadly." ); 164 | var canFly = function(){ return true; }; 165 | window.isDeadly = function(){ return true; }; 166 | ``` 167 | 168 | #### Can functions be defined below return statements 169 | 170 | Yes, can be defined but can be called below return statements. 171 | 172 | ```js 173 | function stealthCheck(){ 174 | assert( stealth(), "We'll never get below the return, but that's OK!" ); 175 | 176 | return stealth(); 177 | 178 | function stealth(){ return true; } 179 | } 180 | 181 | stealthCheck(); 182 | ``` 183 | 184 | ### Named Functions 185 | 186 | We can refer to a function, within itself, by its name. 187 | 188 | ```js 189 | function yell(n){ 190 | return n > 0 ? yell(n-1) + "a" : "hiy"; 191 | } 192 | assert( yell(4) == "hiyaaaa", "Calling the function by itself comes naturally." ); 193 | ``` 194 | 195 | #### What is the name of a function 196 | 197 | ```js 198 | var ninja = function myNinja(){ 199 | assert( ninja == myNinja, "This function is named two things - at once!" ); 200 | }; 201 | ninja(); 202 | assert( typeof myNinja == "undefined", "But myNinja isn't defined outside of the function." ); // myNinja exist only within the function scope 203 | log( ninja ); 204 | ``` 205 | 206 | #### With anonymous function that's an object property 207 | 208 | Yeah, we could also have "hiyaaaa" displayed, with an anonymous function that's an Object Property. 209 | 210 | ```js 211 | var ninja = { 212 | yell: function(n){ 213 | return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 214 | } 215 | }; 216 | assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 217 | ``` 218 | 219 | #### What happens when we remove the original object 220 | 221 | ```js 222 | var ninja = { 223 | yell: function(n){ 224 | return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 225 | } 226 | }; 227 | assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 228 | 229 | var samurai = { yell: ninja.yell }; 230 | var ninja = null; 231 | 232 | try { 233 | samurai.yell(4); 234 | } catch(e){ 235 | assert( false, "Uh, this isn't good! Where'd ninja.yell go" ); 236 | } 237 | ``` 238 | 239 | #### Let's give the anonymous function a name 240 | 241 | ```js 242 | var ninja = { 243 | yell: function yell(n){ 244 | return n > 0 ? yell(n-1) + "a" : "hiy"; 245 | } 246 | }; 247 | assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 248 | 249 | var samurai = { yell: ninja.yell }; 250 | var ninja = null; 251 | assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." ); 252 | ``` 253 | 254 | #### What if we don't want to give the function a name 255 | 256 | `arguments.callee` does the trick here :) 257 | 258 | ```js 259 | var ninja = { 260 | yell: function(n){ 261 | return n > 0 ? arguments.callee(n-1) + "a" : "hiy"; 262 | } 263 | }; 264 | assert( ninja.yell(4) == "hiyaaaa", "arguments.callee is the function itself." ); 265 | ``` 266 | 267 | ### Functions as Objects 268 | 269 | #### How similar are functions and objects 270 | 271 | ```js 272 | var obj = {}; 273 | var fn = function(){}; 274 | assert( obj && fn, "Both the object and function exist." ); 275 | ``` 276 | 277 | More similarity: 278 | 279 | ```js 280 | var obj = {}; 281 | var fn = function(){}; 282 | obj.prop = "some value"; 283 | fn.prop = "some value"; 284 | assert( obj.prop == fn.prop, "Both are objects, both have the property." ); 285 | ``` 286 | 287 | #### Is it possible to cache the return results from a function 288 | 289 | Yes! It is. 290 | 291 | ```js 292 | function getElements( name ) { 293 | var results; 294 | 295 | if ( getElements.cache[name] ) { 296 | results = getElements.cache[name]; 297 | } else { 298 | results = document.getElementsByTagName(name); 299 | getElements.cache[name] = results; 300 | } 301 | 302 | return results; 303 | } 304 | getElements.cache = {}; 305 | 306 | log( "Elements found: ", getElements("pre").length ); 307 | log( "Cache found: ", getElements.cache.pre.length ); 308 | ``` 309 | 310 | #### QUIZ: Can you cache the results of this function 311 | 312 | ```js 313 | function isPrime( num ) { 314 | var prime = num != 1; // Everything but 1 can be prime 315 | for ( var i = 2; i < num; i++ ) { 316 | if ( num % i == 0 ) { 317 | prime = false; 318 | break; 319 | } 320 | } 321 | return prime; 322 | } 323 | 324 | assert( isPrime(5), "Make sure the function works, 5 is prime." ); 325 | assert( isPrime.cache[5], "Is the answer cached" ); 326 | ``` 327 | 328 | #### One possible way to cache the results 329 | 330 | ```js 331 | function isPrime( num ) { 332 | if ( isPrime.cache[ num ] != null ) 333 | return isPrime.cache[ num ]; 334 | 335 | var prime = num != 1; // Everything but 1 can be prime 336 | for ( var i = 2; i < num; i++ ) { 337 | if ( num % i == 0 ) { 338 | prime = false; 339 | break; 340 | } 341 | } 342 | 343 | isPrime.cache[ num ] = prime 344 | 345 | return prime; 346 | } 347 | 348 | isPrime.cache = {}; 349 | 350 | assert( isPrime(5), "Make sure the function works, 5 is prime." ); 351 | assert( isPrime.cache[5], "Make sure the answer is cached." ); 352 | ``` 353 | 354 | ### Context 355 | 356 | #### What happens if a function is an object property 357 | 358 | ```js 359 | var katana = { 360 | isSharp: true, 361 | use: function(){ 362 | this.isSharp = !this.isSharp; 363 | } 364 | }; 365 | katana.use(); 366 | assert( !katana.isSharp, "Verify the value of isSharp has been changed." ); 367 | ``` 368 | 369 | #### What exactly does context represent 370 | 371 | ```js 372 | function katana(){ 373 | this.isSharp = true; 374 | } 375 | katana(); 376 | assert( isSharp === true, "A global object now exists with that name and value." ); 377 | 378 | var shuriken = { 379 | toss: function(){ 380 | this.isSharp = true; 381 | } 382 | }; 383 | shuriken.toss(); 384 | assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." ); 385 | ``` 386 | 387 | #### How can we change the context of a function 388 | 389 | ```js 390 | var object = {}; 391 | function fn(){ 392 | return this; 393 | } 394 | assert( fn() == this, "The context is the global object." ); 395 | assert( fn.call(object) == object, "The context is changed to a specific object." ); 396 | ``` 397 | 398 | #### Different ways of changing the context 399 | 400 | ```js 401 | function add(a, b){ 402 | return a + b; 403 | } 404 | assert( add.call(this, 1, 2) == 3, ".call() takes individual arguments" ); 405 | assert( add.apply(this, [1, 2]) == 3, ".apply() takes an array of arguments" ); 406 | ``` 407 | 408 | #### QUIZ: How can we implement looping with a callback 409 | 410 | ```js 411 | function loop(array, fn){ 412 | for ( var i = 0; i < array.length; i++ ) { 413 | // Implement me! 414 | } 415 | } 416 | var num = 0; 417 | loop([0, 1, 2], function(value){ 418 | assert(value == num++, "Make sure the contents are as we expect it."); 419 | assert(this instanceof Array, "The context should be the full array."); 420 | }); 421 | ``` 422 | 423 | 426 | 427 | #### A possible solution for function looping 428 | 429 | ```js 430 | function loop(array, fn){ 431 | for ( var i = 0; i < array.length; i++ ) 432 | fn.call( array, array[i], i ); 433 | } 434 | var num = 0; 435 | loop([0, 1, 2], function(value, i){ 436 | assert(value == num++, "Make sure the contents are as we expect it."); 437 | assert(this instanceof Array, "The context should be the full array."); 438 | }); 439 | ``` 440 | 441 | ### Instantiation 442 | 443 | #### What does the new operator do 444 | 445 | ```js 446 | function Ninja(){ 447 | this.name = "Ninja"; 448 | } 449 | 450 | var ninjaA = Ninja(); 451 | assert( !ninjaA, "Is undefined, not an instance of Ninja." ); 452 | 453 | var ninjaB = new Ninja(); 454 | assert( ninjaB.name == "Ninja", "Property exists on the ninja instance." ); 455 | ``` 456 | 457 | #### We have a 'this' context that is a Ninja object 458 | 459 | ```js 460 | function Ninja(){ 461 | this.swung = false; 462 | 463 | // Should return true 464 | this.swingSword = function(){ 465 | this.swung = !this.swung; 466 | return this.swung; 467 | }; 468 | } 469 | 470 | var ninja = new Ninja(); 471 | assert( ninja.swingSword(), "Calling the instance method." ); 472 | assert( ninja.swung, "The ninja has swung the sword." ); 473 | 474 | var ninjaB = new Ninja(); 475 | assert( !ninjaB.swung, "Make sure that the ninja has not swung his sword." ); 476 | ``` 477 | 478 | #### QUIZ: Add a method that gives a name to the ninja 479 | 480 | ```js 481 | function Ninja(name){ 482 | // Implement! 483 | } 484 | 485 | var ninja = new Ninja("John"); 486 | assert( ninja.name == "John", "The name has been set on initialization" ); 487 | 488 | ninja.changeName("Bob"); 489 | assert( ninja.name == "Bob", "The name was successfully changed." ); 490 | ``` 491 | 492 | #### Add a new property and method to the object 493 | 494 | ```js 495 | function Ninja(name){ 496 | this.changeName = function(name){ 497 | this.name = name; 498 | }; 499 | 500 | this.changeName( name ); 501 | } 502 | 503 | var ninja = new Ninja("John"); 504 | assert( ninja.name == "John", "The name has been set on initialization" ); 505 | 506 | ninja.changeName("Bob"); 507 | assert( ninja.name == "Bob", "The name was successfully changed." ); 508 | ``` 509 | 510 | #### What happens when we forget to use the new operator 511 | 512 | ```js 513 | function User(first, last){ 514 | this.name = first + " " + last; 515 | } 516 | 517 | var user = User("John", "Resig"); 518 | assert( typeof user == "undefined", "Since new wasn't used, the instance is undefined." ); 519 | ``` 520 | 521 | #### Cont: What happens when we forget to use the new operator 522 | 523 | ```js 524 | function User(first, last){ 525 | this.name = first + " " + last; 526 | } 527 | 528 | window.name = "Resig"; 529 | var user = User("John", name); 530 | 531 | assert( name == "John Resig", "The name variable is accidentally overridden." ); 532 | ``` 533 | 534 | #### We need to make sure that the new operator is always used 535 | 536 | ```js 537 | function User(first, last){ 538 | if ( !(this instanceof User) ) 539 | return new User(first, last); 540 | 541 | this.name = first + " " + last; 542 | } 543 | 544 | var name = "Resig"; 545 | var user = User("John", name); 546 | 547 | assert( user, "This was defined correctly, even if it was by mistake." ); 548 | assert( name == "Resig", "The right name was maintained." ); 549 | ``` 550 | 551 | #### QUIZ: Is there another, more generic, way of doing this 552 | 553 | ```js 554 | function User(first, last){ 555 | // Replace ___ with a value 556 | if ( !(this instanceof ___) ) 557 | return new User(first, last); 558 | 559 | this.name = first + " " + last; 560 | } 561 | 562 | var name = "Resig"; 563 | var user = User("John", name); 564 | 565 | assert( user, "This was defined correctly, even if it was by mistake." ); 566 | assert( name == "Resig", "The right name was maintained." ); 567 | ``` 568 | 569 | #### A solution using arguments.callee 570 | 571 | ```js 572 | function User(first, last){ 573 | if ( !(this instanceof arguments.callee) ) 574 | return new User(first, last); 575 | 576 | this.name = first + " " + last; 577 | } 578 | 579 | var name = "Resig"; 580 | var user = User("John", name); 581 | 582 | assert( user, "This was defined correctly, even if it was by mistake." ); 583 | assert( name == "Resig", "The right name was maintained." ); 584 | ``` 585 | 586 | ### Flexible Arguments 587 | 588 | #### Using a variable number of arguments to our advantage 589 | 590 | ```js 591 | function merge(root){ 592 | for ( var i = 1; i < arguments.length; i++ ) 593 | for ( var key in arguments[i] ) 594 | root[key] = arguments[i][key]; 595 | return root; 596 | } 597 | 598 | var merged = merge({name: "John"}, {city: "Boston"}); 599 | assert( merged.name == "John", "The original name is intact." ); 600 | assert( merged.city == "Boston", "And the city has been copied over." ); 601 | ``` 602 | 603 | #### How can we find the Min/Max number in an array 604 | 605 | ```js 606 | function smallest(array){ 607 | return Math.min.apply( Math, array ); 608 | } 609 | function largest(array){ 610 | return Math.max.apply( Math, array ); 611 | } 612 | assert(smallest([0, 1, 2, 3]) == 0, "Locate the smallest value."); 613 | assert(largest([0, 1, 2, 3]) == 3, "Locate the largest value."); 614 | ``` 615 | 616 | #### Another possible solution 617 | 618 | ```js 619 | function smallest(){ 620 | return Math.min.apply( Math, arguments ); 621 | } 622 | function largest(){ 623 | return Math.max.apply( Math, arguments ); 624 | } 625 | assert(smallest(0, 1, 2, 3) == 0, "Locate the smallest value."); 626 | assert(largest(0, 1, 2, 3) == 3, "Locate the largest value."); 627 | ``` 628 | 629 | #### Uh oh, what's going wrong here 630 | 631 | ```js 632 | function highest(){ 633 | return arguments.sort(function(a,b){ 634 | return b - a; 635 | }); 636 | } 637 | assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 638 | assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 639 | ``` 640 | 641 | #### QUIZ: We must convert array-like objects into actual arrays. Can any built-in methods help 642 | 643 | 644 | 645 | ```js 646 | // Hint: Arrays have .slice and .splice methods which return new arrays. 647 | function highest(){ 648 | return makeArray(arguments).slice(1).sort(function(a,b){ 649 | return b - a; 650 | }); 651 | } 652 | 653 | function makeArray(array){ 654 | // Implement me! 655 | } 656 | 657 | // Expecting: [3,2,1] 658 | assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 659 | // Expecting: [5,4,3,2,1] 660 | assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 661 | ``` 662 | 663 | #### We can use built-in methods to our advantage 664 | 665 | ```js 666 | function highest(){ 667 | return makeArray(arguments).sort(function(a,b){ 668 | return b - a; 669 | }); 670 | } 671 | 672 | function makeArray(array){ 673 | return Array().slice.call( array ); 674 | } 675 | 676 | assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 677 | assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 678 | ``` 679 | 680 | #### QUIZ: Implement a multiplication function (first argument by largest number) 681 | 682 | ```js 683 | function multiMax(multi){ 684 | // Make an array of all but the first argument 685 | var allButFirst = ___; 686 | 687 | // Find the largest number in that array of arguments 688 | var largestAllButFirst = ___; 689 | 690 | // Return the multiplied result 691 | return multi * largestAllButFirst; 692 | } 693 | assert( multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)" ); 694 | ``` 695 | 696 | #### We can use call and apply to build a solution 697 | 698 | ```js 699 | function multiMax(multi){ 700 | // Make an array of all but the first argument 701 | var allButFirst = Array().slice.call( arguments, 1 ); 702 | 703 | // Find the largest number in that array of arguments 704 | var largestAllButFirst = Math.max.apply( Math, allButFirst ); 705 | 706 | // Return the multiplied result 707 | return multi * largestAllButFirst; 708 | } 709 | assert( multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)" ); 710 | ``` 711 | 712 | ### Closures 713 | 714 | A basic closure: 715 | 716 | ```js 717 | var num = 10; 718 | 719 | function addNum(myNum){ 720 | return num + myNum; 721 | } 722 | 723 | assert( addNum(5) == 15, "Add two numbers together, one from a closure." ); 724 | ``` 725 | 726 | #### But why doesn't this work 727 | 728 | ```js 729 | var num = 10; 730 | 731 | function addNum(myNum){ 732 | return num + myNum; 733 | } 734 | 735 | num = 15; 736 | 737 | assert( addNum(5) == 15, "Add two numbers together, one from a closure." ); 738 | ``` 739 | 740 | #### Closures are frequently used for callbacks 741 | 742 | ```js 743 | var results = jQuery("#results").html("