├── README.md ├── course_logo_udemy.png ├── section1 └── NOTE.md ├── section2 ├── arrays.js ├── booleans_conditionals.js ├── console_operators_variables.js ├── loops_iteration.js ├── objects.js ├── syntax_semicolons_comments.js └── undefined_and_primitives.js ├── section3 ├── arrow_functions.js ├── callbacks.js ├── function_declarations_vs_expressions.js ├── interview_question_callbacks.js ├── interview_question_this_keyword.js ├── methods_this_keyword.js └── more_on_this.js ├── section4 ├── almost_everything_is_an_object.js ├── dynamic_language_types.js ├── equality_type_coercion.js ├── explicit_type_conversion.js ├── interview_question_types.js ├── null.js └── truthy_falsy.js ├── section5 ├── hoisting.js ├── interview_question_scoping_hoisting.js ├── let_const_mutability.js ├── let_const_var_scope.js └── template_literals_ternary_operators.js ├── section6 ├── custom_clonedeep_lodash_clonedeep.js ├── interview_question_clones.js ├── json.parse_json.stringify_attempt_at_deep_cloning.js ├── object.is_references_shallow_clone.js ├── object_methods.js ├── property_shorthand_destructuring_assignment.js └── spread_syntax.js ├── section7 ├── closures.js ├── function_factories.js ├── higher_order_functions.js └── interview_question_closures_higher_order_functions_callbacks.js ├── section8 ├── class_inheritance.js ├── classes.js ├── constructor_functions.js ├── interview_question_prototype_based_inheritance.js ├── object_prototype_proto_prototype_chain.js └── prototypes_and_prototype_based_inheritance.js └── section9 ├── asynchronous_code_with_settimeout.js ├── await_and_async.js ├── fetch_and_promise.js ├── interview_question_asynchronous_code_and_engine.js └── promise_from_scratch.js /README.md: -------------------------------------------------------------------------------- 1 | # Learn JavaScript from Scratch, Get Hired 2 | 3 | ![Course Logo](course_logo_udemy.png) 4 | 5 | This repository accompanies the "Learn JavaScript, Get Hired | The Full Bootcamp JavaScript" course by David Katz. 6 | 7 | ## Take the course here: 8 | [http://udemy.com/learn-javascript-get-hired-bootcamp](http://udemy.com/learn-javascript-get-hired-bootcamp) 9 | 10 | JavaScript is the most in-demand skill. Get an extra advantage with this course's special JS interview question videos. 11 | 12 | In this course you will learn: 13 | * the differences between function declarations, expressions, and arrow functions. 14 | * when the `this` keyword is relevant. 15 | * why and how almost everything is an object. 16 | * a handful of ways to create object clones. 17 | * constructor functions 18 | * asynchronous code. 19 | * a myriad of ways to write clean JavaScript code. 20 | * intricacies of types - coercion, null vs. undefined, truthy, falsy, and more. 21 | * the differences between let, const, and var. 22 | * closures, function factories, and higher-order functions. 23 | * prototypes and the prototype-based inheritance model. 24 | * how to track code through the JavaScript runtime - through the callstack, heap, queue, and event loop. 25 | 26 | *** 27 | 28 | ## Starting point for most videos 29 | 30 | #### Blank JavaScript Repl.it Template 31 | 32 | [https://repl.it/@DavidJoseph3/new-browser-javascript](https://repl.it/@DavidJoseph3/new-browser-javascript) 33 | 34 | *** 35 | 36 | ## Starting points for interview question videos 37 | 38 | #### Interview Question | The this keyword 39 | [https://repl.it/@DavidJoseph3/interview-question-this-keyword](https://repl.it/@DavidJoseph3/interview-question-this-keyword) 40 | 41 | #### Interview Question | Callbacks 42 | [https://repl.it/@DavidJoseph3/interview-question-callbacks](https://repl.it/@DavidJoseph3/interview-question-callbacks) 43 | 44 | #### Interview Question | JavaScript Types 45 | [https://repl.it/@DavidJoseph3/interview-question-types](https://repl.it/@DavidJoseph3/interview-question-types) 46 | 47 | #### Interview Question | Scopes and Hoisting 48 | [https://repl.it/@DavidJoseph3/interview-question-scopes-hoisting](https://repl.it/@DavidJoseph3/interview-question-scopes-hoisting) 49 | 50 | 51 | #### Interview Question | Cloning an Object 52 | [https://repl.it/@DavidJoseph3/interview-question-cloning-an-object](https://repl.it/@DavidJoseph3/interview-question-cloning-an-object) 53 | 54 | #### Interview Question | Closures, Higher-Order Functions, and Callbacks 55 | [https://repl.it/@DavidJoseph3/interview-question-closures-higher-order-functions-callbacks](https://repl.it/@DavidJoseph3/interview-question-closures-higher-order-functions-callbacks) 56 | 57 | 58 | #### Interview Question | Prototype-Based Inheritance 59 | [https://repl.it/@DavidJoseph3/interview-question-prototype-based-inheritance](https://repl.it/@DavidJoseph3/interview-question-prototype-based-inheritance) 60 | 61 | 62 | #### Interview Question | Asynchronous JavaScript 63 | [https://repl.it/@DavidJoseph3/interview-question-asynchronous-code-js-engine](https://repl.it/@DavidJoseph3/interview-question-asynchronous-code-js-engine) 64 | -------------------------------------------------------------------------------- /course_logo_udemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/15Dkatz/javascript-bootcamp/b5cb44a98476142269b84a331e5b9214ab9393e8/course_logo_udemy.png -------------------------------------------------------------------------------- /section1/NOTE.md: -------------------------------------------------------------------------------- 1 | This section doesn't have any coding lectures. 2 | It's one video on JavaScript opportunity and the course information/structure. 3 | -------------------------------------------------------------------------------- /section2/arrays.js: -------------------------------------------------------------------------------- 1 | var things = ['laptop', 'speaker', 13]; 2 | 3 | things.length; 4 | 5 | // Add an item to the array 6 | things.push(17); 7 | 8 | // .splice(n, x, ...) 9 | // Starting from index n, remove x items. 10 | // Add additional arguments to the array 11 | things.splice(1, 0, 'pen', 'paper'); 12 | 13 | -------------------------------------------------------------------------------- /section2/booleans_conditionals.js: -------------------------------------------------------------------------------- 1 | var foo = 10; 2 | 3 | if (foo == 13) { 4 | console.log('hello'); 5 | } else if (foo == 10) { 6 | console.log('greetings'); 7 | } else { 8 | console.log('goodbye'); 9 | } 10 | 11 | switch(foo) { 12 | case 13: 13 | console.log('foo is 13'); 14 | break; 15 | case 10: 16 | console.log('foo is 10'); 17 | break; 18 | default: 19 | console.log('foo is something else'); 20 | } -------------------------------------------------------------------------------- /section2/console_operators_variables.js: -------------------------------------------------------------------------------- 1 | // Numbers 2 | 1, 2, 999 3 | 4 | // Strings 5 | "dog", 'dog' 6 | "cat", 'cat' 7 | // notice that double or single quotes are interchangeable 8 | 9 | // Read-eval-print-loop. Four step program: 10 | // 1. Read user input. 11 | // 2. Evaluate that input. 12 | // 3. Print the result 13 | // 4. Loop and repeat steps 1-3. 14 | -------------------------------------------------------------------------------- /section2/loops_iteration.js: -------------------------------------------------------------------------------- 1 | // for loops. 2 | // Three statements on one line. 3 | for (var i = 0; i < 10; i++) { 4 | console.log('ping', i); 5 | } 6 | 7 | // while loops. 8 | // Execute while a condition is true. 9 | // Like a for loop, except the statements are on individual liens 10 | var j = 0; 11 | 12 | while (j < 10) { 13 | console.log(' pong', j); 14 | 15 | j++; 16 | } 17 | 18 | var n = 15; 19 | 20 | // do while loops 21 | // Like a while loop, except the body executes at least once 22 | do { 23 | console.log('n is', n); 24 | n++; 25 | } while (n < 10); -------------------------------------------------------------------------------- /section2/objects.js: -------------------------------------------------------------------------------- 1 | var lion = { 2 | name: 'simba', 3 | age: 3 4 | }; 5 | 6 | // name and age are properties of the lion object. 7 | 8 | // dot notation. 9 | lion.age; 10 | 11 | // bracket notation. 12 | // This is useful if you're accessing a property through a variable 13 | var prop = 'name'; 14 | lion[prop]; 15 | -------------------------------------------------------------------------------- /section2/syntax_semicolons_comments.js: -------------------------------------------------------------------------------- 1 | // semicolons are optional, but recommended after statements 2 | var foo = 10; 3 | var bar = 17; 4 | 5 | // Comments: 6 | 7 | // single: 8 | // bears, beets, battlestar galactica 9 | 10 | // multiline 11 | /** 12 | * bears, 13 | * beets, 14 | * battlestar galactica 15 | */ 16 | -------------------------------------------------------------------------------- /section2/undefined_and_primitives.js: -------------------------------------------------------------------------------- 1 | var foo; 2 | 3 | foo; // undefined 4 | 5 | console.log('hello'); 6 | 7 | var bar = true; 8 | 9 | // primitives 10 | // numbers, strings, booleans, undefined 11 | -------------------------------------------------------------------------------- /section3/arrow_functions.js: -------------------------------------------------------------------------------- 1 | // arrow function 2 | var arrowFunc = () => { 3 | console.log('hello from an arrow function'); 4 | } 5 | 6 | // syntax shortcuts 7 | // one parameter (no parentheses) 8 | // single-line return (no explicit `return` keyword) 9 | 10 | var createLogInfo = info => info + 'info'; 11 | 12 | // they can be anonymous functions! 13 | (() => console.log('I am an anonymous arrow function'))(); 14 | -------------------------------------------------------------------------------- /section3/callbacks.js: -------------------------------------------------------------------------------- 1 | // *** Example 1 *** 2 | 3 | function logMessage() { 4 | console.log('greetings'); 5 | } 6 | 7 | function printMessages(callback) { 8 | callback(); 9 | } 10 | 11 | // logMessage becomes a callbck 12 | printMessages(logMessage); 13 | 14 | // *** Example 2 *** 15 | 16 | var numbers = [1, 3, 5, 7, 9]; 17 | 18 | function logNumber(number) { 19 | console.log('number', number); 20 | } 21 | 22 | // Passing in a callback to an out-of-the box function 23 | numbers.forEach(logNumber); 24 | 25 | // a custom version of forEach 26 | function forEach(array, callback) { 27 | for (var index=0; index console.log('number', number)); 35 | -------------------------------------------------------------------------------- /section3/function_declarations_vs_expressions.js: -------------------------------------------------------------------------------- 1 | // function declaration 2 | function calculateTip(price, percentage) { 3 | // side effects 4 | console.log('calculate tip'); 5 | 6 | return (percentage/100) * price; 7 | } 8 | 9 | // function expression 10 | var divideByN = function(number, n) { 11 | return number/n; 12 | } 13 | 14 | // anonymous function 15 | (function() { 16 | console.log('I am an anonymous function expression'); 17 | }()); 18 | -------------------------------------------------------------------------------- /section3/interview_question_callbacks.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | // write functions `logMessage` and `greet` 4 | // such that the following line logs 'hi' 5 | 6 | logMessage(greet(() => greet(() => 'hi'))); 7 | 8 | // *** Answer *** 9 | 10 | function logMessage(message) { 11 | console.log(message); 12 | } 13 | 14 | function greet(callback) { 15 | return callback(); 16 | } 17 | 18 | // var returnHi = () => 'hi'; 19 | // greet(returnHi); 20 | 21 | // no need for the returnHi variable. Pass the callback inline 22 | // greet(() => 'hi'); // 'hi' 23 | 24 | // Notice how the input to logMessage in the question can be broken down: 25 | // greet(() => greet(() => 'hi')); 26 | // greet(() => 'hi'); 27 | // 'hi' -------------------------------------------------------------------------------- /section3/interview_question_this_keyword.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | // what is the output? 4 | 5 | var vehicle = { 6 | type: 'sedan', 7 | logInfo: function() { 8 | var THIS = this; 9 | console.log('1. this.type', this.type); 10 | console.log('1. THIS.type', THIS.type); 11 | 12 | (function() { 13 | console.log('2. this.type', this.type); 14 | console.log('2. THIS.type', THIS.type); 15 | })(); 16 | } 17 | } 18 | 19 | vehicle.logInfo(); 20 | 21 | // *** Answer *** 22 | // 1. this.type sedan 23 | // 1. THIS.type sedan 24 | // 2. this.type undefined 25 | // 2. THIS.type sedan 26 | 27 | // Explanation with similar example 28 | 29 | var aang = { 30 | element: 'air', 31 | greet: function() { 32 | console.log( 33 | "Hi! I'm Aang. I can do cool things with", 34 | this.element 35 | ); 36 | 37 | (function () { 38 | console.log( 39 | 'I can make a ball with', 40 | this.element 41 | ); 42 | 43 | // logs 'I can make a ball with undefined' 44 | // The function has to be a method (attached to the owner object) 45 | // in order for `this` to represent the owner object 46 | 47 | console.log('this', this); // logs the global object (Window, in the browser) 48 | })(); 49 | } 50 | }; 51 | 52 | aang.boast = function () { 53 | console.log( 54 | 'I can make a ball with', 55 | this.element 56 | ); 57 | 58 | // logs 'I can make a ball with air' 59 | // this boast function is now a member of the aang object 60 | 61 | console.log('this', this); // logs the `aang` object 62 | }; 63 | 64 | aang.greet(); 65 | aang.boast(); 66 | -------------------------------------------------------------------------------- /section3/methods_this_keyword.js: -------------------------------------------------------------------------------- 1 | var coder = { 2 | username: 'joe', 3 | years: 3, 4 | languages: ['JS', 'ruby', 'scala'], 5 | company: { 6 | name: 'Coder Inc.', 7 | location: 'San Francisco' 8 | }, 9 | // functions that are members of objects are called methods 10 | logStatus: function() { 11 | console.log('working from office'); 12 | }, 13 | // methods have access to `this`: a representation of the owner object 14 | logProfile: function() { 15 | console.log('username:', this.username); 16 | console.log('years coding:', this.years); 17 | console.log('languages:', this.languages.join(', ')); 18 | console.log('company name:', this.company.name); 19 | console.log('company location:', this.company.location); 20 | } 21 | }; 22 | 23 | coder.logStatus(); 24 | coder.logProfile(); 25 | -------------------------------------------------------------------------------- /section3/more_on_this.js: -------------------------------------------------------------------------------- 1 | // this = Window object 2 | var coder = { 3 | username: 'joe', 4 | years: 3, 5 | logStatus: () => { 6 | // An arrow function does not create a `this` object 7 | // this = Window object 8 | console.log('working from office'); 9 | }, 10 | logProfile: function() { 11 | // A function expression creates a `this` object 12 | // this = owner object 13 | console.log('username:', this.username); 14 | console.log('years coding:', this.years); 15 | } 16 | }; 17 | 18 | coder.logProfile(); 19 | -------------------------------------------------------------------------------- /section4/almost_everything_is_an_object.js: -------------------------------------------------------------------------------- 1 | // Try the property test 2 | // 1. Create a value. 3 | // 2. Attach a property to that value. 4 | // 3. See if that property is still accessible. 5 | // 4. If so, it's an object. 6 | 7 | var store = {}; 8 | store.test = 'foo'; 9 | store.test; // 'foo' (therefore, object) 10 | 11 | var jobs = []; 12 | jobs.test = 'foo'; 13 | jobs.test; // 'foo' (therefore, object) 14 | 15 | var log = function() {}; 16 | log.test = 'foo'; 17 | log.test; // 'foo' (therefore, object) 18 | 19 | var total = 1; 20 | total.test = 'foo'; 21 | total.test; // undefined (therefore, not object) 22 | 23 | var greeting = 'hello'; 24 | greeting.test = 'foo'; 25 | greeting.test; // undefined (therefore, not object) 26 | -------------------------------------------------------------------------------- /section4/dynamic_language_types.js: -------------------------------------------------------------------------------- 1 | // The variable is dynamic. 2 | // Notice that you don't have to declare the type. 3 | // It's not static like `string foo = 'hello'` 4 | var foo = 'hello'; 5 | 6 | // primitive data types 7 | // numbers: 1 8 | // strings: 'foo' 9 | // booleans: true 10 | // undefined: undefined 11 | 12 | // non-primitives 13 | // objects: {} 14 | // functions: function() {} 15 | 16 | console.log(typeof 1); // number 17 | console.log(typeof 'foo'); // string 18 | console.log(typeof true); // boolean 19 | console.log(typeof undefined); // undefined 20 | console.log(typeof {}); // object 21 | function eatRamen() {}; 22 | console.log(typeof eatRamen); // function 23 | console.log(typeof []); // object 24 | 25 | // An array is implemented as an object under the hood 26 | 27 | // This words array 28 | words = ['foo', 'bar', 'goo', 'zar']; 29 | 30 | // can be represented as: 31 | words = { 32 | 0: 'foo', 33 | 1: 'bar', 34 | 2: 'goo', 35 | 3: 'zar' 36 | } 37 | 38 | // Notice that accessing foo works for both the array and object: 39 | words[0]; -------------------------------------------------------------------------------- /section4/equality_type_coercion.js: -------------------------------------------------------------------------------- 1 | console.log(1 == 1); // true 2 | console.log(1 == '1'); // true 3 | // The double equals operator coerces the string to a number, and then checks. 4 | // This is loose equality. 5 | 6 | console.log(1 === '1') // false 7 | // The triple equals operator does not do type coercion. 8 | // This is strict equality. 9 | 10 | // examples of type coercion: 11 | 1 + '2' // '12' | string -> number 12 | +'9' // 9 | string -> number 13 | +'-0.301' // 0.301 | string -> number 14 | +'foo' // NaN 15 | !true // false | actually, only a boolean -> boolean 16 | !false // true | actually, only a boolean -> boolean 17 | 4 > '9' // false | string -> number ('9' --> 9) 18 | false + 1 // 1 | boolean -> number (false -> 0) 19 | true + 1 // 2 | boolean -> number (true -> 1) 20 | -------------------------------------------------------------------------------- /section4/explicit_type_conversion.js: -------------------------------------------------------------------------------- 1 | // Explicit type conversion 2 | 3 | // For explicit number conversion, use the global Number wrapper: 4 | Number('5'); // 5 5 | // This is opposed to the implicit alternative: +'5' 6 | 7 | // Explicit type conversion works for booleans, strings, arrays (and more) 8 | Boolean(0) // false 9 | String(12) // '12' 10 | Array('foo', 'bar', 'goo') // ['foo', 'bar', 'goo] 11 | 12 | // The global Date wrapper returns a Date representation 13 | Date() // Thu Apr 18 2019 20:55:10 GMT-0700 (Pacific Daylight Time) 14 | Date.now() // current time in milliseconds since Jan 1, 1970. For example: 1555646119603 -------------------------------------------------------------------------------- /section4/interview_question_types.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | // What do each of these lines evaluate to? 4 | 5 | typeof typeof 1 6 | 7 | "1" + "2" 8 | 1 + "2" 9 | 1 + +"2" 10 | true + 1 11 | 12 | 1 < 2 < 3 13 | 3 > 2 > 1 14 | 15 | typeof [] 16 | 17 | // *** Answer *** 18 | 19 | typeof typeof 1 // "string" 20 | // This is equivalent to: 21 | // typeof (typeof 1) 22 | // typeof "number" 23 | // "string" 24 | 25 | "1" + "2" // regular concatenation: evaluates "12" 26 | 1 + "2" // implicit type conversion of the 1 to "1": evaluates "12" 27 | 1 + +"2" // +"1", implicitly converts the string to a number: evaluates 3 28 | true + 1 // true has the value of 1 (false: 0): evaluates 2 29 | 30 | 1 < 2 < 3 // equivalent to (1 < 2) < 3. equivalent to true < 3. true has a value of 1. evaluates true < 3: true 31 | 3 > 2 > 1 // equivalent to (3 > 2) > 1. equivalent to true > 1. true has a value of 1. evaluates true > 1: false 32 | 33 | typeof [] // "object" 34 | // Almost everything in JavaScript is an object 35 | 36 | // *** see almost_everyyhing_is_an_object.js in this directory for a deeper explanation 37 | -------------------------------------------------------------------------------- /section4/null.js: -------------------------------------------------------------------------------- 1 | // Null 2 | // Represents the absence of an object 3 | 4 | var eagle = null; 5 | // represents the fact that eagle will be set to an object later 6 | 7 | typeof null; // object 8 | 9 | // Strict equality with undefined. 10 | // They have different types. 11 | // So it should be false 12 | null === undefined // false. 13 | 14 | // Loose equality with undefined. 15 | // JavaScript coerces null to undefined. 16 | null === undefined // true. 17 | -------------------------------------------------------------------------------- /section4/truthy_falsy.js: -------------------------------------------------------------------------------- 1 | // Truthy values 2 | // All values are truthy, unless they are falsy 3 | // This may seem obvious. But it points out the fact 4 | // that it's more important to narrow the falsy values 5 | // Then everything else is truthy. 6 | 7 | // Falsy values: 8 | 0, undefined, null, false, '' 9 | 10 | if (0) { 11 | console.log('hi'); 12 | } 13 | // Any of these values as an if condition will not create a log 14 | 15 | // Truthy values: 16 | 1, [], {}, 'foo' 17 | 18 | if (1) { 19 | console.log('hi'); 20 | } 21 | // Any of these values as an if condition will create a log -------------------------------------------------------------------------------- /section5/hoisting.js: -------------------------------------------------------------------------------- 1 | // Let variables are not hoisted 2 | { 3 | zoo = {}; 4 | 5 | console.log('zoo', zoo); 6 | 7 | let zoo; // Reference Error 8 | } 9 | 10 | 11 | // Var declarations are hoisted. 12 | // They are hoisted to the top of the function scope 13 | 14 | // A regular code block does not limit the hoisting 15 | // var zoo; // implicitly declared 16 | console.log('zoo', zoo); // zoo undefined 17 | 18 | { 19 | zoo = {}; 20 | 21 | console.log('zoo', zoo); 22 | 23 | var zoo; 24 | } 25 | 26 | // A function does limit the hoisting 27 | console.log('zoo', zoo); // ReferenceError zoo is not defined 28 | 29 | function test() { 30 | zoo = {}; 31 | 32 | console.log('zoo', zoo); 33 | 34 | var zoo; 35 | } 36 | 37 | // Note that hoisting does not include the doesn't include assignment. 38 | 39 | // var a; 40 | // var b; 41 | // var c; 42 | 43 | // Function declarations are hoisted too. 44 | // A reference to the function definition is created. 45 | 46 | { 47 | 48 | a = 2; 49 | b = 4; 50 | c = 3; 51 | 52 | const result = (a+b)/c; 53 | 54 | var a, b, c; 55 | 56 | console.log('result', result); 57 | } -------------------------------------------------------------------------------- /section5/interview_question_scoping_hoisting.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | // What is the output of: 4 | let x = 'red'; 5 | 6 | { 7 | let x = 'green'; 8 | 9 | console.log('1: ', x); 10 | 11 | (function() { 12 | console.log('2: ', x); 13 | 14 | var x = 'blue'; 15 | 16 | console.log('3: ', x); 17 | })(); 18 | } 19 | 20 | console.log('4: ', x); 21 | 22 | // *** Answer *** 23 | 24 | // 1: green 25 | // 2: undefined 26 | // 3: blue 27 | // 4: red 28 | 29 | // *** Explanation *** 30 | 31 | let x = 'red'; 32 | 33 | { 34 | // let variables are scoped by code blocks 35 | // so they can be re-declared 36 | let x = 'green'; 37 | 38 | console.log('1: ', x); // 'green' 39 | 40 | (function() { 41 | // var x; // implicit declaration due to hoisting. x is undefined 42 | 43 | console.log('2: ', x); // undefined 44 | 45 | var x = 'blue'; // the var keyword is hoisted according to the function scope 46 | 47 | console.log('3: ', x); // 'blue' 48 | })(); 49 | } 50 | 51 | console.log('4: ', x); // 'red' -------------------------------------------------------------------------------- /section5/let_const_mutability.js: -------------------------------------------------------------------------------- 1 | // Let variables can be declared without assignment. 2 | // They will be undefined. 3 | let total; 4 | // They can also be re-assigned. 5 | total = 10; 6 | total = 11; 7 | 8 | // Const variables have to be assigned when declared. 9 | const foo; // SyntaxError. JavaScript expects assignemnt, not the semicolon after foo. 10 | 11 | // They can't be re-assigned 12 | const foo = 20; 13 | foo = 21; // SyntaxError. Assignment to constant variable. 14 | 15 | // Constants that are assigned to mutable objects can still be mutated though. 16 | 17 | const foo = function() {}; // or const foo = {}, const foo = [] 18 | 19 | foo.test = 'bar'; 20 | 21 | foo.test; // 'bar' 22 | -------------------------------------------------------------------------------- /section5/let_const_var_scope.js: -------------------------------------------------------------------------------- 1 | // Scope 2 | // Let/const variables are scoped to blocks 3 | let color = 'blue'; 4 | 5 | { 6 | let color = 'red'; 7 | 8 | console.log('in function', color); // red 9 | } 10 | 11 | console.log('out of function', color); // blue 12 | 13 | 14 | // Variables created with `var` are scoped to functions 15 | // *** Ex 1: 16 | // Let/const variables are scoped to blocks 17 | var color = 'blue'; 18 | 19 | { 20 | var color = 'red'; 21 | 22 | console.log('in function', color); // red 23 | } 24 | 25 | console.log('out of function', color); // red 26 | // Notice that the re-declaration of var color in the block changed 27 | // the variable for the whole program! 28 | 29 | // *** Ex 2: 30 | var color = 'blue'; 31 | 32 | (function() { 33 | var color = 'red'; 34 | 35 | console.log('in function', color); // red 36 | })(); 37 | 38 | console.log('out of function', color); // blue 39 | 40 | 41 | let x = 1; 42 | 43 | // Notice that the same const name can be re-used in each case 44 | // It's because of the blocks of code, creating separate scopes 45 | switch(x) { 46 | case 1: { 47 | const message = 'x is one'; 48 | console.log(message); 49 | break; 50 | } 51 | case 2: { 52 | const message = 'x is two'; 53 | console.log(message); 54 | break; 55 | } 56 | default: { 57 | const message = 'x is ' + x; 58 | console.log(message); 59 | } 60 | } -------------------------------------------------------------------------------- /section5/template_literals_ternary_operators.js: -------------------------------------------------------------------------------- 1 | // Template literals offer a concise way to do string concatenation and interpolation 2 | for (let i=0; i<5; i++) { 3 | console.log(`step ${i}`); 4 | } 5 | 6 | // Template literals can also make nicely formatted strings 7 | const message = ( 8 | `Space 9 | The final frontier 10 | These are the voyages of the starship Enterprise` 11 | ); 12 | 13 | console.log(message); 14 | 15 | const x = 5; 16 | 17 | // The ternary operator is essentially a one-line if statement 18 | const info = (x % 2 === 0) ? 'even' : 'odd'; 19 | // let info; 20 | // if (x % 2 === 0) { 21 | // info = 'even'; 22 | // } else { 23 | // info = 'odd'; 24 | // } 25 | 26 | // Here's a one-liner with a template literal and ternary operator 27 | console.log(`The number x is ${(x % 2 === 0) ? 'even' : 'odd'}`); 28 | -------------------------------------------------------------------------------- /section6/custom_clonedeep_lodash_clonedeep.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | // custom function for deep cloning function that mostly works 4 | // it needs to handle arrays as well 5 | const cloneDeep = source => { 6 | const result = {}; 7 | 8 | // [[key, value], [key, value]] 9 | const entries = Object.entries(source); 10 | 11 | entries.forEach(entry => { 12 | // entry: [key, value] 13 | const [key, value] = entry; 14 | 15 | if (typeof value === 'object') { 16 | // clone the object's properties 17 | result[key] = cloneDeep(value); 18 | } else { 19 | result[key] = value; 20 | } 21 | }); 22 | 23 | return result; 24 | }; 25 | 26 | const source = { 27 | one: 1, 28 | nest: { 29 | two: 2, 30 | three: 3 31 | }, 32 | four: [4, 'four'], 33 | log: function() {} 34 | }; 35 | 36 | // shallow clone - only copies inner object references 37 | const assignClone = Object.assign({}, source); 38 | 39 | // shallow clone - only copies inner object references 40 | const spreadClone = { ...source }; 41 | 42 | // deep clone - only supports numbers, strings, booleans, null, objects, and arrays 43 | const jsonClone = JSON.parse(JSON.stringify(source)); 44 | 45 | // deep clone - works, but needs to handle arrays 46 | const customClone = cloneDeep(source); 47 | 48 | // deep clone - the best option out there: use the existing work behind lodash 49 | const lodashClone = _.cloneDeep(source); 50 | 51 | source.nest.two = -9; 52 | 53 | console.log('source', source); 54 | console.log('assignClone', assignClone); 55 | console.log('spreadClone', spreadClone); 56 | console.log('jsonClone', jsonClone); 57 | console.log('customClone', customClone); 58 | console.log('lodashClone', lodashClone); 59 | 60 | // Output: 61 | // source { 62 | // one: 1, 63 | // nest: { two: -9, three: 3 }, 64 | // four: [ 4, 'four' ], 65 | // log: [Function] 66 | // } 67 | // assignClone { 68 | // one: 1, 69 | // nest: { two: -9, three: 3 }, 70 | // four: [ 4, 'four' ], 71 | // log: [Function] 72 | // } 73 | // spreadClone { 74 | // one: 1, 75 | // nest: { two: -9, three: 3 }, 76 | // four: [ 4, 'four' ], 77 | // log: [Function] 78 | // } 79 | // jsonClone { 80 | // one: 1, 81 | // nest: { two: 2, three: 3 }, 82 | // four: [ 4, 'four' ] 83 | // } 84 | // customClone { 85 | // one: 1, 86 | // nest: { two: 2, three: 3 }, 87 | // four: { 0: 4, 1: 'four' }, 88 | // log: [Function] 89 | // } 90 | // lodashClone { 91 | // one: 1, 92 | // nest: { two: 2, three: 3 }, 93 | // four: [ 4, 'four' ], 94 | // log: [Function] 95 | // } -------------------------------------------------------------------------------- /section6/interview_question_clones.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | const luke = { 4 | surname: 'Skywalker', 5 | age: 19, 6 | family: { 7 | father: 'Anakin Skywalker', 8 | mother: 'Padme Amidala', 9 | siblings: ['Leia'] 10 | }, 11 | quote: function() { 12 | console.log( 13 | 'I think that R2 unit may have been stolen' 14 | ); 15 | } 16 | }; 17 | 18 | const assignClone = Object.assign({}, luke); 19 | const spreadClone = { ...luke }; 20 | const jsonClone = JSON.parse(JSON.stringify(luke)); 21 | 22 | assignClone.age = 25; 23 | assignClone.family.father = 'Darth Vader'; 24 | 25 | // What is the output? 26 | console.log('luke', luke); 27 | console.log('assignClone', assignClone); 28 | console.log('spreadClone', spreadClone); 29 | console.log('jsonClone', jsonClone); 30 | 31 | // *** Answer and Invesitgation *** 32 | 33 | import _ from 'lodash'; 34 | 35 | const family = { 36 | father: 'Anakin Skywalker', 37 | mother: 'Padme Amidala', 38 | siblings: ['Leia'] 39 | }; 40 | 41 | const luke = { 42 | surname: 'Skywalker', 43 | age: 19, 44 | family, 45 | quote: function() { 46 | console.log( 47 | 'I think that R2 unit may have been stolen' 48 | ); 49 | } 50 | }; 51 | 52 | // Object.assign(target, source); 53 | const assignClone = Object.assign({}, luke); 54 | // const assignClone = { 55 | // surname: 'Skywalker', 56 | // age: 25, 57 | // family: { 58 | // father: 'Darth Vader', 59 | // mother: 'Padme Amidala', 60 | // siblings: ['Leia'] 61 | // }, 62 | // quote: function() { 63 | // console.log( 64 | // 'I think that R2 unit may have been stolen' 65 | // ); 66 | // } 67 | // }; 68 | const spreadClone = { ...luke }; 69 | const jsonClone = JSON.parse(JSON.stringify(luke)); 70 | const lodashClone = _.cloneDeep(luke); 71 | 72 | assignClone.age = 25; 73 | assignClone.family.father = 'Darth Vader'; 74 | 75 | Object.is(assignClone.family, family); // true 76 | // Notice that assignClone.family is a reference to the original object! 77 | 78 | // What is the output? 79 | console.log('luke', luke); 80 | // family.father = 'Darth Vader'; 81 | // age = 19; 82 | console.log('assignClone', assignClone); 83 | // family.father = 'Darth Vader'; 84 | // age = 25; 85 | console.log('spreadClone', spreadClone); 86 | // family.father = 'Darth Vader'; 87 | // age: 19 88 | console.log('jsonClone', jsonClone); 89 | // family.father = 'Anakin Skywalker'; 90 | // age: 19 91 | // no quote method 92 | 93 | // how would you do a true deep clone? 94 | // - lodash.cloneDeep 95 | console.log('lodashClone', lodashClone); 96 | -------------------------------------------------------------------------------- /section6/json.parse_json.stringify_attempt_at_deep_cloning.js: -------------------------------------------------------------------------------- 1 | // JSON format. 2 | // Notice that that the key names have double quotes 3 | // Only strings, numbers, booleans, null, arrays, and objects are valid in JSON 4 | // No functions 5 | const carJson = `{ 6 | "color": "red", 7 | "doors": 2, 8 | "features": ["convertible", "four-wheel drive"], 9 | "details": { 10 | "year": 2019, 11 | "manufacturer": "Honda" 12 | }, 13 | "sold": false, 14 | "owner": null 15 | }`; 16 | 17 | JSON.parse(carJson); 18 | 19 | const owner = { name: 'J.M.' }; 20 | 21 | const car = { 22 | color: 'blue', 23 | doors: 4, 24 | features: ['sunroof'], 25 | details: { 26 | year: 2019, 27 | manufacturer: 'Mercedes' 28 | }, 29 | sold: true, 30 | owner 31 | }; 32 | 33 | JSON.stringify(car); 34 | 35 | // Using JSON.parse(JSON.stringify(object)) deeply clones the object 36 | // With a catch though. 37 | // The object has to only consist of the values supported by JSON: 38 | // - strings, numbers, booleans, null, arrays, and objects 39 | // - no object methods 40 | const carClone = JSON.parse(JSON.stringify(car)); 41 | const shallowClone = Object.assign({}, car); 42 | car.owner.name = 'D.T.'; 43 | 44 | console.log('car', car); 45 | console.log('carClone', carClone); 46 | console.log('shallowClone', shallowClone); 47 | 48 | // Output: 49 | // car { 50 | // ..., 51 | // owner: { name: 'D.T.' } 52 | // } 53 | // carClone { 54 | // ..., 55 | // owner: { name: 'J.M.' } 56 | // } 57 | // shallowClone { 58 | // ... 59 | // owner: { name: 'D.T.' } 60 | // } 61 | -------------------------------------------------------------------------------- /section6/object.is_references_shallow_clone.js: -------------------------------------------------------------------------------- 1 | // Object.is returns whether two values are equal. 2 | // A primitive is always equal to itself 3 | 4 | Object.is('bar', 'bar'); // true 5 | Object.is(1, 1); // true 6 | Object.is(false, false); // true 7 | Object.is(undefined, undefined); // true 8 | Object.is(null, null); // true 9 | 10 | // Object.is is distinguished from == 11 | 1 == '1'; // true 12 | Object.is(1, '1'); // false 13 | 14 | // Object.is is distinguished from === 15 | +0 === -0; // true 16 | // const x = 9; 17 | // -(9-x); 18 | 19 | Object.is(+0, -0); // false 20 | 21 | // Two objects aren't the same through Object.is, 22 | // unless they're the same reference 23 | Object.is([], []); // false 24 | 25 | const object1 = {}; 26 | const object2 = {}; 27 | 28 | const ref1 = object1; 29 | 30 | Object.is(object1, object2); // false 31 | Object.is(object1, ref1); // true 32 | 33 | // *** 34 | 35 | const inner = { 36 | zoo: 10, 37 | zar: 11 38 | }; 39 | 40 | const source = { 41 | foo: 5, 42 | bar: 6, 43 | tee: inner 44 | }; 45 | 46 | // shallow clone 47 | const shallowClone = Object.assign({}, source); 48 | // shallow clone 49 | const spreadClone = { ...source }; 50 | 51 | shallowClone.foo = -1; 52 | 53 | // These updates modify the `inner` object. 54 | // Even shallowClone.tee modifies `inner` since it's a 55 | // reference to the original inner object 56 | inner.zoo = -88; 57 | shallowClone.tee.zar = -5; 58 | // The effect is that these updates show up everywhere that the reference is used 59 | 60 | console.log('source', source); 61 | console.log('shallowClone', shallowClone); 62 | console.log('spreadClone', spreadClone); 63 | 64 | // Output: 65 | // source { foo: 5, bar: 6, tee: { zoo: -88, zar: -5 } } 66 | // shallowClone { foo: -1, bar: 6, tee: { zoo: -88, zar: -5 } } 67 | // spreadClone { foo: 5, bar: 6, tee: { zoo: -88, zar: -5 } } 68 | 69 | Object.is(inner, shallowClone.tee); // true -------------------------------------------------------------------------------- /section6/object_methods.js: -------------------------------------------------------------------------------- 1 | const gradeMap = { 2 | frank: 92, 3 | elise: 95, 4 | angie: 96 5 | }; 6 | 7 | // Object methods 8 | Object.keys(gradeMap); // ['frank', 'elise', 'angie'] 9 | Object.values(gradeMap); // [92, 95, 96] 10 | Object.entries(gradeMap); // [['frank', 92], ['elise', 95], ['angie', 96]] 11 | 12 | // *** 13 | const source = { 14 | frank: 92, 15 | elise: 95, 16 | angie: 96, 17 | timmy: 79 18 | }; 19 | 20 | const target = { 21 | timmy: 89, 22 | joyce: 87 23 | }; 24 | 25 | const source2 = { 26 | jacob: 91, 27 | timmy: 99 28 | }; 29 | 30 | // timmy in source2 will win. It's the right most argument 31 | const combined = Object.assign(target, source, source2); 32 | 33 | console.log('combined', combined); 34 | // combined { timmy: 99, joyce: 87, frank: 92, elise: 95, angie: 96, jacob: 91 } 35 | -------------------------------------------------------------------------------- /section6/property_shorthand_destructuring_assignment.js: -------------------------------------------------------------------------------- 1 | const weapon = 'lightsaber'; 2 | 3 | // Property shorthand. 4 | // When the property name and the variable name are identical, 5 | // the property name only has to be specified once. 6 | const vader = { 7 | title: 'darth', 8 | children: ['Luke', 'Leia'], 9 | weapon // this is equivalent to weapon: weapon 10 | }; 11 | 12 | console.log(vader); 13 | 14 | const planets = [ 15 | 'mercury', 16 | 'venus', 17 | 'earth', 18 | ['mars', 'phobos', 'deimos'] 19 | ]; 20 | 21 | // Array destructuring 22 | const [one, two, three] = planets; 23 | 24 | // There can be default values 25 | const [one, two, three, fourth='planet'] = planets; 26 | 27 | // Indexes can be skipped 28 | const [one, , , fourth='planet'] = planets; 29 | 30 | // Nested array destructuring 31 | const [one, , , [fourth, moon1, moon2]] = planets; 32 | 33 | 34 | const wizard = { 35 | name: 'Harry', 36 | age: 17, 37 | info: { 38 | hobby: 'Quidditch', 39 | spells: ['Expelliarmus'] 40 | } 41 | }; 42 | 43 | // Object destructuring 44 | // Create variables out of matching property names 45 | const { 46 | name, 47 | age, 48 | house='Gryffindor', 49 | info: { hobby }, // nested object destructuring 50 | info // the nested destructuring above doesn't destructure the base property name. You have to destructure this separately. 51 | } = wizard; 52 | 53 | console.log(name, age, house, hobby, info); 54 | -------------------------------------------------------------------------------- /section6/spread_syntax.js: -------------------------------------------------------------------------------- 1 | const source = { 2 | kim: 99, 3 | tom: 92 4 | }; 5 | 6 | // The spread syntax looks like an ellipses: ... 7 | 8 | const clone = { 9 | bob: 89, 10 | ann: 95, 11 | ...source, // the source object is spread into the target 12 | ron: 85 13 | }; 14 | 15 | console.log('clone', clone); 16 | 17 | const elements = ['fire', 'water']; 18 | 19 | // The spread syntax can be used for array merges 20 | [...elements, 'air', 'earth']; 21 | 22 | // The spread syntax can be used for array clones 23 | const elementsClone = [...elements]; 24 | 25 | console.log('elementsClone', elementsClone); 26 | 27 | const sumXYZ = function(x, y, z) { 28 | return x + y + z; 29 | } 30 | 31 | const numbers = [3, 5, 9]; 32 | // The spread syntax applies the items of an array 33 | // as individual arguments 34 | sumXYZ(...numbers); 35 | // equivalent to sumXYZ(numbers[0], numbers[1], numbers[2]); 36 | -------------------------------------------------------------------------------- /section7/closures.js: -------------------------------------------------------------------------------- 1 | // Closures are a combination of an inner function and its environment. 2 | // The environment consists of three scopes: 3 | // 1. global scope 4 | // 2. outer function's scope 5 | // 3. its own inner scope 6 | 7 | // *** 8 | // global scope 9 | 10 | function outer() { 11 | // outer scope 12 | let username = ''; 13 | let password = ''; 14 | 15 | // function innerClosure() { 16 | // // inner scope 17 | // username = 'drew'; 18 | // console.log('username', username); 19 | // } 20 | 21 | // private closure 22 | function setUsername(newUsername) { 23 | username = newUsername; 24 | } 25 | 26 | // private closure 27 | function setPassword(newPassword) { 28 | password = newPassword; 29 | } 30 | 31 | return { 32 | signup: function(user, pass) { 33 | setUsername(user); 34 | setPassword(pass); 35 | }, 36 | debug: function() { 37 | console.log('username', username, 'password', password); 38 | }, 39 | signin: function(user, pass) { 40 | return (user === username) && (pass === password); 41 | } 42 | }; 43 | } 44 | 45 | const account = outer(); 46 | account.signup('jerry', 'foo123'); 47 | 48 | console.log(account); // signup: [Function], debug: [Function], signin: [Function] } 49 | 50 | account.debug(); // username jerry password foo123 51 | 52 | const attempt1 = account.signin('jerry', 'foo123'); // true 53 | const attempt2 = account.signin('hackr', 'fo1234'); // false 54 | 55 | -------------------------------------------------------------------------------- /section7/function_factories.js: -------------------------------------------------------------------------------- 1 | // A function factory returns a custom function 2 | // The returned function is a closure 3 | 4 | function makeSuffixer(suffix) { 5 | const separator = ' '; 6 | 7 | function addSuffix(word) { 8 | return word + separator + suffix; 9 | } 10 | 11 | return addSuffix; 12 | } 13 | 14 | const addLy = makeSuffixer('ly'); // the result is a returned closure 15 | console.log('addLy', addLy); 16 | 17 | const smartly = addLy('smart'); 18 | console.log('smartly', smartly); 19 | 20 | const addNess = makeSuffixer('ness'); // the result is a returned closure 21 | const awesomeness = addNess('awesome'); 22 | console.log('awesomeness', awesomeness); 23 | -------------------------------------------------------------------------------- /section7/higher_order_functions.js: -------------------------------------------------------------------------------- 1 | // Higher order functions 2 | const numbers = [1, 2, 3]; 3 | 4 | const callback = number => { 5 | console.log('number', number); 6 | }; 7 | 8 | // forEach is a higher-order function 9 | // it takes a callback function as an argument 10 | numbers.forEach(callback); 11 | 12 | // lessThan is a higher-order function 13 | // it returns a function 14 | function lessThan(y) { 15 | return function(x) { 16 | return x < y; 17 | } 18 | } 19 | 20 | const lessThan30 = lessThan(30); 21 | lessThan30(20); 22 | -------------------------------------------------------------------------------- /section7/interview_question_closures_higher_order_functions_callbacks.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | // Write a function sum 3 | 4 | sum(1)(2)(3)(4)(5)(result => console.log('result', result)); 5 | 6 | // such that the above would log "result 15" 7 | 8 | // *** Answer *** 9 | function sum(a) { 10 | // outer scope 11 | return function(b) { 12 | // inner scope 13 | return function(c) { 14 | return function(d) { 15 | return function(e) { 16 | const total = a + b + c + d + e; 17 | 18 | return function(callback) { 19 | callback(total); 20 | } 21 | } 22 | } 23 | } 24 | }; 25 | } 26 | 27 | // *** Investigation *** 28 | 29 | function sum(a) { 30 | return function(b) { 31 | return a + b; 32 | } 33 | } 34 | 35 | // Returns a closure. 36 | // The closure encloses its environment. 37 | // The closure environment consists of the global scope, outer function scope, and inner scope 38 | 39 | // the above returns 3 for sum(1)(2); 40 | 41 | // Extending this pattern you get: 42 | 43 | function sum(a) { 44 | // outer scope 45 | return function(b) { 46 | // inner scope 47 | return function(c) { 48 | return function(d) { 49 | return function(e) { 50 | return a + b + c + d + e; 51 | } 52 | } 53 | } 54 | }; 55 | } 56 | 57 | sum(1)(2)(3)(4)(5); // evaluates to 15 58 | 59 | // Investigation part 2 60 | 61 | // The following executes a callback with a parameter 62 | 63 | function executeArgument(callback) { 64 | const innerResult = 99; 65 | 66 | callback(innerResult); 67 | } 68 | 69 | executeArgument(result => console.log('result', result)); 70 | 71 | // this would log "result 99" 72 | 73 | // Combinining the above concepts, you get the solution: 74 | 75 | function sum(a) { 76 | // outer scope 77 | return function(b) { 78 | // inner scope 79 | return function(c) { 80 | return function(d) { 81 | return function(e) { 82 | const total = a + b + c + d + e; 83 | 84 | return function(callback) { 85 | callback(total); 86 | } 87 | } 88 | } 89 | } 90 | }; 91 | } -------------------------------------------------------------------------------- /section8/class_inheritance.js: -------------------------------------------------------------------------------- 1 | class Structure { 2 | constructor() { 3 | this.isStructure = true; 4 | this.humanMade = true; 5 | } 6 | } 7 | 8 | // Class inheritance is achieved through the `extends` keyword 9 | class Building extends Structure { 10 | constructor(name, stories, address) { 11 | // A super call is necessary to invoke the parent constructor 12 | // Even if the parent constructor has no arguments 13 | // This is helpful because the parent constructor could theoretically 14 | // be setting up some important properties 15 | super(); 16 | this.name = name; 17 | this.stories = stories; 18 | this.address = address; 19 | this.guests = []; 20 | } 21 | 22 | info() { 23 | console.log( 24 | 'name:', 25 | this.name, 26 | '| stories:', 27 | this.stories, 28 | '| address:', 29 | this.address 30 | ); 31 | } 32 | 33 | get needElevator() { 34 | return this.stories > 10; 35 | } 36 | 37 | set guest(value) { 38 | this.guests.push(value); 39 | } 40 | } 41 | 42 | class Restaurant extends Building { 43 | constructor(name, stories, address, cuisine) { 44 | super(name, stories, address); 45 | 46 | this.cuisine = cuisine; 47 | } 48 | 49 | // In addition to the constructor, methods can be overridden as well 50 | // Call the methods implemented in the parent using `super` 51 | info() { 52 | super.info(); 53 | console.log(`We serve ${this.cuisine} cusine`); 54 | } 55 | } 56 | 57 | const kaiju = new Restaurant( 58 | 'kaiju', 59 | 2, 60 | 'SF, CA', 61 | 'Japanese' 62 | ); 63 | 64 | console.log('kaiju', kaiju); 65 | // kaiju Restaurant { 66 | // isStructure: true, 67 | // humanMade: true, 68 | // name: 'kaiju', 69 | // stories: 2, 70 | // address: 'SF, CA', 71 | // guests: [], 72 | // cuisine: 'Japanese' 73 | // } 74 | 75 | kaiju.info(); 76 | // name: kaiju | stories: 2 | address: SF, CA 77 | // We serve Japanese cusine -------------------------------------------------------------------------------- /section8/classes.js: -------------------------------------------------------------------------------- 1 | // A class is a nice alternative to the constructor function 2 | // If you have experience in other language, the `class` keyword 3 | // can be more intuitive 4 | class Building { 5 | constructor(name, stories, address) { 6 | this.name = name; 7 | this.stories = stories; 8 | this.address = address; 9 | this.guests = []; 10 | } 11 | 12 | info() { 13 | console.log( 14 | 'name:', 15 | this.name, 16 | '| stories:', 17 | this.stories, 18 | '| address:', 19 | this.address 20 | ); 21 | } 22 | 23 | // getter methods can do dynamic calculation 24 | // they take no arguments 25 | get needElevator() { 26 | return this.stories > 10; 27 | } 28 | 29 | // setter methods can do dynamic calculation 30 | // they take one argument 31 | set guest(value) { 32 | this.guests.push(value); 33 | } 34 | } 35 | 36 | const empireState = new Building( 37 | 'Empire State Building', 38 | 102, 39 | 'NY, NY' 40 | ); 41 | 42 | console.log('empireState', empireState); 43 | 44 | empireState.info(); 45 | 46 | const salesforceTower = new Building( 47 | 'Salesforce Tower', 48 | 61, 49 | 'SF, CA' 50 | ); 51 | 52 | salesforceTower.info(); 53 | 54 | console.log( 55 | 'salesforceTower.needElevator', 56 | salesforceTower.needElevator // invoking the `needElevator` getter 57 | ); 58 | 59 | // invoking the `guest` setter, twice 60 | salesforceTower.guest = 'J. Smith'; 61 | salesforceTower.guest = 'A. Seedy'; 62 | 63 | console.log( 64 | 'salesforceTower.guests', 65 | salesforceTower.guests 66 | ); 67 | -------------------------------------------------------------------------------- /section8/constructor_functions.js: -------------------------------------------------------------------------------- 1 | // Building is a constructor function 2 | // In object-oriented programming terms, this would be an object template 3 | function Building(name, stories, address) { 4 | this.name = name; 5 | this.stories = stories; 6 | this.address = address; 7 | this.info = function() { 8 | console.log( 9 | 'name:', 10 | this.name, 11 | '| stories:', 12 | this.stories, 13 | '| address:', 14 | this.address 15 | ); 16 | } 17 | } 18 | 19 | // empireState is an instance of the building template 20 | // Notice that the `new` keyword is necessary 21 | // It's implicitly creating an owner object around the 22 | // this` keyword for the instance of the Building constructor 23 | const empireState = new Building( 24 | 'Empire State Building', 25 | 102, 26 | 'NY, NY' 27 | ); 28 | 29 | console.log('empireState', empireState); 30 | 31 | empireState.info(); 32 | 33 | // salesforceTower is an instance of the building template 34 | const salesforceTower = new Building( 35 | 'Salesforce Tower', 36 | 61, 37 | 'SF, CA' 38 | ); 39 | 40 | salesforceTower.info(); 41 | -------------------------------------------------------------------------------- /section8/interview_question_prototype_based_inheritance.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | 3 | function Artist(name, talent) { 4 | this.name = name; 5 | this.talent = talent; 6 | } 7 | 8 | class Musician extends Artist { 9 | constructor(name, talent, instrument) { 10 | super(name, talent); 11 | 12 | this.instrument = instrument; 13 | } 14 | } 15 | 16 | const frank = new Musician( 17 | 'Frank Sinatra', 18 | 'singer', 19 | 'voice' 20 | ); 21 | 22 | Object.prototype.info = function() { 23 | console.log('this', this); 24 | } 25 | 26 | // What is the output of 27 | frank.info(); 28 | 29 | // *** Answer *** 30 | // this Musician { name: 'Frank Sinatra', talent: 'singer', instrument: 'voice' } 31 | 32 | // *** Investigation *** 33 | // The parent of Musician is Artist. 34 | // The parent of the Artist constructor function is the Object prototype. 35 | // Therefore, attaching .info to the Object.prototype, means that 36 | // any instance of artist can find .info. 37 | // 38 | // Properties aren't copied from the parent prototype to a child. 39 | // 40 | // JavaScript does a lookup through the prototype chain to see if the 41 | // method is available. 42 | // 43 | // This is why updates to the prototype make the method immediately 44 | // available for all instances of Object. 45 | -------------------------------------------------------------------------------- /section8/object_prototype_proto_prototype_chain.js: -------------------------------------------------------------------------------- 1 | // NOTE: 2 | // This code assumes you're executing the JavaScript in a 3 | // browser development console 4 | 5 | const t = 'ahoy'; 6 | 7 | t. // Reveals a preview of methods available on t 8 | 9 | // T is a primitive value (stirng) 10 | // But notice that there are methods available, as if the string is an object 11 | 12 | t.__proto__; // revals the properties and methods available in the parent prototype 13 | // Notice that this is identical to `String.prototype` 14 | Object.is(t.__proto__, String.prototype); // true 15 | 16 | // Walk up the prototype chain: 17 | t.__proto__.__proto__; // this reveals an object that is identical to Object.prototype; 18 | Object.is(tee.__proto__.__proto__, Object.prototype); // true 19 | 20 | // All objects have the Object at their root in the prototype chain 21 | Object.is(Number.__proto__.__proto__, Object.prototype); // true 22 | 23 | // This is why almost everything in JavaScript is an object! 24 | -------------------------------------------------------------------------------- /section8/prototypes_and_prototype_based_inheritance.js: -------------------------------------------------------------------------------- 1 | // NOTE: 2 | // This code assumes you're executing the JavaScript in a 3 | // browser development console 4 | 5 | function Building(name, stories) { 6 | this.name = name; 7 | this.stories = stories; 8 | } 9 | 10 | Building.prototype // { constructor: function } 11 | 12 | // You can use the constructor function directly 13 | const foo = new Building('foo', 2); 14 | 15 | // Or you can use the constructor defined in the prototype 16 | const goo = new Building.prototype.constructor('goo', 3); 17 | 18 | // Since every instance has its parent prototype available, 19 | // you can even construct new instances, using an instance itself 20 | const bar = new foo.constructor('bar', 4); 21 | 22 | // You can also add new properties to the constructor's 23 | // prototype at any time 24 | 25 | Building.prototype.isBuilding = true; 26 | Building.prototype; // {isBuilding: true, constructor: function } 27 | 28 | // in the browser console, typing `foo.` will reveal what methods 29 | // it has available. Notice that `isBuilding` is there! 30 | foo. 31 | 32 | // The isBuilding property is available on foo. Even though it was 33 | // added to the Building prototype after the creation of foo. 34 | 35 | // This is because JavaScript does a lookup through prototypes. 36 | // It doesn't copy the prototype properties to an instance at 37 | // creation time. 38 | // Instead, JavaScript will look up to see if the methods are available 39 | // on the parent prototype (if it's not a property set in the constructor) 40 | // itself. 41 | 42 | // This also shows why primitive values have methods. 43 | // Typing `1.` in the console won't reveal methods: 44 | 1. // nothing 45 | 46 | // But if you assign the number to a constant, the browser environment 47 | // will show that there are methods available: 48 | const n = 1; 49 | n. // reveals a list of methods: constructor, toExponential, toFixed, toLocaleString, etc. 50 | 51 | // Notice that these are the methods available in the global 52 | // Number constructor function's prototype! 53 | Number.prototype; // { constructor, toExponential, toFixed, toLocaleString, ... } 54 | 55 | // Under the hood, JavaScript sees that you have a number data type. 56 | // If you use a method 57 | // So it temporarily passes the number to the Number constructor, to get 58 | // the available methods. This converts the primitive number to an object. 59 | // And the methods are now available on that temporarily converted number. -------------------------------------------------------------------------------- /section9/asynchronous_code_with_settimeout.js: -------------------------------------------------------------------------------- 1 | // setTimeout takes two parameters: 2 | // 1) a callback function 3 | // 2) a timer delay (in milliseconds) 4 | const timeoutId = setTimeout( 5 | () => console.log('bonjour'), 6 | 2000 7 | ); 8 | 9 | // logs 'bonjour' after 2 seconds. 10 | 11 | // *** 12 | // The asynchronous setTimeout is placed into a queue in the background 13 | // It's functionality (the callback) will fire once the rest of the 14 | // synchronous code is complete. 15 | // 16 | // This means the timeoutId log will occur before bonjour. 17 | // Output: 18 | // timeoutId 1 19 | // bonjour 20 | const timeoutId = setTimeout( 21 | () => console.log('bonjour'), 22 | 1000 23 | ); 24 | 25 | console.log('timeoutId', timeoutId); 26 | 27 | // *** 28 | // The asynchronous setTimeout is placed into a queue in the background 29 | // It's functionality (the callback) will fire once the rest of the 30 | // synchronous code is complete. 31 | // 32 | // Part of that asynchronous code is a clearTimeout which removes 33 | // the original timeout. 34 | // So in this chunk, the callback never fires. 35 | // It's immediately cleared. 36 | const timeoutId = setTimeout( 37 | () => console.log('bonjour'), 38 | 0 39 | ); 40 | 41 | clearTimeout(timeoutId); 42 | 43 | console.log('timeoutId', timeoutId); 44 | -------------------------------------------------------------------------------- /section9/await_and_async.js: -------------------------------------------------------------------------------- 1 | const boomerang = new Promise((resolve, reject) => { 2 | // reject('stuck in a tree'); 3 | 4 | setTimeout(() => { 5 | resolve('returned'); 6 | }, 3000); 7 | }); 8 | 9 | // The await keyword can be used to block asynchronous functionality 10 | // Normally, JavaScript is non-blocking and executes the rest of the code 11 | // as asynchronous code like promises are handled in the background. 12 | // But the await keyword allows you to wait for the promise to resolve or reject 13 | // before executing the following code 14 | 15 | async function test() { // to use await, it must be in a function marked by the async keyword 16 | try { // try/catch is used to handle resolved/rejected promises 17 | const boomerangStatus = await boomerang; // this blocks the following console.log 18 | console.log('boomerangStatus', boomerangStatus); 19 | } catch(error) { 20 | console.log('error', error); 21 | } 22 | } 23 | 24 | test(); 25 | -------------------------------------------------------------------------------- /section9/fetch_and_promise.js: -------------------------------------------------------------------------------- 1 | // The fetch method allows you to conduct api requests 2 | // The response is a Promise object 3 | 4 | // A promise returns either a successful or failed value for 5 | // functionality that completes after an unknown amount of time. 6 | 7 | // It's perfect for an api fetch, where the speed of the response 8 | // depends on network connection. 9 | 10 | // You handle promises by chaining .then handlers 11 | // These take callback functions, which fire when the promises's 12 | // functionality is complete 13 | 14 | fetch('https://official-joke-api.appspot.com/jokes/random') 15 | .then(response => { 16 | return response.json(); 17 | }) 18 | .then(json => console.log('json', json)); 19 | 20 | // Note that the fetch is asynchronous. These logs that appear 21 | // after in the code, will appear first in the output. 22 | 23 | console.log('after fetch'); 24 | console.log('after fetch 2'); 25 | 26 | // Output: 27 | // after fetch 28 | // after fetch 2 29 | // => undefined (Repl's evaluation) 30 | // json { id: 68, 31 | // type: 'general', 32 | // setup: 'I Started A New Business Making Yachts In My Attic This Year...', 33 | // punchline: 'The sails are going through the roof.' 34 | // } -------------------------------------------------------------------------------- /section9/interview_question_asynchronous_code_and_engine.js: -------------------------------------------------------------------------------- 1 | // *** Question *** 2 | console.log(1); 3 | setTimeout(() => console.log(2), 1000); 4 | setTimeout(() => console.log(3), 0); 5 | console.log(4); 6 | 7 | // what is the output of the above? 8 | 9 | // *** Answer *** 10 | // 1 11 | // 4 12 | // 3 13 | // 2 14 | 15 | // *** Investigation *** 16 | 17 | // output: 18 | // 1 19 | // (non-blocking setTimeout - the next line can execute) 20 | // (non-blocking setTimeout - the next line can execute) 21 | // 4 22 | // 3 (the 0ms delay completes first) 23 | // 2 (the 1000ms delay completes) -------------------------------------------------------------------------------- /section9/promise_from_scratch.js: -------------------------------------------------------------------------------- 1 | // This promise resolves half of the time, 2 | // and rejects the other half of the time 3 | const flip = new Promise((resolve, reject) => { 4 | if (Math.random() > 0.5) { 5 | resolve('success'); 6 | } else { 7 | reject('failure'); 8 | } 9 | }); 10 | 11 | // .then handles resolved promises 12 | // .catch handles rejected promises 13 | flip 14 | .then(result => console.log('result', result)) 15 | .catch(error => console.log('error', error)); 16 | 17 | // Promises are handled asynchronously. 18 | // So even though this log appears after in the code, 19 | // it will be the first log to have output. 20 | console.log('after flip'); 21 | 22 | // Output: 23 | // after flip 24 | // => undefined (Repl's evaluation) 25 | // error failure 26 | 27 | // Or the output can be: 28 | // after flip 29 | // result success 30 | // => undefined (Repl's evaluation) --------------------------------------------------------------------------------