├── legacy-patterns.md └── readme.md /legacy-patterns.md: -------------------------------------------------------------------------------- 1 | # Legacy patterns 2 | 3 | Event though the book is amazing some patterns so the signs of aging (e.g. replaced by ES5 feautures or turned out to be bad ideas) Moved some of the older patterns here. 4 | 5 | ## 6. Code re-use patterns 6 | 7 | ### classical inheritance 8 | 9 | * play on the word 'class', nothing to do with the word classical 10 | * JavaScript has no classes but constructor functions make same people think that 11 | * use the term constructor function 12 | * probably a bad idea but worth knowing about 13 | 14 | ```js 15 | function Parent(){ } 16 | function Child(){ } 17 | //inheritance magic happens here 18 | inherit(Child, Parent); 19 | ``` 20 | 21 | * inherit is not part of the language have to implement it yourself 22 | 23 | ### classical default pattern 24 | 25 | ```js 26 | function inherit(Child, Parent){ 27 | child.prototype = new Parent(); 28 | } 29 | ``` 30 | * prototype points at new parent object 31 | * children gets parent functionality via prototype 32 | * drawback: children gets both own and prototype properties from parent 33 | * drawback: can't really pass parameters, or end up with a lot of objects 34 | 35 | ### classical rent-a-constructor pattern 36 | 37 | ```js 38 | function Child(a, b, c, d){ 39 | Parent.apply(this, arguments); 40 | } 41 | ``` 42 | * solves the problem of passing arguments 43 | * borrows parent constructor 44 | * passing the child object to be bound to this 45 | * and passing any arguments 46 | * children does not inherit prototype properties 47 | * but gets true copies of own properties (not links) 48 | * inheritance is just a one of action 49 | * only copying own properties from parent 50 | * no proto links are kept, can't access parent prototype properties 51 | * multiple inheritance can be achieved by applying more than one constructors 52 | * in case of multiple inheritance and duplicate properties - last one wins 53 | 54 | ### classical rent-and-set prototype 55 | 56 | ```js 57 | function Child(a, b, c, d){ 58 | Parent.apply(this, arguments); 59 | } 60 | Child.prototype = new Parent(); 61 | ``` 62 | * combine the two patterns above 63 | * children gets copies of parents own members 64 | * and references re-usable functionality (from parents prototype) 65 | * drawback: parent constructor is called twice 66 | 67 | ### classical share the prototype pattern 68 | 69 | ```js 70 | function inherit(Child, Parent){ 71 | C.prototype = P.prototype; 72 | } 73 | ``` 74 | * no calls to the parent construtor at all 75 | * just share prototype as reusable functionality should be in the prototype anyways 76 | * fast lookups as all object references one prototype 77 | * BUT children can modify parent behaviour 78 | 79 | ### classical temporary constructor pattern 80 | 81 | ```js 82 | function inherit(Child,Parent){ 83 | var Temp = function(){}; 84 | Temp.prototype = Parent.prototype; 85 | Child.prototype = new Temp(); 86 | } 87 | ``` 88 | * a.k.a. proxy constructor 89 | * similar to default pattern but proxying via Temp constructor 90 | * Temp constructor makes sure we only inherit prototype properties 91 | * storing 'superclass' may be achieved by ```Child.uber = Parent.prototype``` 92 | * may also reset the constructor property on the prototype (```Child.prototype.constructor=Child```) 93 | * constructor property otherwise equals to 'Parent' and may be confusing when introspecting 94 | * optimized version with immediate function and temp function in closure: 95 | 96 | ```js 97 | 98 | var inherit = (function(){ 99 | var Temp = function(){}; 100 | return function(Child,Parent){ 101 | Temp.prototype = Parent.prototype; 102 | Child.prototype = new Temp(); 103 | Child.uber = Parent.prototype; 104 | Child.prototype.constructor = Child; 105 | } 106 | })(); 107 | ``` 108 | 109 | ### klass 110 | 111 | * some legacy JS libraries/frameworks emulate classes 112 | * usually there's a convention on how to name constructor functions (e.g. init) 113 | * they tend to support classical inheritance 114 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # JavaScript patterns 2 | 3 | Notes for JavaScript Patterns by Stoyan Stefanov 4 | 5 | ## 1. Introduction 6 | 7 | ### patterns in general 8 | 9 | * solution to a common problem 10 | * don't re-invent the wheel if you don't have to 11 | * provide a level of abstraction (models - all is wrong, some are useful) 12 | * common patterns help communication 13 | * this book covers 14 | * design patterns: (generally simpler) JS implementation of GoF patterns 15 | * coding pattens: JS specific pattern and good practices (main topic of the book) 16 | * antipatterns: common approach causing more problem than it solves 17 | 18 | ### (almost) everything is an object 19 | 20 | * only five primitive types are not objects: number, string, boolean, null, undefined 21 | * first three has object representation with primitive wrappers 22 | * functions are objects too 23 | * a var is an object 24 | * becomes a property of the internal Activation Object or global object if it's global 25 | * var itself is object-like, has properties called attributes (exposed since ES5) 26 | * attributes determine if var can be changed, deleted or enumerated in ```for-in``` 27 | 28 | ### what's an object? 29 | 30 | * an object is simply a collection of named properties (key-value pairs) 31 | * an object can have methods - simply when a value of a property is a function 32 | * and object can be modified (almost) anytime - properties added, removed, updated 33 | * an object is 34 | * either native: user defined (```{}```) or built-in (```Date```) - 35 | * native objects are described in the ES standard 36 | * or host object (```window```) - defined by the host environment 37 | 38 | ### no classes 39 | 40 | * no classes, unlearn if you worked with another OO language before 41 | * usually you just start with empty object and add properties to it 42 | * prefer composition over inheritance 43 | 44 | ### prototypes 45 | 46 | * one way of achieving inheritance, but nothing special 47 | * prototype is just object property that gets added to every function 48 | * prototype property points to a new empty object 49 | * prototype object almost identical to object literal or new Object() but 50 | * but constructor property points at the function you're creating not the builtin Object() 51 | 52 | ### environment 53 | 54 | * JS programs need an environment to run 55 | * the browser is the most common one 56 | * most patterns in the book are environment-agnostic 57 | * environments can provide their own host objects 58 | * which might have unexpected behaviour 59 | * not defined in the ES standard 60 | 61 | ### EcmaScript 5 62 | 63 | * core javascript is based on the EcmaScript standard 64 | * ES3 was accepted in 1999, ES4 was dropped, ES5 2009 65 | * most important addition: strict mode 66 | * trigerred by a ```'use strict';``` - backwards compatible 67 | * once per scope - that function is executed in a strict subset of the language 68 | * error is thrown if a non strict allowed feature is used 69 | * the plan is that future version will only support strict mode 70 | 71 | ### JSLint/JSHint 72 | 73 | * Javascript is an interpreted language with no compiler to help spotting errors 74 | * JSLint and JSHint are static code analysis tools 75 | * JSLint is really strict but JSHint is more configurable 76 | * All examples in the book pass JSLint 77 | 78 | ### console 79 | 80 | * the ```console``` object is not part of the language but available in most environments 81 | * Firebug in Firefox, Chrome Dev Tools in Chrome, standard I/O in Node.js, IE8+ Developer Tools 82 | * ```console.log``` - prints all parameters passed to it 83 | * ```console.dir``` - enumerates object and print all properties 84 | * when testing in browser console they log output by default 85 | 86 | ### chrome dev console 87 | 88 | * log XHR 89 | * console.time()/console.timeEnd() 90 | * console.dir()/dir() 91 | * inspect() / $0 92 | * $$ 93 | * monitorEvents() // mouse, key 94 | * keys()/values() 95 | * copy() 96 | 97 | ## 2. Essentials 98 | 99 | ### minimizing globals 100 | 101 | * JavaScript uses functions to manage scope 102 | * variables declared within a function are local and not visible outside 103 | * global variables are declared outside of functions or used without being declared 104 | * every JS environment has a global object - accessible via ```this``` outside of any function 105 | * every global variable becomes a property of the global object 106 | * in browser the global object has a property called ```window``` pointing to the global object itself 107 | 108 | ### problems with globals 109 | 110 | * shared among all the code in your app or webpage 111 | * danger of name collisions (especially with third-party code) 112 | * easy to create globals involuntarily: implied globals 113 | * implied globals: undeclared var is global 114 | * chained declaration also results in implied globals unless vars are declared in advance 115 | * you might accidentally overwrite existing host object properties 116 | * globals created with var outside of any function can't be ```delete```d 117 | * ES5 strict mode doesn't allow undeclared variables 118 | 119 | ### access to the global object 120 | 121 | * pass a reference to this from global scope to your function 122 | * non-strict mode: any function invoked (not with ```new```) has ```this``` pointing at the global object 123 | 124 | ### single var pattern (nope) 125 | 126 | * the world has moved on: see [this blog post](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) 127 | * single var statement at the top of your functions 128 | * single place to look for all the vars needed by your function 129 | * prevents logical errors when var is used before it's defined (hoisting) 130 | * might be good practice to initialize them with some defaults 131 | * to avoid undefined 132 | * also communicates the intended use 133 | 134 | ### hoisting 135 | 136 | * multiple var statements (anywhere in a function) act as if they were at the top 137 | 138 | ### caching array length in for loops (?) 139 | 140 | * for cache array length ```for (var i, max = arr.length; i < max, i++)``` 141 | * unless length can change 142 | 143 | ### for in and hasOwnProperty when needed 144 | 145 | * for in - filter things coming down the prototype chain 146 | ```js 147 | for (prop in obj){ 148 | if(obj.hasOwnProperty(prop)){ 149 | } 150 | } 151 | ``` 152 | * unless you know and can trust the object you're dealing with 153 | 154 | ### don't augment builtin prototypes 155 | 156 | * augmenting the prototype property of constructor functions is powerful 157 | * but hurts maintainability when done on builtin prototypes 158 | * when you absolutely need to: communicate with your team and check if property already exists 159 | 160 | ```js 161 | if (typeof Object.prototype.myMethod != = function) { 162 | Object.protoype.myMethod = function () { // implementation 163 | } 164 | } 165 | ``` 166 | 167 | ### ```switch``` 168 | 169 | * align each ```case``` with ```switch``` 170 | * indent code within each ```case``` 171 | * always end ```case``` with ```break;``` 172 | * avoid fallthroughs (no break) 173 | * end the switch with ```default:``` 174 | 175 | ### avoid implied typecasting 176 | 177 | * JavaScript implicitly typecasts variables when you compare them 178 | * always use ```===``` and ```!===``` as they check types as well 179 | 180 | ### eval() is evil 181 | 182 | * executing any String as JavaScript, seriously? 183 | * use ```[]``` to access dynamic properties 184 | * use JSON.parse for AJAX responses 185 | * always use ```function```s with ```setTimeout``` and ```setInterval``` (instead of Strings) 186 | * try not to use ```new Function()``` as it's similar to ```eval``` 187 | 188 | ### specify radix ```parseInt()``` (?) 189 | 190 | * ```parseInt()``` gets a numeric value from a String 191 | * has an optional radix parameter which shouldn't be omitted (in ES3) 192 | * ES3 ```parseInt('08')``` gets confused and treats numbers starting with 0 as octals 193 | 194 | ### code conventions 195 | 196 | * more important to agree and consistently follow than what the exact details are 197 | * indent everything within curly braces 198 | * always use curly braces (even when optional: for, if) 199 | * avoid errors or confusion by automatic semicolon insertion 200 | * always put opening curly braces on the same line as the previous statement 201 | * always use semicolons 202 | * generic and consistent use of spaces makes code more readable 203 | * use vertical spacing too - blank lines to separate units of code 204 | 205 | ### naming conventions 206 | 207 | * Capitalize constructor functions to differentiate them 208 | * use camelcase in function and variable name to separate words 209 | * all caps for constants (not expected to change even though this can't be enforced until ES6 const) 210 | * underscore (```_``` or ```__```) prefix (and/or suffix) for private API 211 | 212 | ### write to be read 213 | 214 | * keep comments up to date if you write them 215 | * jsDoc and YUIDoc can generate API docs from comments 216 | 217 | ### minify in production 218 | 219 | * any code delivered to the browser should be minified to improve performance 220 | * Uglify, Closure Compiler, YUI Compressor 221 | 222 | ### Run JSLint/JSHint 223 | 224 | * on edit/save 225 | 226 | ## 3. Literals 227 | 228 | * ```{}```,```[]``` and ```//``` is preferable 229 | * instead of using ```new Array()```, ```new Object()```, ```new RegExp()``` 230 | * more concise, less error-prone 231 | 232 | ### Object literal 233 | 234 | * object are simple maps or key value pairs 235 | * properties: values that are primitives or other objects 236 | * methods: values that are functions 237 | * custom objects (or user defined native objects) are mutable at any time 238 | * you can add/remove functionality as you go 239 | ```js 240 | var test = {}; 241 | test.message = 'hello'; 242 | test.hello = function(){ console.log('hello'); }; 243 | delete test.hello; 244 | ``` 245 | * but you're not required to start with an empty object 246 | ```js 247 | var test = { message: 'hello' }; 248 | ``` 249 | * note that empty object also inherit everything from ```Object.prototype``` 250 | * object literal notation emphasises that objects are just mutable hashes 251 | * no need for classes 252 | * also there's no need for scope resolution like with constructor 253 | * (scope resulotion is required to look for constructor with same name in the scope chain) 254 | 255 | ### Object constructor catch 256 | 257 | * there's no reason to use new Object() but legacy code might rely on an additional 'feature' 258 | * Object constructor accepts a parameter 259 | * and might delegate to another builtin constructor depending on the passed in value 260 | 261 | ### Custom constructor functions 262 | 263 | * just a function 264 | * when invoked with new: 265 | * an empty object is created referenced by ```this``` 266 | * this inherits the prototype of the function 267 | * properties and method are added to ```this``` 268 | * the newly created object referenced by ```this``` is returned 269 | * reusable members such as methods should go to the prototype 270 | * ```this``` is not really initalized with an empty object 271 | * but ```this``` actually is a result of Object.create() called with prototype 272 | * return value can be changed by explicitly returning a different object 273 | * non-Object return values are silently ignored and ```this``` is returned 274 | 275 | ### enforcing new 276 | 277 | * when a constructor is called without new ```this``` points at the global object 278 | * above is no longer true in strict mode, ```this``` won't point at the global object 279 | * always uppercase the first letter of constructor functions (and lowercase function names) 280 | * also when not using the prototype you can just return a new object literal (```that```) 281 | 282 | ### self-invoking constructor 283 | 284 | * another pattern to enforce new 285 | * calls itself with new if not called with new 286 | ```js 287 | function Waffle(){ 288 | if(!(this instanceof Waffle)){ 289 | return new Waffle(); 290 | //... 291 | } 292 | } 293 | ``` 294 | * alternative to above not hardcoding constructor name (not in ES5 strict mode!) 295 | ```js 296 | if(!(this instanceof arguments.callee)){ 297 | return new arguments.callee(); 298 | } 299 | ``` 300 | ### ```arguments``` 301 | 302 | * inside every function an object called arguments is created 303 | * containing all the parameters passed to the function when it was invoked 304 | * arguments has a property named callee which points back at the called function 305 | * arguments.callee is not allowed in ES5 strict mode 306 | 307 | ### Array literal 308 | 309 | * ```[]``` is preferred to ```new Array()``` 310 | * unexpected Array constructor behaviour: 311 | * called with single number, uses the number as length 312 | * if number is a float, results in RangeError as it's not valid array length 313 | * hack for repeating strings: ```new Array(256).join(' '); //255 spaces``` 314 | 315 | ### checking Array-iness 316 | 317 | * ```typeof``` with array operands returns ```object``` 318 | * before ES5 - checking for length or slice property 319 | * ES5 introduced .isArray method 320 | * also ```Object.prototype.toString()``` returns ```"[object Array]"``` (instead of ```"[object Object]"```) 321 | 322 | ### JSON 323 | 324 | * combination of object and array literal notation 325 | * but property names and Strings have to be wrapped in double-quotes 326 | * no functions or regex literals 327 | * ES5 JSON.parse and JSON.stringify 328 | 329 | ### Regex literal 330 | 331 | * ```//``` is preferred to ```new RegExp()``` 332 | * shorter and no need to escape quotes or double-escape backslashes 333 | * ```//``` - flags (g)lobal, (m)ultiline, case (i)nsensitive 334 | * use new RegExp() if the pattern is not known, and constructed runtime as a String 335 | * before ES5 the literal created only one object even when called repeatedly 336 | * (same object is a problem as it has properties like lastIndex set) 337 | * starting from ES5 regex literal returns new object 338 | 339 | ### primitive wrappers 340 | 341 | * JS primitives: number, string, boolean, null and undefined 342 | * number, string and boolean have primitive wrapper 343 | * primitive wrapper objects come with useful methods like .toFixed() or substring() 344 | * these methods work on primitive (variables) as well, converted temporarily 345 | * only use wrapper object when you need to augment a value and persist state (do you really need to?) 346 | * attempting to augment primitive value doesn't result in error but won't persist state 347 | * ```new``` wrapper constructor converts argument to primitive value 348 | 349 | ### Error object 350 | 351 | * JS has built-in Error constructors to be used with ```throw``` 352 | * ```Error()```, ```SyntaxError()```, ```TypeError()``` 353 | * these error object have ```name``` and ```message``` properties 354 | * Error constructors work the same way when called with or without ```new``` 355 | * ```throw``` works with any object - you can come up with custom error objects 356 | 357 | ### ```new Date()``` 358 | 359 | * one of the only builtin constructors you actually want to use 360 | 361 | ## 4. Functions 362 | 363 | ### functions are first class objects 364 | 365 | * can be created dynamically at runtime 366 | * can be assigned to vars, have their references copied to other vars 367 | * can be augmented and deleted (except for some special cases) 368 | * can be passed as arguments to other functions or returned by them 369 | * can have their own properties and methods 370 | * they's just another object with a special feature: they're executable 371 | * (but don't use ```new Function(arguments, code)``` as it's bad as eval) 372 | 373 | ### functions provide scope 374 | 375 | * in JS there's no curly braces local scope 376 | * blocks don't create a scope 377 | * there's only function scope 378 | * any var defined within function is only visible in that function 379 | * any war within an if block or for loop is local only to the wrapper function 380 | * if there's no wrapper function then it becomes global 381 | 382 | ### terminology 383 | 384 | * named function expression 385 | ```js 386 | var add = function add (a,b) { ... } 387 | ``` 388 | * if you skip the name: (unnamed) function expression or anonymous function 389 | ```js 390 | var add = function (a,b) { ... } 391 | ``` 392 | * using anonymous function won't affect definition or invocations etc. 393 | * but the name property of the function object will be undefined 394 | * careful, that's what's shown in stacktraces 395 | * (the name property isn't (wasn't?) part of the ES standard) 396 | 397 | * function declaration: 398 | ```js 399 | function (a,b) { ... } 400 | ``` 401 | * no semicolon required for function declarations 402 | * can only appear in program code: inside bodies of other functions or global space 403 | 404 | ### name property 405 | 406 | * use named function expression or declaration as it sets name property 407 | * allows function name to be displayed properly in debugger 408 | 409 | ### literal and using a different var name 410 | 411 | * function literal is an ambiguous term and should be avoided 412 | * giving a function a name and assigning it to a var with a different name might not work in older browsers 413 | 414 | ### function hoisting 415 | 416 | * for function declarations the function definition also gets hoisted 417 | * for function expressions it's only the variable that gets hoisted 418 | * function expressions are not callable before their definitions 419 | 420 | ### callback pattern 421 | 422 | * function reference (callback) can be passed into another function 423 | * the other function can execute (call back) the passed in function when appropriate 424 | 425 | ### callback method vs function 426 | 427 | * careful with using ```this``` if callback function is a method of an object 428 | * as callback is not called as method but a function this points at either global or the callers this 429 | * a workaround is to pass to object in a second parameter, then ```callback_function.call(obj, args)``` 430 | * or pass object and method name as string then ```var callback_function = obj[callback_name]``` 431 | 432 | ### callback examples 433 | 434 | * async event handlers in the browser accept callbacks (addEventListener) 435 | * ```setTimeOut``` and ```setInterval``` also accepts callbacks 436 | 437 | ### returning functions 438 | 439 | * functions are objects so they can be used as return values 440 | * a function can return another more specialized function or create one on demand 441 | 442 | ### self defining function pattern 443 | 444 | ```js 445 | var selfDefining = function(){ 446 | //e.g. do some prep you only got to do once 447 | selfDefining = function(){ 448 | // new function body to overwrite old one 449 | } 450 | } 451 | ``` 452 | 453 | * another name for this is lazy function definition 454 | * any properties previously set are cleared when redefining 455 | * also if function used with a different name (assigned to another var) 456 | * the var with the different name will use the non-redefined function 457 | 458 | ### immediate function 459 | 460 | * execute function as soon as it's defined 461 | 462 | ```js 463 | (function(){ 464 | //... 465 | }()); 466 | ``` 467 | 468 | * wrapped in parens so it's clear it's not a function declaration (but expression) 469 | * scope sandbox for initialization code 470 | * not leaking any variables 471 | * global objects can be passed as parameters 472 | * so don't have to use window inside immediate function 473 | * but don't pass too many vars to keep it readable 474 | * scope of the immediate function can be used to store private data 475 | * and then an object with public methods can be returned 476 | * can be used to implement self-contained modules 477 | * other names: self-invoking, self-executing 478 | 479 | ### immediate Object initialization 480 | 481 | ```js 482 | ({ 483 | property: 'value' 484 | method: function(){ ... } 485 | init: function(){ ... } 486 | }).init(); 487 | ``` 488 | 489 | * wrapped in parens so that it's clear it's not a code block but object 490 | * same purpose as immediate function above but more structured 491 | * private helper methods are clearly distinguishable 492 | * might not be trivial for minifiers to shorten inner helper method names 493 | * no reference to the object is available after init 494 | * unless you do ```return this;``` in init 495 | 496 | ### init-time branching 497 | 498 | * also known as load-time branching 499 | * if a condition is not going to change (e.g. browser feature) 500 | * only check it once in your program in your init code 501 | 502 | ### function properties and memoization 503 | 504 | * functions are objects and can have properties 505 | * all functions have a length property (number of arguments it accepts) 506 | * memoization: caching the return value of a function 507 | * you can add a property named cache - object 508 | * cache object: keys - argument, values - return value 509 | * multiple arguments - serialize them (e.g. JSON.stringify) to get a single value 510 | 511 | ### options/configuration object 512 | 513 | * when a function needs to be called with many parameters just use an object 514 | * parameter or doesn't matter, optional parameters can be skipped 515 | * easier to read, add remove parameters 516 | * but you need to remember parameter names, older minifier might not shorten names properly 517 | 518 | ### function application 519 | 520 | ```js 521 | var hello = function(message){ ... }; 522 | hello.apply(null, ['hey!']); 523 | ``` 524 | 525 | * a function can be applied using ```Function.prototype.apply``` 526 | * first argument is an object to bind ```this``` to 527 | * second argument is an array of arguments 528 | * in non-strict mode if first argument is null then it'll point at the global object 529 | * ```Function.prototype.call``` is just syntactic sugar over apply 530 | * call expects parameters as normal parameter list instead of an array 531 | 532 | ### function binding 533 | 534 | * ```Function.prototype.bind``` has the same signature as ```.call``` 535 | * creates a new function bound to the object and optional parameters passed in 536 | 537 | ### partial application / currying 538 | 539 | * call a function with less than all of it's arguments 540 | * and return a function expecting the rest of the arguments 541 | * this transformation of function is called currying or schonfinkelizing 542 | * use when find yourself calling a function with same arguments 543 | 544 | ```js 545 | // basic curry example 546 | function curriedAdd(x,y) { 547 | if(y === undefined) { 548 | return function(y) { 549 | return x+y; 550 | } 551 | } 552 | return x + y; 553 | } 554 | 555 | // currying with bind 556 | var curried = add.bind(undefined, 10); 557 | curried(5); 558 | ``` 559 | ## 5. Object creation patterns 560 | 561 | ### namespace pattern 562 | 563 | * reduce number of globals and name collisions (without excessive prefixing) 564 | * create a single global object for your app/lib 565 | * change all your functions and variables to become a property of that object 566 | 567 | ```js 568 | var myApp = myApp || {}; // prevent overwriting if namespace split across files 569 | myApp.myFunc = function(){ ... } 570 | ``` 571 | 572 | ### prevent overwriting 573 | 574 | * prevent overwriting if namespace split across files 575 | 576 | ```js 577 | var myApp = myApp || {}; 578 | ``` 579 | 580 | ### namespace function 581 | 582 | * can create a function to turn a dot separated string to nested objects 583 | 584 | ```js 585 | myApp.namespace = function namespace(ns){ 586 | var parts = ns.split('.').slice(1); // split on dot, skip first item 587 | var parent = myApp; 588 | parts.forEach(part, i){ 589 | if(typeof parent[part] === 'undefined') { 590 | parent[part] = {}; 591 | } 592 | parent = parent[part]; 593 | } 594 | } 595 | 596 | myApp.namespace('myApp.this.is.nested') === myApp.this.is.nested; 597 | ``` 598 | 599 | ### declare dependencies at the top 600 | 601 | ```js 602 | function myFunction(){ 603 | var dom = myApp.utils.dom; 604 | var event = myApp.utils.event; 605 | } 606 | ``` 607 | 608 | * shorter to type module names afterwards 609 | * deps in one place 610 | * some minifiers won't shorten global var names 611 | 612 | ### private members with closures 613 | 614 | * constructor function create a closure 615 | * any variables part of constructor closure are not visible outside of it 616 | 617 | ```js 618 | function Person() { 619 | var secret = 'hey'; 620 | return { 621 | doIt: function (){ 622 | // can see secret 623 | } 624 | } 625 | } 626 | 627 | var me = new Person(); 628 | me.secrect === undefined; 629 | secret === undefined; 630 | me.doIt(); //does it 631 | ``` 632 | 633 | * privileged methods are the ones declared within the constructor 634 | * privileged methods have access to private members 635 | * old versions of Firefox allowed access to private scope 636 | * internal arrays/objects are still modifiable via reference 637 | * make sure you return a new object with only the properties needed by caller 638 | * or copy your objects/arrays (with utility methods) 639 | 640 | ### private properties on prototype 641 | 642 | * properties are re-created every time an object is initialized 643 | * shared properties of prototype can also be made private with the same pattern 644 | 645 | ```js 646 | Person.prototype = (function(){ 647 | //all person sharing the same secret 648 | var secret = 'hey'; 649 | return { 650 | doIt: function (){ 651 | // can see secret 652 | } 653 | } 654 | }()); 655 | ``` 656 | 657 | ### revealing module pattern 658 | 659 | * revealing private methods by assigning them to properties of the returned object 660 | * can name the property differently to the internal function 661 | * can expose internal function under more than one names 662 | 663 | ```js 664 | Person.prototype = (function(){ 665 | function sayHello() {} 666 | return { 667 | greeting: sayHello 668 | } 669 | }()); 670 | ``` 671 | 672 | ### module pattern 673 | 674 | * combination of above: 675 | 1. define a namespace 676 | 1. assign an immediate function to it 677 | 1. which declares dependencies at the top 678 | 1. has private methods 679 | 1. and returns an object revealing public API of the module 680 | 1. globals can be passed in parameters to the immediate function 681 | * a module can create a constructor as well 682 | * if you return the constructor function instead of an object 683 | 684 | ### sandbox pattern 685 | 686 | * addresses some namespace drawbacks: single global var, long dotted names 687 | 688 | ```js 689 | new SandBox('dependencies', 'here', function(box){ 690 | // your code here 691 | }); 692 | ``` 693 | 694 | * you have a single global constructor 695 | * passed in callback function is you isolated environment 696 | * you initiate multiple Sandbox object and even nest them 697 | * you can name the constructor appropriately (instead of Sandbox) 698 | * YUI (now dead) used this 699 | 700 | ### implementing the sandbox pattern 701 | 702 | * as the Sandbox function is an object we can add a modules (object) property to it 703 | * add required modules to ```this``` 704 | * then call callback with ```this``` 705 | * the callback is the users sandbox and get populated with the requested functionality 706 | 707 | ### static members 708 | 709 | * just add property to constructor function - as that's an object as well 710 | * (normal methods are added to the prototype or the returned object) 711 | * you can't call static methods on instance unless you add an alias to prototype 712 | * when aliasing static method on prototype - careful if you use ```this``` within the method 713 | 714 | ### private static members 715 | 716 | * return constructor function via an immediate (self invoking) function 717 | * variables within the immediate function are invisible outside (as usual) 718 | 719 | ### constants 720 | 721 | * no real way to do it in ES5 722 | * just naming convention: all caps, and also worth making sure they're static 723 | * ```const``` keyword coming in ES6 724 | 725 | ### chaining 726 | 727 | * methods (that may not have a meaningful return value) returning this 728 | * allows calling methods in a chain in a single expression 729 | * pros: save some typing, more concise, small and focused methods 730 | * cons: may get a bit harder to debug - lot happening on a single line 731 | * JQuery uses it 732 | 733 | ### ```method()``` method 734 | 735 | * making JavaScript a bit more class-like (which is probably a bad idea) 736 | * some syntactic sugar to add functions to the prototype property of the constructor 737 | 738 | ```js 739 | var Person = fucntion(){...}.method('methodName', function(){ ... }); 740 | ``` 741 | 742 | ## 6. Code reuse patterns 743 | 744 | Prefer composition over inheritance. 745 | 746 | ### classical inheritance 747 | 748 | * play on the word 'class', nothing to do with the word classical 749 | * JavaScript has no classes but constructor functions make same people think that 750 | * use the term constructor function 751 | * probably a bad idea but worth knowing about 752 | 753 | ### the prototype chain 754 | 755 | * can think of objects as blocks of memory 756 | * all objects from the same constructor point at same prototype object 757 | * can think of it as if they were pointing at it via a ```__proto__``` property 758 | * ```__proto__``` is actually available in some JavaScript environments 759 | * properties are looked up by walking through this prototype chain: 760 | * if an object doesn't have a property it's ```__proto__``` is consulted 761 | 762 | ### prototypal inheritance 763 | 764 | * modern classless pattern 765 | * no classes, objects inherit from objects 766 | 767 | ```js 768 | function object (parent) { 769 | function F() {} 770 | F.prototype = parent; 771 | return new F(); 772 | } 773 | 774 | var parent = { ... } 775 | var child = object(parent); 776 | ``` 777 | 778 | * children get parent methods and properties via ```__proto__``` link 779 | * parent can be created via constructor as well (not just literal) 780 | 781 | ### Object.create 782 | 783 | * prototypal inheritance is built-in since ES5 784 | * ```Object.create(parentObject, ownPropertiesObject)``` 785 | 786 | ### inheritance by copying properties 787 | 788 | ```js 789 | //shallow copy 790 | function extend (parent, child) { 791 | var key; 792 | child = child || {}; 793 | for(key in parent){ 794 | if(parent.hasOwnProperty(key)){ 795 | child[key] = parent[key]; 796 | } 797 | } 798 | return child; 799 | } 800 | ``` 801 | 802 | * shallow copy just copies references of arrays and object 803 | * children can modify parent properties :( 804 | * deep copy is when array elements and object properties are copied as well 805 | 806 | ### mixins 807 | 808 | * just deep copy all properties from multiple objects and mix in to a new Object 809 | * not like mixins in other languages, here: no link to parent 810 | 811 | ### borrowing methods 812 | 813 | * sometimes you want to use one or two methods of an existing Objects 814 | * you don't want a parent-child relationship with that Object 815 | * can be done with ```call``` and ```apply``` 816 | * pass in your object to bind ```this``` to within the function 817 | 818 | ```js 819 | notMyObject.doStuff.call(myObject, param1, param2); 820 | //or 821 | notMyObject.doStuff.apply(myObject, [param1, param2]); 822 | ``` 823 | 824 | ### example: borrowing from Array 825 | 826 | ```js 827 | function example(){ 828 | return [].slice.call(arguments, 1, 3); 829 | //or 830 | //Array.prototype.slice.call(arguments, 1, 3) 831 | } 832 | ``` 833 | 834 | * one typical example is borrowing ```Array``` methods for the ```arguments``` object 835 | 836 | ### borrow and bind 837 | 838 | * with call/apply ```this``` reference has to be passed in when called 839 | * sometimes it's better to locked/bound to specific object in advance 840 | * ```bind``` function binds a method to an object 841 | 842 | ```js 843 | //simple bind function 844 | function bind (object, method) { 845 | return function() { 846 | return method.apply(object, [].slice.call(arguments)); 847 | } 848 | } 849 | ``` 850 | 851 | ### ```Function.prototype.bind``` 852 | 853 | * ES5 has bind builtin, it also accepts partial argument list 854 | 855 | ```js 856 | //basic example implementation handling partial application 857 | Function.prototype.bind = Function.prototype.bind || function(thisArg) { 858 | var fn = this; 859 | var slice = Array.prototype.slice; 860 | var args = slice.call(arguments, 1); 861 | 862 | return function () { 863 | return fn.call(thisArg, args.concat(slice.call(arguments))) 864 | } 865 | } 866 | ``` 867 | ## 7. Design patterns 868 | 869 | * as made famous by the GoF book 870 | * language independent but mainly for Java/C++ like strongly typed languages 871 | * most of them are really easy to implement in JavaScript 872 | 873 | ### singleton 874 | 875 | * only one instance of specific class 876 | * JS: no classes, when you create a new object there's no other like it 877 | * (in JS some people mean Chapter 5. module pattern by singletons) 878 | * you might want singletons when using ```new```: 879 | * store instance as a (static) property on constructor function 880 | * but static property is public 881 | * can also protect instance as a private static member (via closure) 882 | * (be careful not to wipe out prototype properties) 883 | 884 | ```js 885 | var IAmSingleton; 886 | 887 | (function(){ 888 | var instance; 889 | IAmSingleton = function() { 890 | if (instance) { 891 | return instance; 892 | } 893 | instance = this; 894 | 895 | //... all the functionality 896 | this.whatever = true; 897 | } 898 | })(); 899 | ``` 900 | 901 | 902 | ### factory 903 | 904 | * performs repeating operations when setting up similar objects 905 | * built-in Object() constructor is an example, returns Number, String, etc based in argument 906 | 907 | ### other patterns 908 | 909 | * all easily implemented in Javascript 910 | * iterator - processing aggregate data 911 | * decorator - dinamically add functionallity to an object at runtime 912 | * strategy - select algorithm at runtime 913 | * facade - alternative interface to an object - combining method 914 | * proxy - alternative interface, sitting in front of object 915 | * mediator - separate object providing communication instead of loose coupling 916 | * observer - publish/subscribe to aid loose coupling, widely used in JavaScript 917 | 918 | ## 8. DOM and browser patterns 919 | 920 | ### separation of concerns 921 | 922 | * content - HTML, presentation - CSS, behaviour - JS 923 | * progressive enhancement - basic HTML should work, too 924 | * no inline JS (```onclick```) or CSS (```style``` attribute) 925 | * JS - capability detection instead of browser sniffing 926 | 927 | ### DOM access 928 | 929 | * DOM access is expensive and should be kept to minimum 930 | * avoid DOM access in loops 931 | * assign DOM references to local variables and work with those 932 | * use selectors API (since IE8) 933 | * cache length when iterating over HTML collections (old IE versions) 934 | * using ids is the fastest way to access DOM elements 935 | 936 | ```js 937 | //var it up 938 | var hey = document.getElementById('hey'); 939 | //use selectors API 940 | document.querySelector('.hello'); 941 | document.querySelectorAll('.hello'); 942 | ``` 943 | 944 | ### DOM manipulation 945 | 946 | * in addition to accessing you often want to add/remove/modify DOM elements 947 | * updates to DOM can be really expensive, fewer the better 948 | * can cause a browser to repaint the screen 949 | * can also cause a reflow - recalculating the elements geometry 950 | 951 | ### batch DOM additions 952 | 953 | * do batch additon to DOM, and try doing them outside of the live tree 954 | 955 | ```js 956 | //create elements outside of live tree 957 | var paragraph = document.createElement('p'); 958 | var text = document.createTextNode('some text'); 959 | paragraph.appendChild(text); 960 | //add it at the end 961 | document.body.appendChild(paragraph); 962 | ``` 963 | 964 | * use a document fragment if no parent element otherwise 965 | ```js 966 | //create a fragment if the elements you're adding don't have a parent 967 | var fragment = document.createDocumentFragment(); 968 | fragment.appendChild(document.createElement('p')); 969 | fragment.appendChild(document.createElement('p')); 970 | //add fragment at the end 971 | document.body.appendChild(fragment); 972 | ``` 973 | 974 | ### batch DOM updates 975 | 976 | * you can make a clone of the root of the subtree you're changing 977 | * then swap your clone with the original 978 | 979 | ```js 980 | var oldNode = document.getElementById('result'); 981 | var clone = oldNode.cloneNode(true); 982 | //... make your changes 983 | //then replace 984 | oldnode.parentNode.replaceChild(clone, oldNode); 985 | ``` 986 | 987 | ### event handling 988 | 989 | * no ```onclick``` in HTML (seriously :) 990 | * usually via framework but worth knowing the basics 991 | * ```EventTarget.addEventListener(eventType, listener, useCapture)``` adds an event handler 992 | * event type - String representing event type to capture 993 | * listener - object implementing EventListener interface, or simply a function 994 | * useCapture - all events will events dispathed to this listener first before other listeners beneath this in the DOM tree 995 | * ```addEventListener``` is not available in IE8 and before 996 | * useCapture - optional (with default=false) in recent browser 997 | * just always pass useCapture in for broadest compatibility 998 | * handler is called with event object 999 | * ```e.stopPropagation()``` - prevents event from bubbling up to document root 1000 | * ```e.preventDefault()``` - prevents default action (if required) 1001 | 1002 | ### event delegation 1003 | 1004 | * events are bubbling up to parent elements 1005 | * can reduce the number of event listeners by attaching one only to the parent 1006 | * event properties can be used to filter out the events we care about 1007 | * drawback: slightly more complex code but there are JS libraries to make this easy 1008 | 1009 | ### web workers 1010 | 1011 | * javascript runs on a single thread in the browser 1012 | * web workers: background thread support by the browser 1013 | * only in modern browsers (from IE 10) 1014 | * you put worker code in a separate file 1015 | * worker can use ```postMessage``` to send messages to the caller 1016 | * caller can subscribe to messages using ```Worker.onMessage``` 1017 | 1018 | ```js 1019 | var worker = new Worker('my_worker.js'); 1020 | worker.onMessage(function(event) { 1021 | console.log(event.data); 1022 | }); 1023 | //my_worker.js: postMessage('hello there'); 1024 | ``` 1025 | 1026 | ### XMLHttpRequest 1027 | 1028 | * special constructor function available in most browsers 1029 | * allows sending HTTP requests 1030 | * libraries wrap this - e.g. Jquery.ajax 1031 | 1032 | ```js 1033 | var xhr = new XMLHttpRequest(); 1034 | xhr.onreadystatechange = function handleResponse() { 1035 | if(xhr.readyState === 4 && xhr.status === 200) { 1036 | console.log(xhr.responseText); 1037 | } 1038 | }; 1039 | xhr.open('GET', 'page.html', true); 1040 | xhr.send(); 1041 | ``` 1042 | 1043 | ### JSONP 1044 | 1045 | * JSON with padding 1046 | * not restricted by same origin policy (but really you should just setup CORS properly) 1047 | * callback parameter in URL specifies the JS function handling the response 1048 | * server should return data passed into the callback function as parameter 1049 | 1050 | ### Image beacons 1051 | 1052 | * even without javascript, data can be sent to the server 1053 | * include an img tag (typically 1x1 transpatrent PNG) 1054 | * actually better to respond with 204 No Content (old IE version might not like this) 1055 | * browser makes a request when the page is being loaded 1056 | 1057 | ### combining scripts, minification, caching 1058 | 1059 | * concatenate scripts to reduce number of HTTP requests 1060 | * loosing some of the granular caching benefits 1061 | * have to come up with versioning scheme 1062 | * minify and gzip to reduce script size 1063 | * use source maps to still allow easy debugging 1064 | * use cache headers properly (browsers don't cache for too long by default) 1065 | 1066 | ### script tag location and attributes 1067 | 1068 | * don't use language or type attribute as browsers assume JS anyways 1069 | * script tags (by default) block page loading until they're downloaded, parsed and run 1070 | * put your script tags at the bottom of the page or use HTML5 async script (since IE10) 1071 | 1072 | ### HTTP chunking 1073 | 1074 | * sending HTTP response in chunks, browser can deal with this 1075 | * possible to send page header first, then page content, the script tags at the bottom 1076 | * these can be sent in different chunks, browser will progressively render 1077 | 1078 | ### dynamic script tag example 1079 | 1080 | ```js 1081 | //create script tag 1082 | var script = document.createElement('script'); 1083 | script.src = 'my_script.js'; 1084 | //append to head 1085 | document.documentElement.firstChild.appendChild(script); 1086 | ``` 1087 | 1088 | * careful with multiple files (if they depend on each other) 1089 | * careful with what you append to (make sure it exists or use the script tag running the append) 1090 | 1091 | ### lazy loading 1092 | 1093 | * dynamically loading (page-enhancing) JS on page onload 1094 | 1095 | ### preloading 1096 | 1097 | * dinamically create object tag (to prevent execution) 1098 | * set src to js file, and width, height to 0 1099 | * next page will get that JS file from cache 1100 | --------------------------------------------------------------------------------