├── .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("
  • Loading...
  • "); 744 | 745 | jQuery.get("test.html", function(html){ 746 | results.html( html ); 747 | assert( results, "The element to append to, via a closure." ); 748 | }); 749 | ``` 750 | 751 | #### They're also useful for timers 752 | 753 | ```js 754 | var count = 0; 755 | 756 | var timer = setInterval(function(){ 757 | if ( count < 5 ) { 758 | log( "Timer call: ", count ); 759 | count++; 760 | } else { 761 | assert( count == 5, "Count came via a closure, accessed each step." ); 762 | assert( timer, "The timer reference is also via a closure." ); 763 | clearInterval( timer ); 764 | } 765 | }, 100); 766 | ``` 767 | 768 | #### And they're also frequently used when attaching event listeners 769 | 770 | ```js 771 | var count = 1; 772 | var elem = document.createElement("li"); 773 | elem.innerHTML = "Click me!"; 774 | elem.onclick = function(){ 775 | log( "Click #", count++ ); 776 | }; 777 | document.getElementById("results").appendChild( elem ); 778 | assert( elem.parentNode, "Clickable element appended." ); 779 | ``` 780 | 781 | #### Private properties, using closures 782 | 783 | ```js 784 | function Ninja(){ 785 | var slices = 0; 786 | 787 | this.getSlices = function(){ 788 | return slices; 789 | }; 790 | this.slice = function(){ 791 | slices++; 792 | }; 793 | } 794 | 795 | var ninja = new Ninja(); 796 | ninja.slice(); 797 | assert( ninja.getSlices() == 1, "We're able to access the internal slice data." ); 798 | assert( ninja.slices === undefined, "And the private data is inaccessible to us." ); 799 | ``` 800 | 801 | #### QUIZ: What are the values of the variables 802 | 803 | ```js 804 | var a = 5; 805 | function runMe(a){ 806 | assert( a == ___, "Check the value of a." ); 807 | 808 | function innerRun(){ 809 | assert( b == ___, "Check the value of b." ); 810 | assert( c == ___, "Check the value of c." ); 811 | } 812 | 813 | var b = 7; 814 | innerRun(); 815 | var c = 8; 816 | } 817 | runMe(6); 818 | 819 | for ( var d = 0; d < 3; d++ ) { 820 | setTimeout(function(){ 821 | assert( d == ___, "Check the value of d." ); 822 | }, 100); 823 | } 824 | ``` 825 | 826 | #### The last one is quite tricky, we'll revisit it 827 | 828 | ```js 829 | var a = 5; 830 | function runMe(a){ 831 | assert( a == 6, "Check the value of a." ); 832 | 833 | function innerRun(){ 834 | assert( b == 7, "Check the value of b." ); 835 | assert( c == undefined, "Check the value of c." ); 836 | } 837 | 838 | var b = 7; 839 | innerRun(); 840 | var c = 8; 841 | } 842 | runMe(6); 843 | 844 | for ( var d = 0; d < 3; d++ ) { 845 | setTimeout(function(){ 846 | assert( d == 3, "Check the value of d." ); 847 | }, 100); 848 | } 849 | ``` 850 | 851 | ### Temporary Scope 852 | 853 | #### Self-executing, temporary, function 854 | 855 | ```js 856 | (function(){ 857 | var count = 0; 858 | 859 | var timer = setInterval(function(){ 860 | if ( count < 5 ) { 861 | log( "Timer call: ", count ); 862 | count++; 863 | } else { 864 | assert( count == 5, "Count came via a closure, accessed each step." ); 865 | assert( timer, "The timer reference is also via a closure." ); 866 | clearInterval( timer ); 867 | } 868 | }, 100); 869 | })(); 870 | 871 | assert( typeof count == "undefined", "count doesn't exist outside the wrapper" ); 872 | assert( typeof timer == "undefined", "neither does timer" ); 873 | ``` 874 | 875 | #### Now we can handle closures and looping 876 | 877 | ```js 878 | for ( var d = 0; d < 3; d++ ) (function(d){ 879 | setTimeout(function(){ 880 | log( "Value of d: ", d ); 881 | assert( d == d, "Check the value of d." ); 882 | }, d * 200); 883 | })(d); 884 | ``` 885 | 886 | #### The anonymous wrapper functions are also useful for wrapping libraries 887 | 888 | ```js 889 | (function(){ 890 | var myLib = window.myLib = function(){ 891 | // Initialize 892 | }; 893 | 894 | // ... 895 | })(); 896 | ``` 897 | 898 | #### Another way to wrap a library 899 | 900 | ```js 901 | var myLib = (function(){ 902 | function myLib(){ 903 | // Initialize 904 | } 905 | 906 | // ... 907 | 908 | return myLib; 909 | })(); 910 | ``` 911 | 912 | #### QUIZ: Fix the broken closures in this loop 913 | 914 | ```js 915 | var count = 0; 916 | for ( var i = 0; i < 4; i++ ) { 917 | setTimeout(function(){ 918 | assert( i == count++, "Check the value of i." ); 919 | }, i * 200); 920 | } 921 | ``` 922 | 923 | #### A quick wrapper function will do the trick 924 | 925 | ```js 926 | var count = 0; 927 | for ( var i = 0; i < 4; i++ ) (function(i){ 928 | setTimeout(function(){ 929 | assert( i == count++, "Check the value of i." ); 930 | }, i * 200); 931 | })(i); 932 | ``` 933 | 934 | ### Function Prototypes 935 | 936 | #### Adding a prototyped method to a function 937 | 938 | ```js 939 | function Ninja(){} 940 | 941 | Ninja.prototype.swingSword = function(){ 942 | return true; 943 | }; 944 | 945 | var ninjaA = Ninja(); 946 | assert( !ninjaA, "Is undefined, not an instance of Ninja." ); 947 | 948 | var ninjaB = new Ninja(); 949 | assert( ninjaB.swingSword(), "Method exists and is callable." ); 950 | ``` 951 | 952 | #### Properties added in the constructor (or later) override prototyped properties 953 | 954 | ```js 955 | function Ninja(){ 956 | this.swingSword = function(){ 957 | return true; 958 | }; 959 | } 960 | 961 | // Should return false, but will be overridden 962 | Ninja.prototype.swingSword = function(){ 963 | return false; 964 | }; 965 | 966 | var ninja = new Ninja(); 967 | assert( ninja.swingSword(), "Calling the instance method, not the prototype method." ); 968 | ``` 969 | 970 | #### Prototyped properties affect all objects of the same constructor, simultaneously, even if they already exist 971 | 972 | ```js 973 | function Ninja(){ 974 | this.swung = true; 975 | } 976 | 977 | var ninjaA = new Ninja(); 978 | var ninjaB = new Ninja(); 979 | 980 | Ninja.prototype.swingSword = function(){ 981 | return this.swung; 982 | }; 983 | 984 | assert( ninjaA.swingSword(), "Method exists, even out of order." ); 985 | assert( ninjaB.swingSword(), "and on all instantiated objects." ); 986 | ``` 987 | 988 | #### QUIZ: Make a chainable Ninja method 989 | 990 | ```js 991 | function Ninja(){ 992 | this.swung = true; 993 | } 994 | 995 | var ninjaA = new Ninja(); 996 | var ninjaB = new Ninja(); 997 | 998 | // Add a method to the Ninja prototype which 999 | // returns itself and modifies swung 1000 | 1001 | assert( !ninjaA.swing().swung, "Verify that the swing method exists and returns an instance." ); 1002 | assert( !ninjaB.swing().swung, "and that it works on all Ninja instances." ); 1003 | ``` 1004 | 1005 | #### The chainable method must return this 1006 | 1007 | ```js 1008 | function Ninja(){ 1009 | this.swung = true; 1010 | } 1011 | 1012 | var ninjaA = new Ninja(); 1013 | var ninjaB = new Ninja(); 1014 | 1015 | Ninja.prototype.swing = function(){ 1016 | this.swung = false; 1017 | return this; 1018 | }; 1019 | 1020 | assert( !ninjaA.swing().swung, "Verify that the swing method exists and returns an instance." ); 1021 | assert( !ninjaB.swing().swung, "and that it works on all Ninja instances." ); 1022 | ``` 1023 | 1024 | ### Instance Type 1025 | 1026 | #### Examining the basics of an object 1027 | 1028 | ```js 1029 | function Ninja(){} 1030 | 1031 | var ninja = new Ninja(); 1032 | 1033 | assert( typeof ninja == "object", "However the type of the instance is still an object." ); 1034 | assert( ninja instanceof Ninja, "The object was instantiated properly." ); 1035 | assert( ninja.constructor == Ninja, "The ninja object was created by the Ninja function." ); 1036 | ``` 1037 | 1038 | #### We can still use the constructor to build other instances 1039 | 1040 | ```js 1041 | function Ninja(){} 1042 | var ninja = new Ninja(); 1043 | var ninjaB = new ninja.constructor(); 1044 | 1045 | assert( ninjaB instanceof Ninja, "Still a ninja object." ); 1046 | ``` 1047 | 1048 | #### QUIZ: Make another instance of a Ninja 1049 | 1050 | ```js 1051 | var ninja = (function(){ 1052 | function Ninja(){} 1053 | return new Ninja(); 1054 | })(); 1055 | 1056 | // Make another instance of Ninja 1057 | var ninjaB = ___; 1058 | 1059 | assert( ninja.constructor == ninjaB.constructor, "The ninjas come from the same source." ); 1060 | ``` 1061 | 1062 | #### Use the .constructor property to dig in 1063 | 1064 | ```js 1065 | var ninja = (function(){ 1066 | function Ninja(){} 1067 | return new Ninja(); 1068 | })(); 1069 | 1070 | // Make another instance of Ninja 1071 | var ninjaB = new ninja.constructor(); 1072 | 1073 | assert( ninja.constructor == ninjaB.constructor, "The ninjas come from the same source." ); 1074 | ``` 1075 | 1076 | ### Inheritance 1077 | 1078 | #### The basics of how prototypal inheritance works 1079 | 1080 | ```js 1081 | function Person(){} 1082 | Person.prototype.dance = function(){}; 1083 | 1084 | function Ninja(){} 1085 | 1086 | // Achieve similar, but non-inheritable, results 1087 | Ninja.prototype = Person.prototype; 1088 | Ninja.prototype = { dance: Person.prototype.dance }; 1089 | 1090 | assert( (new Ninja()) instanceof Person, "Will fail with bad prototype chain." ); 1091 | 1092 | // Only this maintains the prototype chain 1093 | Ninja.prototype = new Person(); 1094 | 1095 | var ninja = new Ninja(); 1096 | assert( ninja instanceof Ninja, "ninja receives functionality from the Ninja prototype" ); 1097 | assert( ninja instanceof Person, "... and the Person prototype" ); 1098 | assert( ninja instanceof Object, "... and the Object prototype" ); 1099 | ``` 1100 | 1101 | #### QUIZ: Let's try our hand at inheritance 1102 | 1103 | ```js 1104 | function Person(){} 1105 | Person.prototype.getName = function(){ 1106 | return this.name; 1107 | }; 1108 | 1109 | // Implement a function that inherits from Person 1110 | // and sets a name in the constructor 1111 | 1112 | var me = new Me(); 1113 | assert( me.getName(), "A name was set." ); 1114 | ``` 1115 | 1116 | #### The result is rather straight-forward 1117 | 1118 | ```js 1119 | function Person(){} 1120 | Person.prototype.getName = function(){ 1121 | return this.name; 1122 | }; 1123 | 1124 | function Me(){ 1125 | this.name = "John Resig"; 1126 | } 1127 | Me.prototype = new Person(); 1128 | 1129 | var me = new Me(); 1130 | assert( me.getName(), "A name was set." ); 1131 | ``` 1132 | 1133 | ### Built-in Prototypes 1134 | 1135 | #### We can also modify built-in object prototypes 1136 | 1137 | ```js 1138 | if (!Array.prototype.forEach) { 1139 | Array.prototype.forEach = function(fn){ 1140 | for ( var i = 0; i < this.length; i++ ) { 1141 | fn( this[i], i, this ); 1142 | } 1143 | }; 1144 | } 1145 | 1146 | ["a", "b", "c"].forEach(function(value, index, array){ 1147 | assert( value, "Is in position " + index + " out of " + (array.length - 1) ); 1148 | }); 1149 | ``` 1150 | 1151 | #### Beware: Extending prototypes can be dangerous 1152 | 1153 | ```js 1154 | Object.prototype.keys = function(){ 1155 | var keys = []; 1156 | for ( var i in this ) 1157 | keys.push( i ); 1158 | return keys; 1159 | }; 1160 | 1161 | var obj = { a: 1, b: 2, c: 3 }; 1162 | 1163 | assert( obj.keys().length == 3, "We should only have 3 properties." ); 1164 | 1165 | delete Object.prototype.keys; 1166 | ``` 1167 | 1168 | ### Enforcing Function Context 1169 | 1170 | #### What happens when we try to bind an object's method to a click handler 1171 | 1172 | ```js 1173 | var Button = { 1174 | click: function(){ 1175 | this.clicked = true; 1176 | } 1177 | }; 1178 | 1179 | var elem = document.createElement("li"); 1180 | elem.innerHTML = "Click me!"; 1181 | elem.onclick = Button.click; 1182 | document.getElementById("results").appendChild(elem); 1183 | 1184 | elem.onclick(); 1185 | assert( elem.clicked, "The clicked property was accidentally set on the element" ); 1186 | ``` 1187 | 1188 | #### We need to keep its context as the original object 1189 | 1190 | ```js 1191 | function bind(context, name){ 1192 | return function(){ 1193 | return context[name].apply(context, arguments); 1194 | }; 1195 | } 1196 | 1197 | var Button = { 1198 | click: function(){ 1199 | this.clicked = true; 1200 | } 1201 | }; 1202 | 1203 | var elem = document.createElement("li"); 1204 | elem.innerHTML = "Click me!"; 1205 | elem.onclick = bind(Button, "click"); 1206 | document.getElementById("results").appendChild(elem); 1207 | 1208 | elem.onclick(); 1209 | assert( Button.clicked, "The clicked property was correctly set on the object" ); 1210 | ``` 1211 | 1212 | #### Add a method to all functions to allow context enforcement 1213 | 1214 | ```js 1215 | Function.prototype.bind = function(object){ 1216 | var fn = this; 1217 | return function(){ 1218 | return fn.apply(object, arguments); 1219 | }; 1220 | }; 1221 | 1222 | var Button = { 1223 | click: function(){ 1224 | this.clicked = true; 1225 | } 1226 | }; 1227 | 1228 | var elem = document.createElement("li"); 1229 | elem.innerHTML = "Click me!"; 1230 | elem.onclick = Button.click.bind(Button); 1231 | document.getElementById("results").appendChild(elem); 1232 | 1233 | elem.onclick(); 1234 | assert( Button.clicked, "The clicked property was correctly set on the object" ); 1235 | ``` 1236 | 1237 | #### Our final target (the .bind method from Prototype.js) 1238 | 1239 | 1240 | 1241 | ```js 1242 | Function.prototype.bind = function(){ 1243 | var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 1244 | return function(){ 1245 | return fn.apply(object, 1246 | args.concat(Array.prototype.slice.call(arguments))); 1247 | }; 1248 | }; 1249 | 1250 | var Button = { 1251 | click: function(value){ 1252 | this.clicked = value; 1253 | } 1254 | }; 1255 | 1256 | var elem = document.createElement("li"); 1257 | elem.innerHTML = "Click me!"; 1258 | elem.onclick = Button.click.bind(Button, false); 1259 | document.getElementById("results").appendChild(elem); 1260 | 1261 | elem.onclick(); 1262 | assert( Button.clicked === false, "The clicked property was correctly set on the object" ); 1263 | ``` 1264 | 1265 | ### Bonus: Function Length 1266 | 1267 | #### How does a function's length property work 1268 | 1269 | ```js 1270 | function makeNinja(name){} 1271 | function makeSamurai(name, rank){} 1272 | assert( makeNinja.length == 1, "Only expecting a single argument" ); 1273 | assert( makeSamurai.length == 2, "Multiple arguments expected" ); 1274 | ``` 1275 | 1276 | #### We can use it to implement method overloading 1277 | 1278 | ```js 1279 | function addMethod(object, name, fn){ 1280 | // Save a reference to the old method 1281 | var old = object[ name ]; 1282 | 1283 | // Overwrite the method with our new one 1284 | object[ name ] = function(){ 1285 | // Check the number of incoming arguments, 1286 | // compared to our overloaded function 1287 | if ( fn.length == arguments.length ) 1288 | // If there was a match, run the function 1289 | return fn.apply( this, arguments ); 1290 | 1291 | // Otherwise, fallback to the old method 1292 | else if ( typeof old === "function" ) 1293 | return old.apply( this, arguments ); 1294 | }; 1295 | } 1296 | ``` 1297 | 1298 | #### How method overloading might work, using the function length property 1299 | 1300 | ```js 1301 | function addMethod(object, name, fn){ 1302 | // Save a reference to the old method 1303 | var old = object[ name ]; 1304 | 1305 | // Overwrite the method with our new one 1306 | object[ name ] = function(){ 1307 | // Check the number of incoming arguments, 1308 | // compared to our overloaded function 1309 | if ( fn.length == arguments.length ) 1310 | // If there was a match, run the function 1311 | return fn.apply( this, arguments ); 1312 | 1313 | // Otherwise, fallback to the old method 1314 | else if ( typeof old === "function" ) 1315 | return old.apply( this, arguments ); 1316 | }; 1317 | } 1318 | 1319 | function Ninjas(){ 1320 | var ninjas = [ "Dean Edwards", "Sam Stephenson", "Alex Russell" ]; 1321 | addMethod(this, "find", function(){ 1322 | return ninjas; 1323 | }); 1324 | addMethod(this, "find", function(name){ 1325 | var ret = []; 1326 | for ( var i = 0; i < ninjas.length; i++ ) 1327 | if ( ninjas[i].indexOf(name) == 0 ) 1328 | ret.push( ninjas[i] ); 1329 | return ret; 1330 | }); 1331 | addMethod(this, "find", function(first, last){ 1332 | var ret = []; 1333 | for ( var i = 0; i < ninjas.length; i++ ) 1334 | if ( ninjas[i] == (first + " " + last) ) 1335 | ret.push( ninjas[i] ); 1336 | return ret; 1337 | }); 1338 | } 1339 | 1340 | var ninjas = new Ninjas(); 1341 | assert( ninjas.find().length == 3, "Finds all ninjas" ); 1342 | assert( ninjas.find("Sam").length == 1, "Finds ninjas by first name" ); 1343 | assert( ninjas.find("Dean", "Edwards").length == 1, "Finds ninjas by first and last name" ); 1344 | assert( ninjas.find("Alex", "X", "Russell") == null, "Does nothing" ); 1345 | ``` 1346 | 1347 | ## Credits 1348 | 1349 | [John Resig](https://github.com/jeresig) - He made the whole thing. 1350 | 1351 | ## Change Logs 1352 | 1353 | 1354 | ## Contribution 1355 | 1356 | Contributions are highly welcome 1357 | 1358 | ## License 1359 | 1360 | ISC 1361 | -------------------------------------------------------------------------------- /helper.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk') 2 | 3 | module.exports = { 4 | assert: function assert(t, msg) { 5 | // If assertion is truthy, PASS else FAIL 6 | var res = !!t ? chalk.green('PASS') : chalk.red('FAIL') 7 | 8 | return console.log(res + ' ' + msg) 9 | }, 10 | error: function error(msg) { 11 | return console.log(chalk.red('ERROR') + ' ' + msg) 12 | }, 13 | log: function log() { 14 | return console.log(Array.prototype.slice.call(arguments).join(' ')) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advanced-javascript", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.0", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", 10 | "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", 11 | "dev": true, 12 | "requires": { 13 | "color-convert": "1.9.1" 14 | } 15 | }, 16 | "chalk": { 17 | "version": "2.3.0", 18 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", 19 | "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", 20 | "dev": true, 21 | "requires": { 22 | "ansi-styles": "3.2.0", 23 | "escape-string-regexp": "1.0.5", 24 | "supports-color": "4.5.0" 25 | } 26 | }, 27 | "color-convert": { 28 | "version": "1.9.1", 29 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 30 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 31 | "dev": true, 32 | "requires": { 33 | "color-name": "1.1.3" 34 | } 35 | }, 36 | "color-name": { 37 | "version": "1.1.3", 38 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 39 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 40 | "dev": true 41 | }, 42 | "escape-string-regexp": { 43 | "version": "1.0.5", 44 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 45 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 46 | "dev": true 47 | }, 48 | "has-flag": { 49 | "version": "2.0.0", 50 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 51 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 52 | "dev": true 53 | }, 54 | "supports-color": { 55 | "version": "4.5.0", 56 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", 57 | "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", 58 | "dev": true, 59 | "requires": { 60 | "has-flag": "2.0.0" 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advanced-javascript", 3 | "version": "1.0.0", 4 | "description": "Advanced Javascript Documentation", 5 | "main": "start.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ooade/Advanced-JavaScript.git" 12 | }, 13 | "license": "ISC", 14 | "bugs": { 15 | "url": "https://github.com/ooade/Advanced-JavaScript/issues" 16 | }, 17 | "homepage": "https://github.com/ooade/Advanced-JavaScript#readme", 18 | "devDependencies": { 19 | "chalk": "^2.3.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /start.js: -------------------------------------------------------------------------------- 1 | var assert = require('./helper').assert 2 | var error = require('./helper').error 3 | var log = require('./helper').log 4 | 5 | // write your code below this line 6 | --------------------------------------------------------------------------------