├── 01 Variables and Parameters ├── 01_let.md ├── 02_const.md ├── 03_Destructuring.md ├── 04_Default_Parameter_Values.md ├── 05_Rest_Parameters.md ├── 06_Spread_Operator.md └── 07_Template_Literals.md ├── 02 Classes ├── 01_Classes.md ├── 02_Getters_and_Setters.md └── 03_Inheritance.md ├── 03 Functional JS ├── 01_Arrow_Functions.md ├── 02_Arrows_as_Callbacks.md ├── 03_Iterables_and_Iterators.md ├── 04_Generators.md ├── 05_Generator_Range_Function_Example.md └── 06_Comprehensions.md ├── 04 Built-In Objects ├── 01_Arrays.md ├── 01_Numbers.md ├── 02_Sets.md ├── 02_Sets_and_Maps.md ├── 03_Maps.md └── 04_WeakMap_and_WeakSet.md ├── 05 Asynchronous Development ├── 01_Promise_Primer.md ├── 02_Promise_Basics.md ├── 03_Advanced_Promises.md ├── 04_Basic_Async_Generators.md ├── 05_More_Async_Generators.md └── 06_Async_Generators_and_Promises.md ├── 06 Objects in ES6 ├── 01_Object_is_and_Object_assign.md ├── 02_Object_Shorthand_and_Computed_Properties.md ├── 03_Proxies.md └── 04_Proxying_Functions.md └── 07 Modules ├── 01_IIFE.md ├── 02_CommonJS.md ├── 03_AMD.md └── 04_ES6_Modules.md /01 Variables and Parameters/01_let.md: -------------------------------------------------------------------------------- 1 | # The `let` Keyword 2 | 3 | `let` allows us to define variables in a different way than `var`. 4 | 5 | With `var` there are two types of scope: *global* when the variable is defined outside of the function, and *function scope* for variables declared inside of a function. 6 | 7 | **JavaScript has no block scope.** In this example, it seems like `x` would only be available inside of the `if`, but that's not the case: 8 | 9 | ```javascript 10 | var doesSomething = function(param) { 11 | 12 | if(param){ 13 | var x = 5; 14 | } 15 | return x; 16 | }; 17 | ``` 18 | 19 | `x` is avaible throughout the `doesSomething` function, and the `return` will work because there is going to be a variable named `x`. 20 | 21 | 22 | **`let` gives us true block scoping:** 23 | 24 | ```javascript 25 | var doesSomething = function(param) { 26 | 27 | if(param){ 28 | let x = 5; 29 | } 30 | return x; 31 | }; 32 | ``` 33 | In the above example, there will be an error if no other `x` variables are defined in an outer scope and the `if` block isn't hit. 34 | 35 | Here is an example of behavior in a `for` loop: 36 | ```javascript 37 | for(var i = 0; i < 10; i++){ 38 | 39 | } 40 | return i; // returns 10 41 | 42 | for(let j = 0; j < 10; j++){ 43 | 44 | } 45 | return j; // ReferenceError: j is not defined 46 | ``` 47 | 48 | 49 | `let` will be the replacement for `var` because it will let us declare variables where we need them, avoid hoisting, and give us true block scoping like most developers expect. 50 | 51 | -------------------------------------------------------------------------------- /01 Variables and Parameters/02_const.md: -------------------------------------------------------------------------------- 1 | # The `const` Keyword 2 | 3 | `const` creates and initializes a read-only variable at a constant value that you can never change. 4 | 5 | In ES6, `const` has block scoping: 6 | 7 | ```javascript 8 | var doSomething = function() { 9 | const x = 12; 10 | var x = 10; //Syntax error: Variable 'x' has already been declared. 11 | } 12 | ``` 13 | 14 | The `x` defined in `doSomething` shadows the outer `x`: 15 | ```JavaScript 16 | const x = 12; 17 | var doSomething = function() { 18 | let x = 10; // Note: would also work with `var` 19 | return x; // 10 20 | } 21 | ``` 22 | 23 | *Note: it is convention to use ALL CAPS for constants, but the above is for illustrative purposes.* 24 | 25 | 26 | -------------------------------------------------------------------------------- /01 Variables and Parameters/03_Destructuring.md: -------------------------------------------------------------------------------- 1 | # Destructuring 2 | 3 | Destructuring allows you to assign values to a set of variables. 4 | 5 | ```JavaScript 6 | let x = 2; 7 | let y = 3; 8 | 9 | [x, y] = [y, x]; // x is now 3, and y is now 2 10 | ``` 11 | What we see on the right side of the destructuring assignment in the above example is an array built from the values that are in `y` and `x`. 12 | However, on the left side, it is **not** an array, but rather the individual variables `x` and `y` surrounded by square brackets (`[]`) because we are destructuring an array. 13 | 14 | In other words, in the above example we are taking the value of the variable in the first index of the array `[y, x]` (in this case '`y = 3`') and assigning it to `x`, and taking the value of the variable in the second index of the array [`y, x`] (in this case `x = 2`) and assigning it to `y`. Thus, our variables have switched values. 15 | 16 | Variable assignment can also be done from a function that returns an array: 17 | ```JavaScript 18 | var doSomething = function() { 19 | return [3, 2]; 20 | } 21 | 22 | let [x, y] = doSomething(); // x = 3, y = 2 23 | ``` 24 | 25 | If there was an additional member of the array, but you still wanted `x` to be 3 and `y` to be 2, a `,` can be added to skip the value the destructuring assignment: 26 | ```JavaScript 27 | var doSomething = function() { 28 | return [1, 3, 2]; 29 | } 30 | 31 | let [, x, y] = doSomething(); // x = 3, y = 2 32 | ``` 33 | 34 |   35 | 36 | ###### Object Destructuring 37 | 38 | In this example, we create variables `first`, `last`, and `twitter` and assign them to the return values of the `doSomething` function. 39 | 40 | ```JavaScript 41 | let doSomething = function() { 42 | return { 43 | firstName: "Charles", 44 | lastName: "Bronson", 45 | twitter: "@NoDice" 46 | }; 47 | } 48 | let { firstName: first, 49 | lastName: last, 50 | twitter: twitter } = doSomething(); 51 | ``` 52 | *It looks like we're creating an object literal, but we're not.* 53 | 54 | Think of the left hand side as being how you navigate into an object: 55 | 56 | ```JavaScript 57 | let doSomething = function() { 58 | return { 59 | firstName: "Charles", 60 | lastName: "Bronson", 61 | handles: { 62 | twitter: "@NoDice", 63 | facebook: "bronson69" 64 | } 65 | }; 66 | } 67 | let { firstName: first, 68 | lastName: last, 69 | handles:{twitter: twitter} } = doSomething(); 70 | ``` 71 | 72 | If you are cool with having your variable names be the same as the properties, you can use the shorthand: 73 | 74 | ```JavaScript 75 | let { firstName, lastName, handles:{twitter} } = doSomething(); 76 | ``` 77 | 78 |   79 | 80 | ###### Destructuring in a function 81 | The old way when working with a function: 82 | 83 | 84 | ```JavaScript 85 | let doSomething = function(url, config){ 86 | return config.data; // "test" 87 | }; 88 | 89 | var config = { data: "test", cache: false } 90 | 91 | let result = doSomething("api/test", config); 92 | ``` 93 | 94 | New way: 95 | ```JavaScript 96 | let doSomething = function(url, {data, cache}){ 97 | return data; // `test` comes directly from the `data` property 98 | }; 99 | 100 | let result = doSomething( 101 | "api/test", { 102 | data: "test", 103 | cache: false 104 | } 105 | ); 106 | 107 | ``` 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /01 Variables and Parameters/04_Default_Parameter_Values.md: -------------------------------------------------------------------------------- 1 | # Default Parameter Values 2 | 3 | In JS, the old way of handling a missing or falsey parameter: 4 | ```JavaScript 5 | var doSomething = function(name){ 6 | name = name | "Taylor"; 7 | return name; 8 | }; 9 | ``` 10 | 11 | The new way in ES6 is like so: 12 | ```JavaScript 13 | var doSomething = function(name="Taylor"){ 14 | return name; 15 | }; 16 | ``` 17 | 18 | **Note: The default parameter syntax has some different behavior with different calling parameters.** 19 | 20 | ```JavaScript 21 | let result = doSomething(); // "Taylor" 22 | let result = doSomething(""); // Undefined 23 | let result = doSomething(undefined); // "Taylor" 24 | let result = doSomething("Colleen"); // "Colleen" 25 | let result = doSomething(null); // null 26 | ``` 27 | 28 |   29 | 30 | #### Combining Concepts: 31 | ```JavaScript 32 | let doSomething = function(a = 1, b = 2, c = 3){ 33 | return [a, b, c]; 34 | }; 35 | 36 | let [a,b,c] = doSomething(5, undefined); // a = 5, b = 2, c = 3 37 | ``` 38 | 39 | Example with destructuring: 40 | ```JavaScript 41 | let doSomething = function(url, {data = "Taylor", cache = true}){ 42 | return data; 43 | }; 44 | 45 | let result = doSomething("api/test", { cache: false }); // "Taylor" is returned (and `cache` is `false` inside the function) 46 | 47 | 48 | -------------------------------------------------------------------------------- /01 Variables and Parameters/05_Rest_Parameters.md: -------------------------------------------------------------------------------- 1 | # Rest Parameters 2 | Rest parameters are how you deal with an unknown number of params with a function. 3 | 4 | The old way was to use the implicit `arugments` object that holds are the args passed into a function: 5 | ```JavaScript 6 | let sum = function() { 7 | let result = 0; 8 | for (let i = 0; i < arguments.length; i++) { 9 | result += arguments[i]; 10 | } 11 | return result; 12 | }; 13 | 14 | let result = sum(1, 2, 3) // 6 15 | ``` 16 | 17 | One of the problems though is that `arguments` looks like an array, but it really isn't (it's an object that has some of the qualities of an array). 18 | 19 | The new way in ES6 gives us a better syntax to achieve the same result: 20 | ```JavaScript 21 | let sum = function(...numbers) { 22 | let result = 0; 23 | for (let i = 0; i < numbers.length; i++){ 24 | result += numbers[i]; 25 | } 26 | return result; 27 | }; 28 | ``` 29 | 30 |   31 | 32 | The *rest parameter* is always the last parameter, and features the `...` prefix. 33 | 34 | This version of `sum()` has a parameter named `numbers`, and incoming values passed in are packaged into a true array for the function to use. 35 | 36 | Another example: 37 | ```JavaScript 38 | let doSomething = function(name, ...numbers){ 39 | let result = 0; 40 | numbers.forEach(function(n){ 41 | result += n; 42 | }); 43 | return result; 44 | }; 45 | 46 | let result = doSomething("Taylor", 1, 2, 3); // 6 47 | ``` 48 | 49 | If you don't pass any numbers in, then the function is fed an empty array, and `result` is returned as 0 like we specified. 50 | 51 | 52 | -------------------------------------------------------------------------------- /01 Variables and Parameters/06_Spread_Operator.md: -------------------------------------------------------------------------------- 1 | # Spread Operator 2 | 3 | The spread operator uses the same `...` syntax as the rest parameter. However, when used outside a function parameter list, the `...` means we want to spread an array across individual parameters. 4 | 5 | In this example, 1 goes to `x`, 2 goes to `y`, and `3` goes to z: 6 | ```JavaScript 7 | let doSomething = function(x, y, z) { 8 | return x + y + z; 9 | } 10 | 11 | var result = doSomething(...[1, 2, 3]); // gives us 6 12 | ``` 13 | 14 |   15 | 16 | #### Building Arrays with the Spread Operator 17 | ```JavaScript 18 | var a = [4, 5, 6]; 19 | var b = [1, 2, 3, ...a, 7, 8, 9]; //b is now [1, 2, 3, 4, 5, 6, 7, 8, 9] 20 | ``` 21 | 22 | Spreading makes data we already have in arrays easier to work with. 23 | 24 | -------------------------------------------------------------------------------- /01 Variables and Parameters/07_Template_Literals.md: -------------------------------------------------------------------------------- 1 | # Template Literals 2 | 3 | Template literals can help to build string values we may want to assign to a variable, among other uses. 4 | 5 | Old school string concatenation becomes tedious: 6 | ```JavaScript 7 | let category = "music"; 8 | let id = 2112; 9 | 10 | let url = "http://apiserver/" + category + "/" + id; 11 | ``` 12 | 13 | In ES6, there are some differences: 14 | * The string is delimited with backticks (` `` `) 15 | * Inside of the backticks, you can have literal text and substitution placeholders via `${placeholder}` 16 | 17 | ```JavaScript 18 | let category = "music"; 19 | let id = 2112; 20 | 21 | let url= `http://apiserver/${category}/${id}`; 22 | ``` 23 | 24 | #### A more interesting example: 25 | ```JavaScript 26 | let upper = function(strings, ...values){ 27 | let result = ""; 28 | for (var i = 0; i < strings.length; i++){ 29 | result += strings[i]; 30 | 31 | if (i < values.length){ 32 | result += values[i]; 33 | } 34 | 35 | } 36 | 37 | return result.toUpperCase(); 38 | }; 39 | 40 | var x = 1; 41 | var y = 3; 42 | var result = upper `${x} + ${y} is ${x + y}`; // Gives us "1 + 3 IS 4" 43 | ``` 44 | 45 | You can associate a tag with a template. In this case, the tag `upper` is a function that is invoked. 46 | 47 | The first parameter being passed into the `upper()` function is known as a parsed template string. These are the pieces of literal text in the template, chopped into an array such that the substitutions are removed. The second parameter is a rest parameter (also an array) using the values of the substitutions. 48 | 49 | ```JavaScript 50 | strings = ["", " + ", " is ", ""]; 51 | values = [1, 3, 4] 52 | ``` 53 | 54 |   55 | 56 | This shows us that there are lots of possibilities in terms of tags that take a template and encode it, tags for localization, and more. 57 | -------------------------------------------------------------------------------- /02 Classes/01_Classes.md: -------------------------------------------------------------------------------- 1 | # Classes 2 | 3 | In the old way of writing classes, a constructor function and a prototype object that all instances would inherit from had to be created. 4 | 5 | This looked something like this: 6 | ```JavaScript 7 | var Employee = function() { 8 | 9 | }; 10 | 11 | Employee.prototype = { 12 | doSomething: function(){ 13 | return "done!"; 14 | } 15 | }; 16 | 17 | var e = new Employee(); 18 | ``` 19 | 20 | This syntax sucks. The new ES6 syntax to do the same thing is like this: 21 | ```JavaScript 22 | class Employee { 23 | doSomething() { 24 | return "complete!"; 25 | } 26 | 27 | doSomethingElse() { 28 | return "also complete!" 29 | } 30 | } 31 | 32 | var e = new Employee(); 33 | e.doSomething(); // "complete!" 34 | ``` 35 | 36 | This doesn't require the use of the `function` keyword. Behind the scenes, the same thing is happening. Commas aren't required between method definitions, and semicolons are optional. 37 | 38 | Currently we only have hardcoded strings in our objects, so let's introduce state and constructor functions. 39 | 40 | #### The Constructor 41 | Managing state consistently requires some initialization logic for an object, that is implemented with a constructor. The constructor function is automatically invoked when you say `new `, and inside the constructor you can use the implicit `this` reference to manipulate the object and store instance state. 42 | 43 | ```JavaScript 44 | class Employee { 45 | constructor(name) { 46 | this._name = name; 47 | } 48 | 49 | doSomething() { 50 | return "complete!"; 51 | } 52 | 53 | getName() { 54 | return this._name; 55 | } 56 | } 57 | 58 | let e1 = new Employee("Taylor"); 59 | let e2 = new Employee("Charles"); 60 | 61 | e1.getName(); // "Taylor" 62 | e2.getName(); // "Charles" 63 | ``` 64 | -------------------------------------------------------------------------------- /02 Classes/02_Getters_and_Setters.md: -------------------------------------------------------------------------------- 1 | # Getters and Setters 2 | 3 | 4 | 5 | ```JavaScript 6 | class Employee { 7 | 8 | get name() { 9 | return this._name.toUpperCase; 10 | } 11 | 12 | set name(newValue) { 13 | this._name = newValue; 14 | } 15 | } 16 | let e1 = new Employee("Taylor"); 17 | let e1 = new Employee("Charles"); 18 | 19 | e1.name; // "Taylor" 20 | e2.name; // "Charles" 21 | 22 | e1.name = "Chris"; // sets e1's name to "Chris" 23 | ``` 24 | 25 | Using `get` in this manner, we are now able to use the cleaner call `e1.name`. 26 | 27 | You don't have to supply a setter if you don't want the object's properties to be changed. 28 | 29 | It is also possible for the constructor to call the setter: 30 | ```JavaScript 31 | constructor(name) { 32 | this.name = name; 33 | } 34 | 35 | set name(newValue) { 36 | this._name = newValue; 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /02 Classes/03_Inheritance.md: -------------------------------------------------------------------------------- 1 | # Inheritance 2 | 3 | The 3 Pillars of Object Oriented Programming: 4 | 1. Encapsulation 5 | 2. Polymorphism 6 | 3. Inheritance 7 | 8 | In ES6, it is easy to specify an inheritance relationship. 9 | 10 | ```JavaScript 11 | class Person { 12 | constructor(name) { 13 | this.name = name; 14 | } 15 | 16 | get name() { 17 | return this._name; 18 | } 19 | 20 | set name(newValue) { 21 | this._name = newValue; 22 | } 23 | } 24 | ``` 25 | 26 | Say we want to create an employee, and each employee "is-a" person. The `extends` keyword handles our inheritance. 27 | 28 | ```JavaScript 29 | . 30 | . 31 | . 32 | class Employee extends Person { 33 | doWork() { 34 | return `${this._name} is working`; // uses ES6's literal syntax 35 | } 36 | } 37 | 38 | let p1 = new Person("Taylor"); 39 | let e1 = new Employee("Chris"); 40 | e1.doWork(); // "Chris is working" 41 | ``` 42 | 43 | Often there will be additional pieces of information that will need to be stored in an object. For example, our Employees should have a title... 44 | 45 | # super 46 | 47 | `super` allows you to forward a call from a child class's constructor to the superclass's constructor and forward along the required parameters. 48 | 49 | ```JavaScript 50 | class Employee extends Person { 51 | constructor(name, title){ 52 | super(name); 53 | this._title = title; 54 | } 55 | 56 | get title() { 57 | return this._title; 58 | } 59 | } 60 | ``` 61 | You could use `this._name = name;` in the derived class constructor, but this isn't the appropriate approach, since there could be validation logic, etc. 62 | 63 | `super` can also be called in other ways. For example, If a `Person` does work, they do it for free. If they are an `Employee`, they are paid: 64 | 65 | ```JavaScript 66 | class Person { 67 | 68 | constructor(name) { 69 | this.name = name; 70 | } 71 | 72 | get name() { 73 | return this._name; 74 | } 75 | 76 | set name(newValue) { 77 | if(newValue) { 78 | this._name = newValue; 79 | } 80 | } 81 | 82 | doWork() { 83 | return "free"; 84 | } 85 | } 86 | 87 | 88 | class Employee extends Person { 89 | 90 | constructor(title, name) { 91 | super(name); 92 | this._title = title; 93 | } 94 | 95 | get title() { 96 | return this._title; 97 | } 98 | 99 | doWork() { 100 | return "paid"; 101 | } 102 | } 103 | 104 | let e1 = new Employee("Taylor", "Developer"); 105 | let p1 = new Person("Charles"); 106 | 107 | p1.doWork(); // "free" 108 | e1.doWork(); // "paid" 109 | ``` 110 | 111 | We have overwritten the `doWork()` method. We can also overwrite the `toString()` method: 112 | 113 | ```JavaScript 114 | class Person { 115 | . 116 | . 117 | . 118 | toString() { 119 | return this.name; 120 | } 121 | } 122 | ``` 123 | 124 | We can cycle through an array of people to see if they're working: 125 | ```JavaScript 126 | let makeEveryoneWork = function(...people) { 127 | var results = []; 128 | for (var i = 0; i < people.length; i++){ 129 | results.push(people[i].doWork()); 130 | }; 131 | return results; 132 | } 133 | 134 | makeEveryoneWork(p1, e1); // ["free", "paid"] 135 | ``` 136 | Note that the `makeEveryoneWork` function doesn't care if someone is a person or an employee, because both have a `doWork` method. To adjust to code to have a check that the object passed in has a `doWork` function, use: 137 | ```JavaScript 138 | if(people[i].doWork) { ... } 139 | ``` 140 | or 141 | 142 | ```JavaScript 143 | if(people[i] instanceof Person) { ... } 144 | ``` 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /03 Functional JS/01_Arrow_Functions.md: -------------------------------------------------------------------------------- 1 | # Arrow Functions 2 | 3 | Arrow functions introduce a concise syntax for defining a function. 4 | 5 | Example: 6 | ```JavaScript 7 | let add = (x,y) => x + y; 8 | add(3,5); // 8 9 | ``` 10 | 11 | On the left hand side of the arrow (`=>`) are the parameters, and on the right hand side is the function body. There is no need for braces or a return statement. 12 | 13 | Example: 14 | ```JavaScript 15 | let add = (x,y) => x + y; 16 | let square = x => x * x; 17 | 18 | square(add(2,3)); // 25 19 | ``` 20 | 21 | In order to write an arrow function that takes no parameters, you must include empty parenthesis: 22 | ```JavaScript 23 | let three = () => 3; 24 | ``` 25 | 26 | If you need multiple lines of code for an arrow function, you must use braces and a return statement (but this isn't really best practice for an arrow function): 27 | ```JavaScript 28 | let add = (x,y) => { 29 | let temp = x + y; 30 | return temp; 31 | }; 32 | ``` 33 | 34 | Arrow functions can be used as parameters to other functions. 35 | Example of adding all numbers in an array: 36 | ```JavaScript 37 | var numbers = [1, 2, 3, 4]; 38 | 39 | var sum = 0; 40 | numbers.forEach(n => sum += n); // 10 41 | ``` 42 | 43 | This code takes each number in the array `numbers`, assigns its value to `n`, then adds it to `sum`. 44 | 45 | Example of creating a new array with each value doubled: 46 | ```JavaScript 47 | var doubled = numbers.map(n => n * 2); 48 | 49 | doubled; //[2, 4, 6, 8] 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /03 Functional JS/02_Arrows_as_Callbacks.md: -------------------------------------------------------------------------------- 1 | # Arrows as Async Callbacks 2 | 3 | One problem with callbacks is managing the `this` reference, since it is established by the context. 4 | To illustrate: 5 | ```JavaScript 6 | e1.doWork() 7 | 8 | .... 9 | 10 | doWork() { 11 | this.name = "Taylor"; // this refers to e1 12 | 13 | ajaxCall("aUrl", function(response) { 14 | // this does not point to e1 15 | this.name = response.data; 16 | }); 17 | } 18 | ``` 19 | 20 | However, Arrow functions always capture the `this` value of the context they are in (it lexically binds to `this`). 21 | 22 | ```JavaScript 23 | this.name = "Taylor"; 24 | 25 | ajaxCall("aUrl", (response) => { 26 | this.name = response.data; 27 | }); 28 | ``` 29 | -------------------------------------------------------------------------------- /03 Functional JS/03_Iterables_and_Iterators.md: -------------------------------------------------------------------------------- 1 | # Iterables and Iterators 2 | 3 | If you have a collection of objects that is iterable, you can call an iterator. For example, an array has a `next()` function. The iterator holds the value and a boolean `done`. 4 | 5 | You have to use the iterator to get the items in the iterable. There is no length attribute of an iterator. 6 | 7 | Let's look at different ways to sum up all the numbers in an array. 8 | 9 | ```JavaScript 10 | let sum = 0; 11 | let numbers = [1, 2, 3, 4]; 12 | 13 | // for loop 14 | sum = 0; 15 | for(let i = 0; i < numbers.length; i++){ 16 | sum += numbers[i]; 17 | } 18 | sum; // 10 19 | 20 | // "for in" 21 | sum = 0; 22 | for(let i in numbers){ 23 | sum += numbers[i]; 24 | } 25 | 26 | // iterator 27 | sum = 0; 28 | let iterator = numbers.values(); // returns an iterator, not an array 29 | let next = iterator.next(); 30 | while(!next.done){ 31 | sum += next.value; 32 | next = iterator.next(); 33 | } 34 | 35 | but there's a new way... 36 | 37 | # for of 38 | Sometimes we only want to iterate values and not worry about indexes or keys: 39 | ```JavaScript 40 | let numbers = [1, 2, 3, 4]; 41 | 42 | for(let i of numbers) { 43 | console.log(i); 44 | } 45 | ``` 46 | 47 | This type of loop works with iterators: 48 | ```JavaScript 49 | 50 | let sum = 0; 51 | let numbers = [1, 2, 3, 4]; 52 | 53 | for(let n of numbers){ 54 | sum += n; 55 | } 56 | ``` 57 | 58 | "for of" gets to an iterator's value without having to use .values() like above because it implements `Symbol.iterator`. 59 | 60 | # Implementing `Symbol.iterator` 61 | 62 | The "hard way": 63 | ```JavaScript 64 | class Company { 65 | constructor() { 66 | this.employees = []; 67 | } 68 | 69 | addEmployees(...names) { 70 | this.employees = this.employees.concat(names); 71 | } 72 | 73 | [Symbol.iterator]() { 74 | return { 75 | next() { 76 | ... // This is the hard way 77 | } 78 | } 79 | } 80 | 81 | let count = 0; 82 | let company = new Company(); 83 | company.addEmployees("Tim", "Sue", "Joy", "Tom"); 84 | 85 | for(let employee of company) { 86 | count += 1; 87 | } 88 | } 89 | ``` 90 | 91 | It is better to implement the iterator with a class (but still not the easiest way!): 92 | ```JavaScript 93 | class Company { 94 | 95 | constructor() { 96 | this.employees = []; 97 | } 98 | 99 | addEmployees(...names) { 100 | this.employees = this.employees.concat(names); 101 | } 102 | 103 | [Symbol.iterator]() { 104 | return new ArrayIterator(this.employees); 105 | } 106 | } 107 | 108 | class ArrayIterator { 109 | constructor(array) { 110 | this.array = array; 111 | this.index = 0; 112 | } 113 | 114 | next() { 115 | var result = { value: undefined, done: true }; 116 | 117 | if(this.index < this.array.length) { 118 | result.value = this.array[this.index]; 119 | result.done = false; 120 | this.index += 1; 121 | } 122 | return result; 123 | } 124 | } 125 | ``` 126 | 127 | What's good about implementing an iterator like this is that it allows users to go through our employees without giving them access to the underlying data structure. 128 | 129 | However, the better way to implement an iterator is by using a **Generator**. 130 | 131 | -------------------------------------------------------------------------------- /03 Functional JS/04_Generators.md: -------------------------------------------------------------------------------- 1 | # Generators 2 | 3 | A generator is a function that generates an iterator. It uses `function*()` and the `yield` keyword: 4 | 5 | ```JavaScript 6 | let numbers = function*() { 7 | yield 1; 8 | yield 2; 9 | yield 3; 10 | yield 4; 11 | }; 12 | ``` 13 | 14 | `yield` returns multiple values (instead of `return`ing just one). You can `yield` multiple values because the generator is a factory for iterators: 15 | 16 | ```JavaScript 17 | let sum = 0; 18 | let iterator = numbers(); 19 | let next = iterator.next(); 20 | while(!next.done){ 21 | sum += next.value; 22 | next = iterator.next(); 23 | } 24 | 25 | sum; // 10 26 | ``` 27 | 28 | Each time a generator uses `yield`, it yields the thread of execution back to the caller. The value is returned immediately, the caller can do whatever it needs to do, and then when the caller asks for the next value, the generator picks up where it left off. 29 | 30 | #### Using a `for of` with a Generator 31 | 32 | ```JavaScript 33 | let numbers = function*(start, end) { 34 | for(let i = start; i <= end; i++) { 35 | console.log(i); 36 | yield i; 37 | } 38 | }; 39 | 40 | let sum = 0; 41 | 42 | for(let n of numbers(1,5)){ 43 | sum += n; 44 | console.log("next"); 45 | } 46 | 47 | sum; // 15 48 | ``` 49 | 50 | # Replacing the iterator we built in the Company example 51 | 52 | We will create a filter generator function to look for employees with a "T" in their name: 53 | 54 | ```JavaScript 55 | class Company { 56 | constructor() { 57 | this.employees = []; 58 | } 59 | 60 | addEmployees(...names) { 61 | this.employees = this.employees.concat(names); 62 | } 63 | 64 | *[Symbol.iterator]() { 65 | for(let e of this.employees) { 66 | console.log(e); 67 | yield e; 68 | } 69 | } 70 | } 71 | 72 | let filter = function*(items, predicate) { 73 | for(let item of items) { 74 | console.log("filter", item); 75 | 76 | // Only process items that match the filter 77 | if(predicate(item)) { 78 | yield item; 79 | } 80 | } 81 | } 82 | 83 | // Function that only matches a set number without iterating the entire collection 84 | // (e.g. we only want 'x employees out of everyone') 85 | let take = function*(items, number) { 86 | let count = 0; 87 | if(number < 1) return; // Inside a generator, hitting a `return` sets the `done` flag to `true` 88 | 89 | for(let item of items) { 90 | console.log("take", item); 91 | yield item; 92 | count += 1; 93 | if(count >= number) { 94 | return; 95 | } 96 | } 97 | } 98 | 99 | let count = 0; 100 | let company = new Company(); 101 | company.addEmployees("Tim", "Sue", "Joy", "Tom"); 102 | 103 | // Pass the filter function our company and an arrow function 104 | // to look for names that start with T 105 | for(let employee of filter(company, e => e[0] == 'T')) { 106 | count += 1; 107 | } 108 | 109 | count; // 2 (Tim and Tom are the only employees starting with T) 110 | 111 | // "take" only one employee whose name starts with T 112 | for(let employee of take(filter(company, e => e[0] == 'T'), 1)) { 113 | count += 1; 114 | } 115 | 116 | ``` 117 | 118 | 119 | -------------------------------------------------------------------------------- /03 Functional JS/05_Generator_Range_Function_Example.md: -------------------------------------------------------------------------------- 1 | # Range Function Example 2 | 3 | ```JavaScript 4 | let range = function*(start, end) { 5 | let current = start; 6 | while(current <= end) { 7 | let delta = yield current; 8 | current += delta || 1; 9 | } 10 | } 11 | 12 | 13 | // This is not a generator function, but returns an object that can be used as an iterator:: 14 | let range2 = function(start, end) { 15 | let current = start; 16 | let first = true; 17 | return { 18 | next(delta = 1) { 19 | let result = { value: undefined, done: true }; 20 | 21 | if(!first) { 22 | current += delta; 23 | } 24 | 25 | if(current <= end) { 26 | result.value = current; 27 | result.done = false; 28 | } 29 | first = false; 30 | return result; 31 | } 32 | } 33 | } 34 | 35 | let result = []; 36 | let iterator = range(1, 10); 37 | let next = iterator.next(); 38 | while(!next.done) { 39 | result.push(next.value); 40 | next = iterator.next(2); 41 | } 42 | 43 | result; // [1, 3, 5, 7, 9] 44 | ``` 45 | 46 | 47 | -------------------------------------------------------------------------------- /03 Functional JS/06_Comprehensions.md: -------------------------------------------------------------------------------- 1 | # Comprehensions 2 | 3 | Comprehensions are a terse syntax for building arrays and generators, similar to what's found in Python. 4 | 5 | #### Array Comprehension 6 | We know we are building an array from the square brackets. 7 | 8 | ```JavaScript 9 | var numbers = [for (n of [1, 2, 3]) n * n]; 10 | numbers; // [1, 4, 9] 11 | ``` 12 | 13 | This comprehension is the same as: 14 | ```JavaScript 15 | let numbers = []; 16 | for(let n of [1, 2, 3]) { 17 | numbers.push(n * n); 18 | } 19 | numbers; // [1, 4, 9] 20 | ``` 21 | 22 | We can also use a filter predicate to look for numbers greater than 1: 23 | ```JavaScript 24 | var numbers = [for (n of [1, 2, 3]) if (n > 1) n * n]; 25 | ``` 26 | 27 | Using this syntax, an array will be yielded. In order to yield the members of the array, a `*` is used. 28 | Example: 29 | ```JavaScript 30 | yield [for (item of items) item] // is like `yield ["Tim", "Sue", "Joy", "Tom"]` 31 | 32 | yield* [for (item of items) item] // is like `yield "Tim"; yield "Sue"; yield "Joy"; yield "Tom"` 33 | ``` 34 | 35 | Array comprehensions are a greedy syntax. In the Company example, we had a filter that would filter the list using a predicate. Rewriting it using an array comprehension: 36 | 37 | ```JavaScript 38 | let filter = function*(items, predicate) { 39 | yield* [for (item of items) if(predicate(item)) item]; 40 | } 41 | ``` 42 | 43 | The problem with this is that the entire array is iterated before returning just the desired item. In order to stop iterating as soon as we've "taken" what we wanted, use a generator comprehension: 44 | 45 | 46 | #### Generator Comprehension 47 | Use `()` instead of `[]` 48 | ```JavaScript 49 | var numbers = (for (n of [1, 2, 3]) n * n); 50 | ``` 51 | 52 | So to rewrite our filter function to stop iterating when we've got what we wanted (using the `take` function in the Company example): 53 | ```JavaScript 54 | yield* (for (item of items) if(predicate(item)) item); 55 | ``` 56 | 57 | Essentially, use the Generator Comprehension when you want to be lazy and do as little work as possible, or use the Array Comprehension when you want to have everything in a data structure from the start. 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /04 Built-In Objects/01_Arrays.md: -------------------------------------------------------------------------------- 1 | # New `Array` Changes 2 | 3 | 4 | -------------------------------------------------------------------------------- /04 Built-In Objects/01_Numbers.md: -------------------------------------------------------------------------------- 1 | # New `Array` Changes 2 | 3 | ES6 has added several new functions to Array instances. 4 | 5 | #### `find` 6 | Find the first member whose value is greater than 8: 7 | 8 | ```JavaScript 9 | var ary = [1, 5, 10]; 10 | var match = ary.find(item => item > 8); 11 | match; // 10 12 | ``` 13 | 14 | 15 | #### `findIndex` 16 | Returns the index of the first match: 17 | ```JavaScript 18 | var ary = [1, 5, 10]; 19 | var match = ary.findIndex(item => item > 3); 20 | match; // 1 21 | ``` 22 | 23 | #### `fill` 24 | The `fill` function will fill the entire array with what you supply: 25 | ```JavaScript 26 | var ary = [1, 2, 3, 4, 5]; 27 | ary.fill('a'); 28 | ary; // ['a', 'a', 'a', 'a', 'a'] 29 | ``` 30 | 31 | You can also supply a start and end index to the call: 32 | 33 | `ary.fill('a', 2, 3) // would change only index 2 to 'a'` 34 | 35 | 36 | #### `copyWithin` 37 | Copies portions of existing array on top itself in a different location. 38 | 39 | ```JavaScript 40 | var ary = [1, 2, 3, 4]; // we want [1, 2, 1, 2] 41 | 42 | // First parameter as the target (index 2) 43 | // Second parameter is where you copy from (index 0) 44 | // A third parameter would be the stopping point. 45 | ary.copyWithin(2, 0); // [1, 2, 1, 2] 46 | ary.copyWithin(2, 0, 1); // [1, 2, 1, 4] 47 | ary.copyWithin(2, 0, 2); // [1, 2, 1, 2] 48 | 49 | // Can also use negative indexes: 50 | ary.copyWithin(0, -2); // [3, 4, 3, 4] 51 | ``` 52 | 53 | #### `of` 54 | This is a way to create an array: 55 | ```JavaScript 56 | var ary = new Array(3); //[, , ] 57 | var ofAry = Array.of(3); // [3] 58 | 59 | ary.length; // 3 60 | ofAry.length; // 1 61 | ``` 62 | 63 | #### `from` 64 | Creates an array when given an array-like object. 65 | Example grabbing all `div`s in an HTML document: 66 | ```JavaScript 67 | var arrayLike = document.querySelectorAll('div'); 68 | // arrayLike.forEach is undefined 69 | 70 | var fromArray = Array.from(arrayLike); 71 | ``` 72 | 73 | #### `entries` and `keys` 74 | Returns entries from the entries function 75 | ```JavaScript 76 | var a = ['Joe', 'Jim', 'John']; 77 | var entries = a.entries(); 78 | 79 | var firstEntry = entries.next().value; 80 | firstEntry[0]; // 0 (the index) 81 | firstEntry[1]; // 'Joe' (the value) 82 | 83 | // `keys` are the indexes 84 | var firstKey = keys.next().value; 85 | firstKey; // 0 86 | ``` 87 | 88 | # Array Comprehensions 89 | Create a list based on an original list. This is easier to read than `map`. 90 | 91 | ```JavaScript 92 | var ary = [for (i of [1, 2, 3]) i]; // [1, 2, 3] 93 | 94 | var ary2 = [for (i of [1, 2, 3]) i * i]; // [1, 4, 9] 95 | 96 | var ary3 = [for (i of [1, 2, 3]) if (i < 3 ) i]; // [1, 2] 97 | 98 | // Cartesian product: 99 | var ary4 = [for (first of ['William', 'John', 'Blake']) 100 | for (middle of ['Robert', 'Andrew', 'John']) 101 | if(first != middle) (first + ' ' + middle + ' Smith')] 102 | 103 | ary4; // ["William Robert Smith", "William Andrew Smith" ...] 104 | ``` 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /04 Built-In Objects/02_Sets.md: -------------------------------------------------------------------------------- 1 | # Set 2 | Sets are a new ES6 object that are unique lists. 3 | 4 | ```JavaScript 5 | var set = new Set(); 6 | 7 | set.size; // 0 8 | 9 | set.add("somevalue"); 10 | set.size; // 1 11 | ``` 12 | 13 | Sets convert everything using the `toString`. Sets can be initialized with an Array: 14 | ```JavaScript 15 | var set = new Set([1, 2, 3]); 16 | set.has(1); //true 17 | ``` 18 | 19 | `set.clear();` will empty the set. 20 | 21 | `set.delete(item);` will delete the item: 22 | 23 | ```JavaScript 24 | var set = new Set(); 25 | set.add(1); 26 | set.add(2); 27 | set.delete(1); 28 | set.size; // 1 29 | ``` 30 | 31 | Sets can be iterated with a `forEach`, and will be iterated in insertion order. However, if order is really important, use an Array. `for of` can also be used: 32 | ```JavaScript 33 | var set = new Set(); 34 | set.add(['Tom']); 35 | set.add(['Dick']); 36 | set.add(['Harry']); 37 | 38 | var iterCount = 0; 39 | set.forEach(item => iterationCount++); 40 | iterationCount; // 3 41 | 42 | var set = new Set([1, 2, 3]); 43 | for (var item of set) { 44 | iterationCount++; 45 | } 46 | iterationCount; // 3 47 | ``` 48 | 49 | 50 | -------------------------------------------------------------------------------- /04 Built-In Objects/02_Sets_and_Maps.md: -------------------------------------------------------------------------------- 1 | # Set 2 | Sets are a new ES6 object that are unique lists. 3 | 4 | ```JavaScript 5 | var set = new Set(); 6 | 7 | set.size; // 0 8 | 9 | set.add("somevalue"); 10 | set.size; // 1 11 | ``` 12 | 13 | Sets convert everything using the `toString`. Sets can be initialized with an Array: 14 | ```JavaScript 15 | var set = new Set([1, 2, 3]); 16 | set.has(1); //true 17 | ``` 18 | 19 | `set.clear();` will empty the set. 20 | 21 | `set.delete(item);` will delete the item: 22 | 23 | ```JavaScript 24 | var set = new Set(); 25 | set.add(1); 26 | set.add(2); 27 | set.delete(1); 28 | set.size; // 1 29 | ``` 30 | 31 | Sets can be iterated with a `forEach`, and will be iterated in insertion order. However, if order is really important, use an Array. `for of` can also be used: 32 | ```JavaScript 33 | var set = new Set(); 34 | set.add(['Tom']); 35 | set.add(['Dick']); 36 | set.add(['Harry']); 37 | 38 | var iterCount = 0; 39 | set.forEach(item => iterationCount++); 40 | iterationCount; // 3 41 | 42 | var set = new Set([1, 2, 3]); 43 | for (var item of set) { 44 | iterationCount++; 45 | } 46 | iterationCount; // 3 47 | ``` 48 | 49 | 50 | -------------------------------------------------------------------------------- /04 Built-In Objects/03_Maps.md: -------------------------------------------------------------------------------- 1 | # Maps 2 | Maps are collections of key-value pairs. 3 | 4 | ```JavaScript 5 | var map = new Map(); 6 | 7 | map.set("age", 35); 8 | map.size; // 1 9 | map.get("age"); // 35 10 | 11 | 12 | var ageMap = new Map(); 13 | var ralph = {'name': 'Ralph'}; 14 | ageMap.set(ralph, 29); 15 | ageMap.get(ralph); // 29 16 | 17 | 18 | vap map = new Map([['name', 'John'], ['age', 15], ['weight', 155]]); 19 | map.size; // 3 20 | map.has('age'); // true 21 | 22 | // Maps do not allow duplicate keys: 23 | var map = new Map(); 24 | var key = {}; 25 | map.set(key, 'first'); 26 | map.set(key, 'second'); 27 | map.get(key); // 'second' 28 | ``` 29 | 30 | Maps have `clear` and `delete` like Sets. 31 | 32 | Iterate Maps: 33 | ```JavaScript 34 | vap map = new Map([['name', 'John'], ['age', 15], ['weight', 155]]); 35 | var iterationCount = 0; 36 | 37 | map.forEach(function(value, key) { 38 | iterationCount++; 39 | // use value & key 40 | }); 41 | 42 | // Now with `for of` 43 | for(var [key, value] of map) { 44 | // the key and the value are their own variables 45 | iterationCount++; 46 | } 47 | ``` 48 | 49 | 50 | Maps return an iterator of arrays of key-value pairs when `entries` is called: 51 | ```JavaScript 52 | var map = new Map(); 53 | map.set('name', 'Joe'); 54 | var items = map.entries(); 55 | var first = items.next().value; 56 | first[0]; // 'name' 57 | first[1]; // 'Joe' 58 | ``` 59 | 60 | An iterator of values is returned when calling `values`, and iterator of keys when calling `keys`: 61 | ```JavaScript 62 | var map = new Map(); 63 | map.set(1, 'a'); 64 | var values = map.values(); 65 | var first = values.next().value; 66 | first; // 'a' 67 | 68 | var keys = map.keys(); 69 | var firstKey = keys.next().value; 70 | firstKey; // 1 71 | ``` 72 | 73 | Maps can be constructed with an iterator. 74 | -------------------------------------------------------------------------------- /04 Built-In Objects/04_WeakMap_and_WeakSet.md: -------------------------------------------------------------------------------- 1 | # WeakMap & WeakSet 2 | In a normal Map and Set, pointers to objects are strong. If the object being pointed to is deleted, the Map/Set retains its pointer so the object can't be garbage collected. This is a problem for DOM nodes for example. 3 | 4 | Because the garbage collector can happen at any time (and thus could remove items from a `WeakSet` at any time), all the functionality that deals with sets as a whole has been removed from the `WeakSet`. 5 | 6 | For example, there is no `size` property, `forEach`, etc. 7 | 8 | Other than that, they function the same: `has`, `delete`, `clear`, etc. 9 | 10 | `WeakMap`s are like `WeakSet`s, but we can do `has`, `get`, `delete`, `clear`, etc. 11 | 12 | Since you can't check if a `WeakMap` or `WeakSet` are empty, you can just check if a specific member is gone. 13 | 14 | 15 | -------------------------------------------------------------------------------- /05 Asynchronous Development/01_Promise_Primer.md: -------------------------------------------------------------------------------- 1 | # Callbacks 2 | 3 | The most basic form of async programming in JS is the callback. 4 | The caller spawns an asynchronous operation. When the caller initiates this process, it passes the process to a callback, trusting that when the process finishes, it will invoke the callback. 5 | When the async process finishes, the callback is put onto the call stack, and will execute when all the other processes ahead of it are complete. 6 | 7 | There are some problems: 8 | * Only the caller can be notified that the async process has completed. 9 | * No other interested parties can act, unless the callback informs them. 10 | * Error handling is difficult. 11 | * Handling multiple callback processes at once is hard. 12 | * The callback is responsible for multiple things 13 | * Processing the async call 14 | * Starting other processes that want to execute 15 | 16 | Callback Example: 17 | ```JavaScript 18 | function getCompanyFromOrderId(orderId) { 19 | getOrder(orderId, function(order) { 20 | getUser(order.userId, function(user) { 21 | getCompany(user.companyId, function(company) { 22 | // do something with company 23 | }); 24 | }); 25 | }); 26 | } 27 | ``` 28 | In this example, first we get the order, then when that's complete we get the user, then when that's complete we can get the company. 29 | 30 | Notice that each callback has to process data by calling the next function, but also pass the appropriate callback into the next function. 31 | 32 | When we add in exception handling, it's even worse: 33 | ```JavaScript 34 | function getCompanyFromOrderId(orderId) { 35 | try { 36 | getOrder(orderId, function(order) { 37 | try { 38 | getUser(order.userId, function(user){ 39 | try { 40 | getCompany(user.companyId, function(company) { 41 | try { 42 | // do something with company 43 | } catch(ex) { 44 | // handle exception 45 | } 46 | }); 47 | } catch(ex) { 48 | // handle exception 49 | } 50 | }); 51 | } catch(ex) { 52 | // handle exception 53 | } 54 | }); 55 | } catch(ex) { 56 | // handle exception 57 | } 58 | } 59 | ``` 60 | 61 | So what's the solution? 62 | 63 | # Promises 64 | Promises are what save us from "Callback Hell". A promise is an object which represents a handle to listen to the results of an async operation, whether it succeeds or fails. The promise "promises" to alert you when the async operation is done, and give you the results of that operation. 65 | 66 | Promises are composable, which means you can take two promises that represent two different async operations, and chain them together so one happens after the other, or wait for them both to run and do a new operation when both are complete. You can make other promises that depend on the results of a previous promise, and succeeds when it succeeds, or fails when it fails. 67 | 68 | A promise is made up of two parts: 69 | 70 | **The Control of a Promise** 71 | * Also called "the deferred". 72 | * May be a separate object, or a callback. 73 | * Gives the creator of the promise the ability to mark the promise as "succeeded" or "failed". 74 | 75 | **The Promise itself** 76 | * Object can be passed around 77 | * Enables interested parties to register actions to take when the async operation completes. 78 | * Flow control no longer responsibility of the handler 79 | * Error handling is much easier 80 | 81 |   82 | 83 | A promise exists in one of three states: 84 | 1. Pending (Not yet completed) 85 | 2. Fulfilled (Completed sucessfully) 86 | 3. Rejected (Failed) 87 | 88 | _Note: the "rejected" state means we are no longer dealing with exception handling. We need to let other functions know that the promise failed so they have to do something._ 89 | 90 | Same example as the Callback + Error Handling, but with Promises: 91 | ```JavaScript 92 | function getCompanyFromOrderId(orderId){ 93 | getOrder(orderId).then(function(order) { 94 | return getUser(order.userId); 95 | }).then(function(user) { 96 | return getCompany(user.companyId); 97 | }).then(function(company) { 98 | // do something with company 99 | }).then(undefined, function(error) { 100 | // handle error 101 | }) 102 | } 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /05 Asynchronous Development/02_Promise_Basics.md: -------------------------------------------------------------------------------- 1 | # Promise Basics 2 | _Note: This code is originally used in Jasmine tests_ 3 | 4 | Resolve: 5 | ```JavaScript 6 | var promise = new Promise(function(resolve, reject){ 7 | resolve(1); 8 | }); 9 | 10 | promise.then(function(data) { 11 | console.log(data); // 1 12 | done(); 13 | }); 14 | ``` 15 | 16 | Reject: 17 | ```JavaScript 18 | var promise = new Promise(function(resolve, reject){ 19 | reject(Error('I am Error')); 20 | }); 21 | 22 | promise.then(function() { 23 | // success 24 | }, function(error) { 25 | console.log(error.message); // 'I am Error' 26 | }); 27 | ``` 28 | 29 | There is a shorthand if all you care about is a failure: 30 | ```JavaScript 31 | var promise = new Promise(function(resolve, reject){ 32 | reject(Error('I am Error')); 33 | }); 34 | 35 | promise.catch(function(error) { 36 | console.log(error.message); // 'I am Error' 37 | }); 38 | ``` 39 | 40 |   41 | 42 | #### Resolving a Promise with Another Promise 43 | ```JavaScript 44 | var previousPromise = new Promise(function(resolve, reject){ 45 | resolve(3); 46 | }); 47 | 48 | var promise = new Promise(function(resolve, reject) { 49 | resolve( previousPromise ); 50 | }); 51 | 52 | promise 53 | .then( function(data) { 54 | console.log(data); // 3 55 | }) 56 | ``` 57 | -------------------------------------------------------------------------------- /05 Asynchronous Development/03_Advanced_Promises.md: -------------------------------------------------------------------------------- 1 | # Advanced Promises 2 | 3 | Chaining promises to make multiple async calls sequentially: 4 | ```JavaScript 5 | function getOrder(orderId) { 6 | return Promise.resolve({userId: 35}); 7 | } 8 | 9 | function getUser(userId) { 10 | return Promise.resolve({companyId: 18}); 11 | } 12 | 13 | function getCompany(companyId) { 14 | return Promise.resolve({name: 'Pluralsight'}) 15 | } 16 | 17 | 18 | getOrder(3).then(function(order) { 19 | return getUser(order.userId); 20 | }).then(function(user) { 21 | return getCompany(user.companyId); 22 | }).then(function(company) { 23 | company.name; // 'Pluralsight' 24 | }).catch(function(error) { 25 | // handle error 26 | }); 27 | ``` 28 | 29 | #### Execute after all promises have been returned 30 | ```JavaScript 31 | function getCourse(courseId) { 32 | var courses { 33 | 1: {name: 'Intro to Cobol'}, 34 | 2: {name: 'Another class'}, 35 | 3: {name: 'Class 3'} 36 | } 37 | 38 | return Promise.resolve(courses[courseId]); 39 | } 40 | 41 | 42 | var courseIds [1, 2, 3]; 43 | var promises = []; 44 | 45 | for(var i = 0; i < courseIds.length; i++){ 46 | promises.push(getCourse(courseIds[i])); 47 | } 48 | 49 | // 50 | Promise.all(promises).then(function(values) { 51 | values.length; // 3 52 | }) 53 | ``` 54 | 55 | #### How to make a promise resolve after the very first of a set of promises resolves: 56 | If you have several async processes going on and you want something to happen after the first one resolves, then you would use the `race` function. 57 | ```JavaScript 58 | 59 | var courseIds [1, 2, 3]; 60 | var promises = []; 61 | 62 | for(var i = 0; i < courseIds.length; i++){ 63 | promises.push(getCourse(courseIds[i])); 64 | } 65 | 66 | Promise.race(promises).then(function(firstValue) { 67 | firstValue.name; // should be defined, but we don't know what order the promises resolve in. 68 | }) 69 | 70 | ``` 71 | -------------------------------------------------------------------------------- /05 Asynchronous Development/04_Basic_Async_Generators.md: -------------------------------------------------------------------------------- 1 | # Basic Asynchronous Generators 2 | 3 | We can use generators to make our async code more readable. 4 | 5 | Say we want to log 3 strings to the console, and between each log we want to pause for half a second. 6 | 7 | **Without Generators** 8 | ```JavaScript 9 | function oldPause(delay, cb) { 10 | setTimeout(function() { 11 | console.log('paused for ' + delay + 'ms'); 12 | cb(); 13 | }, delay); 14 | } 15 | 16 | console.log('start'); 17 | oldPause(500, function() { 18 | console.log('middle'); 19 | oldPause(500, function() { 20 | console.log('end'); 21 | }); 22 | }); 23 | 24 | ``` 25 | 26 | **With Generators** 27 | ```JavaScript 28 | 29 | // IIFE to create variables without polluting the global namespace 30 | (function() { 31 | var sequence; 32 | 33 | var run = function(generator) { 34 | sequence = generator(); 35 | var next = sequence.next(); 36 | } 37 | 38 | // Notify that the pause function has completed 39 | var resume = function() { 40 | sequence.next(); 41 | } 42 | 43 | // Put it all inside an object that can be accessed anywhere 44 | window.async = { 45 | run: run, 46 | resume: resume 47 | } 48 | }()); 49 | 50 | function pause(delay) { 51 | setTimeout(function() { 52 | console.log('paused for ' + delay + 'ms'); 53 | async.resume(); 54 | }, delay); 55 | } 56 | 57 | function* main() { 58 | console.log('start'); 59 | yield pause(500); 60 | console.log('middle'); 61 | yield pause(500); 62 | console.log('end'); 63 | } 64 | 65 | async.run(main); 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /05 Asynchronous Development/05_More_Async_Generators.md: -------------------------------------------------------------------------------- 1 | # More Async Generators 2 | 3 | Imagine we are writing a program that will conduct stock trades. 4 | 5 | ```JavaScript 6 | // IIFE to create variables without polluting the global namespace 7 | (function() { 8 | var sequence; 9 | 10 | var run = function(generator) { 11 | sequence = generator(); 12 | var next = sequence.next(); 13 | } 14 | 15 | // Notify that the pause function has completed 16 | // Any caller to 17 | var resume = function(value) { 18 | sequence.next(value); 19 | } 20 | 21 | // Put it all inside an object that can be accessed anywhere 22 | window.async = { 23 | run: run, 24 | resume: resume 25 | } 26 | }()); 27 | 28 | function getStockPrice() { 29 | // $.get('/prices', function(prices) { 30 | // mimic jQuery's get request 31 | // }); 32 | 33 | setTimeout(function() { 34 | async.resume(50); 35 | }, 300); 36 | } 37 | 38 | function executeTrade() { 39 | setTimeout(function() { 40 | console.log('trade completed'); 41 | async.resume(); 42 | }, 300); 43 | } 44 | 45 | function* main() { 46 | // when this function calls sequence.next, 47 | // we're going to pass in the result of the yield statement 48 | // which will be the price variable. 49 | var price = yield getStockPrice(); 50 | 51 | if(price > 45) { 52 | yield executeTrade(); 53 | } else { 54 | console.log('trade not made'); 55 | } 56 | } 57 | 58 | async.run(main); 59 | ``` 60 | 61 | 62 | -------------------------------------------------------------------------------- /05 Asynchronous Development/06_Async_Generators_and_Promises.md: -------------------------------------------------------------------------------- 1 | # Asynchronous Generators and Promises 2 | 3 | Let's pair up our async generators with promises. 4 | 5 | One of the downsides to the async generators we've been working with is that they must be aware that they're being called from within an async generator, so they have to be able to call `async.resume()` or `async.fail()`. 6 | 7 | ```JavaScript 8 | (function() { 9 | var run = function(generator) { 10 | var sequence; 11 | var process = function(result) { 12 | // parameter receives the value that the promise resolves with 13 | result.value.then(function(value) { 14 | if(!result.done) { 15 | process(sequence.next(value)) 16 | } 17 | }, function(error) { 18 | if(!result.done) { 19 | process(sequence.throw(error)); 20 | } 21 | }) 22 | } 23 | 24 | sequence = generator(); 25 | var next = sequence.next(); 26 | process(next); 27 | } 28 | 29 | 30 | // Because we are going to use promises, 31 | // the promises themselves will be the notifications to `asyncP` 32 | // as to whether they are done or failed. 33 | window.asyncP = { 34 | run: run, 35 | } 36 | }()); 37 | 38 | function getStockPriceP() { 39 | return new Promise(function(resolve, reject) { 40 | setTimeout(function() { 41 | resolve(50); 42 | }, 300); 43 | }); 44 | } 45 | 46 | function executeTradeP() { 47 | return new Promise(function(resolve, reject){ 48 | setTimeout(function() { 49 | resolve(); 50 | }, 300); 51 | }); 52 | } 53 | 54 | function* main() { 55 | try { 56 | var price = yield getStockPriceP(); 57 | if(price > 45) { 58 | yield executeTradeP(); 59 | } else { 60 | console.log('trade not made'); 61 | } 62 | } catch(ex) { 63 | console.log('error! ' + ex.message); 64 | } 65 | done(); 66 | } 67 | 68 | asyncP.run(main); 69 | ``` 70 | -------------------------------------------------------------------------------- /06 Objects in ES6/01_Object_is_and_Object_assign.md: -------------------------------------------------------------------------------- 1 | # Object.is() 2 | 3 | The `is` function is similar to `===` but has some added features. 4 | 5 | Call `Object.is()` and pass in two parameters. If they are the same, then `true` will be returned. 6 | In almost every case, it works the same as `===` but there are some differences: 7 | * Comparing `0` to `-0` with `===` returns `true`, but `false` with `Object.is()` 8 | * `NaN === NaN` returns `false`. `Object.is(NaN, NaN)` returns `true` 9 | 10 |   11 | 12 | # Object.assign() 13 | 14 | The `Object.assign()` allows you to take properties from one object and copy it onto another object. This is similar to `extend` in jQuery and Underscore. 15 | 16 | In other languages, this functionality is described as "mixins". 17 | 18 | Example: 19 | ```JavaScript 20 | var shark = { 21 | bite: function(target) { 22 | target.hurt = true; 23 | } 24 | } 25 | 26 | var person = {} 27 | 28 | var laser = { 29 | pewpew: function(target) { 30 | target.exploded = true; 31 | } 32 | } 33 | 34 | Object.assign(shark, laser); // Multiple mixins would be added as parameters 35 | 36 | shark.pewpew(person); 37 | person.exploded; // true 38 | 39 | ``` 40 | 41 | Note that there isn't any type of forwarding going on-- the `shark` object has its own `pewpew()` function. 42 | -------------------------------------------------------------------------------- /06 Objects in ES6/02_Object_Shorthand_and_Computed_Properties.md: -------------------------------------------------------------------------------- 1 | # Property Initializer Shorthand 2 | 3 | Example: 4 | ```JavaScript 5 | var model = 'Ford'; 6 | var year = 1969; 7 | 8 | // Old way 9 | var Classic = { 10 | model: model, 11 | year: year // { model: 'Ford', year: 1969 } 12 | } 13 | 14 | // ES6 way 15 | var Classic = { 16 | model, year // {model: 'Ford', year: 1969 } 17 | } 18 | ``` 19 | 20 | # Method Initializer Shorthand 21 | 22 | Create methods on objects using a shorthand. 23 | 24 | ```JavaScript 25 | var server = { 26 | 27 | // Old way 28 | getPort: function() { 29 | // stuff 30 | } 31 | 32 | // ES6 way 33 | getPort() { 34 | //stuff 35 | } 36 | } 37 | ``` 38 | 39 | # Computed Property Names 40 | 41 | ```JavaScript 42 | // Old way 43 | function createSimpleObject(propName, propVal) { 44 | var obj = {}; 45 | obj[propName] = propVal; 46 | return obj; 47 | } 48 | 49 | // New way 50 | function createSimpleObject(propName, propVal) { 51 | return { 52 | [propName]: propVal 53 | } 54 | } 55 | ``` 56 | 57 | Being able to use expressions like this for property name also allows us to do string concatenation: 58 | ```JavaScript 59 | function createTriumvirate(first, second, third) { 60 | return { 61 | ['member_' + first.name]: first, 62 | ['member_' + second.name]: second, 63 | ['member_' + third.name]: third 64 | } 65 | } 66 | 67 | var Joe = {name: 'Joe'} 68 | var Ralph = {name: 'Ralph'} 69 | var Harry = {name: 'Harry'} 70 | 71 | var tri = createTriumvirate(Joe, Ralph, Hary) 72 | 73 | tri.member_Joe; // Joe (the person object) 74 | 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /06 Objects in ES6/03_Proxies.md: -------------------------------------------------------------------------------- 1 | # Proxies 2 | 3 | Proxies allow us to intercept operations done on objects. The most straightforward operations to intercept are `set` and `get`. 4 | 5 | ### Intercepting `get` 6 | ```JavaScript 7 | var unicorn = { 8 | legs: 4, 9 | color: 'brown', 10 | horn: true 11 | } 12 | 13 | ``` 14 | 15 | When we use a proxy object, we aren't modifying the original object, but are instead creating a new object that is a wrapper around the original object. If you are going to use proxies, you have to access to the original object through the proxy. 16 | 17 | Say we want to make it so when someone gets the color of a unicorn, it says "awesome" in front. 18 | 19 | ```JavaScript 20 | // Create a proxy object by passing the target object and an object literal of the operation you want to intercept. 21 | var proxyUnicorn = new Proxy(unicorn, { 22 | get: function(target, property) { 23 | if(property === 'color') { 24 | return 'awesome ' + target[property]; 25 | } else { 26 | return target[property]; 27 | } 28 | } 29 | }) 30 | ``` 31 | 32 | 33 | ### Intercepting `set` 34 | ```JavaScript 35 | var unicorn = { 36 | legs: 4, 37 | color: 'brown' 38 | horn: true 39 | } 40 | 41 | // Disallow the 'horn' from being set to `false` 42 | var proxyUnicorn = new Proxy(unicorn, { 43 | set: function(target, property, value) { 44 | if (property === 'horn' && value === false) { 45 | console.log('Unicorn will never be hornless!'); 46 | } else { 47 | target[property] = value; 48 | } 49 | } 50 | }) 51 | ``` 52 | 53 | 54 | In addition to `get` and `set`, you can intercept calls to `delete`, `define`, `freeze`, `in`, `has`, etc. You can intercept multiple calls by adding to the handler: 55 | 56 | ```JavaScript 57 | set: function (...), 58 | get: function (...) 59 | ``` 60 | -------------------------------------------------------------------------------- /06 Objects in ES6/04_Proxying_Functions.md: -------------------------------------------------------------------------------- 1 | # Proxying Functions 2 | 3 | ```JavaScript 4 | var unicorn = { 5 | legs: 4, 6 | color: 'brown', 7 | horn: true 8 | hornAttack: function(target) { 9 | return target.name + ' was obliterated!'; 10 | } 11 | } 12 | 13 | // The thief can steal methods from others and use them as his own 14 | var thief = { name: 'Rupert' } 15 | thief.attack = unicorn.hornAttack; 16 | thief.attack(); // the hornAttack method belongs to the thief now. 17 | ``` 18 | 19 | To prevent method theft, we would create a proxy around the `hornAttack` function to intercept its invocation: 20 | 21 | ```JavaScript 22 | var unicorn = { 23 | legs: 4, 24 | color: 'brown', 25 | horn: true 26 | hornAttack: function(target) { 27 | return target.name + ' was obliterated!'; 28 | } 29 | } 30 | 31 | unicorn.hornAttack = new Proxy(unicorn.hornAttack) { 32 | apply: function(target, context, args) { 33 | if(context !== unicorn) { 34 | return "Only unicorn can use hornAttack"; 35 | } else { 36 | return target.apply(context, args); 37 | } 38 | } 39 | } 40 | 41 | var thief = { name: 'Rupert' } 42 | thief.attack = unicorn.hornAttack; 43 | thief.attack(); // "Only unicorn can use hornAttack" 44 | unicorn.attack(thief); // "Rupert was obliterated!" 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /07 Modules/01_IIFE.md: -------------------------------------------------------------------------------- 1 | # IIFE Module Pattern 2 | 3 | Use an immediately invoked function expression (IIFE). 4 | 5 | There won't be any global variables defined by this code as long as the `var` keyword is used appropriately. 6 | 7 | You can choose to expose pieces publically if you explicitly do so. 8 | 9 | For example, attaching `target.Employee = Employee` to the `window` of the browser. 10 | 11 | It is easy to have private implementation details like the `privateDoWork` function that is only available inside the IIFE's closure. 12 | 13 | ```JavaScript 14 | (function(target) { 15 | var privateDoWork = function(name) { 16 | return name + " is working"; 17 | }; 18 | 19 | var Employee = function(name) { 20 | this.name = name; 21 | } 22 | 23 | Employee.prototype = { 24 | doWork: function() { 25 | return privateDoWork(this.name); 26 | } 27 | } 28 | 29 | target.Employee = Employee; 30 | }(window)); 31 | ``` 32 | 33 | ## Two Goals of a Module: 34 | 1. Organize code into related concepts and treat everything as a unit 35 | 2. Control visibility and hide implementation details 36 | -------------------------------------------------------------------------------- /07 Modules/02_CommonJS.md: -------------------------------------------------------------------------------- 1 | # CommonJS Modules 2 | 3 | CommonJS revolves primarily around `exports` and `require`. 4 | 5 | There is no IIFE required, because the module system will execute the script in a non-global context. 6 | 7 | The `exports` object is required to make code publicly available. 8 | 9 | Example of the Module: 10 | ```JavaScript 11 | var privateDoWork = function(name) { 12 | return name + " is working"; 13 | } 14 | 15 | var Employee = function(name) { 16 | this.name = name; 17 | } 18 | 19 | Employee.prototype = { 20 | doWork: function() { 21 | return privateDoWork(this.name); 22 | } 23 | } 24 | 25 | exports.Employee = Employee; 26 | 27 | // or if there is more... 28 | // exports.Employee = Employee; 29 | // exports.Another = SomethingElse; 30 | ``` 31 | 32 | Accessing the module: 33 | ```JavaScript 34 | var Employee = require('./Employee').Employee; 35 | 36 | var e1 = new Employee("Taylor"); 37 | console.log(e1.doWork()); // "Taylor is working" 38 | ``` 39 | -------------------------------------------------------------------------------- /07 Modules/03_AMD.md: -------------------------------------------------------------------------------- 1 | # AMD 2 | 3 | While CommonJS focuses on coming up with a standard for running JS outside of a web browser, Asynchronous Module Definition (AMD) takes into account loading scripts asynchronously in a web app, as well as optimizing scripts into fewer and smaller downloads at runtime. 4 | 5 | During development of a webapp, we want to use modules in our code and develop them independently. 6 | 7 | The popular implementation of AMD is provided by RequireJS. It's a script loader that consists primarily of a define function, that contains a callback to be given to the script loader. 8 | 9 | This function needs to return the object you want to be a public member. In this example we only return an Employee, but we could return an object with many attributes to make public. 10 | 11 | This is similar to an IIFE, because our implementation details can be kept private, and we have closure around functions. The primary difference is that an IIFE executes immediately, and AMD executes when the script loader tells it to. 12 | 13 | 14 | ```JavaScript 15 | // employee.js 16 | define(function() { 17 | 18 | var privateDoWork = function() { 19 | // ... 20 | } 21 | 22 | var Employee = function(name) { 23 | // ... 24 | } 25 | 26 | return Employee; 27 | }); 28 | ``` 29 | 30 | In order to use this, define a module with an array of the imports you need to include. 31 | 32 | ```JavaScript 33 | define(["employee"], function(Employee){ 34 | var e = new Employee("Taylor"); 35 | }); 36 | ``` 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /07 Modules/04_ES6_Modules.md: -------------------------------------------------------------------------------- 1 | # ES6 Modules 2 | 3 | There are two reasons we covered CommonJS and AMD. ES6 modules are similar to the other two systems-- we can select things we want to export, and what we want to import into another module. 4 | 5 | Notice the `export` keyword. It is reasonable to have one export per file. 6 | 7 | Example of ES6 Module syntax: 8 | ```JavaScript 9 | export class Employee { 10 | constructor(name) { 11 | this._name = name; 12 | } 13 | 14 | get name() { 15 | return this._name; 16 | } 17 | 18 | doWork { 19 | return `${this._name} is working`; 20 | } 21 | } 22 | ``` 23 | 24 | Importing in another module: 25 | ```JavaScript 26 | import {Employee} from './es6/employee'; 27 | 28 | var e = new Employee("Taylor"); 29 | e.doWork(); 30 | ``` 31 | 32 | _Note we have a destructured import by using `{Employee}`._ 33 | 34 | 35 | What we have is closer to the CommonJS way of doing things. 36 | --------------------------------------------------------------------------------