├── README.md └── js ├── amd-commonjs-es6modules.js ├── array-concat-push.js ├── array-every-some.js ├── array-filter-map-reduce.js ├── array-foreach.js ├── array-pass-by-val-reference.js ├── array-reduce.js ├── array-slice-splice.js ├── bind-function.js ├── bitwise-operators.js ├── call-apply-function.js ├── closures.js ├── coercion.js ├── conditional-function-declaration.js ├── currying.js ├── dom.js ├── event-bubbling.js ├── event-delegation.js ├── event-handling.js ├── factory-functions.js ├── floating-point-precision.js ├── for-in-with-hasOwnProperty.js ├── getOwnPropertyNames-vs-keys.js ├── getters-setters.js ├── logical-operations-with-string.js ├── method-overloading.js ├── mixins.js ├── new-keyword.js ├── number-maxmin-val.js ├── object-clone.js ├── object-constructor.js ├── object-create.js ├── object-defineProperty.js ├── object-freeze.js ├── object-keys.js ├── object-oriented.js ├── object-prototype.js ├── object-reference.js ├── oloo-pattern.js ├── setTimeout-inside-loop.js ├── shim-polyfill-monkeypatch.js ├── string-methods.js ├── styling.js └── this-keyword.js /README.md: -------------------------------------------------------------------------------- 1 | # JS Bits 2 | 3 | JavaScript concepts explained with code. 4 | 5 | Community contributions welcome :) 6 | 7 | **Translations by community:** 8 | 9 | - 中文版 (Chinese): [js-bits-cn](https://github.com/ecmadao/js-bits-cn) 10 | 11 | --- 12 | 13 | ### Topics 14 | 15 | * [AMD CommonJS and ES6 Modules Usage](js/amd-commonjs-es6modules.js) 16 | * [Array concat() push()](js/array-concat-push.js) 17 | * [Array every() some()](js/array-every-some.js) 18 | * [Array filter() map() reduce()](js/array-filter-map-reduce.js) 19 | * [Array forEach()](js/array-foreach.js) 20 | * [Array pass by val vs reference](js/array-pass-by-val-reference.js) 21 | * [Array reduce()](js/array-reduce.js) 22 | * [Array slice() splice()](js/array-slice-splice.js) 23 | * [Apply & Call function](js/call-apply-function.js) 24 | * [Bind function](js/bind-function.js) 25 | * [Bitwise operators](js/bitwise-operators.js) 26 | * [Closures](js/closures.js) 27 | * [Coercion](js/coercion.js) 28 | * [Conditional function declaration](js/conditional-function-declaration.js) 29 | * [Currying](js/currying.js) 30 | * [DOM](js/dom.js) 31 | * [Event Bubbling](js/event-bubbling.js) 32 | * [Event Delegation](js/event-delegation.js) 33 | * [Event Handling](js/event-handling.js) 34 | * [Factory Functions](js/factory-functions.js) 35 | * [Floating point precision](js/floating-point-precision.js) 36 | * [for-in with hasOwnProperty](js/for-in-with-hasOwnProperty.js) 37 | * [Getters and Setters](js/getters-setters.js) 38 | * [Logical operations with string](js/logical-operations-with-string.js) 39 | * [Method Overloading](js/method-overloading.js) 40 | * [Mixins](js/mixins.js) 41 | * [new keyword](js/new-keyword.js) 42 | * [Number Max Min val](js/number-maxmin-val.js) 43 | * [Object clone](js/object-clone.js) 44 | * [Object constructor](js/object-constructor.js) 45 | * [Object create()](js/object-create.js) 46 | * [Object defineProperty](js/object-defineProperty.js) 47 | * [Object freeze](js/object-freeze.js) 48 | * [Object keys](js/object-keys.js) 49 | * [Object oriented concepts](js/object-oriented.js) 50 | * [Object prototype](js/object-prototype.js) 51 | * [Object references](js/object-reference.js) 52 | * [OLOO pattern](js/oloo-pattern.js) 53 | * [setTimeout inside a loop](js/setTimeout-inside-loop.js) 54 | * [Shim vs Polyfill vs Monkey patch](js/shim-polyfill-monkeypatch.js) 55 | * [String methods](js/string-methods.js) 56 | * [Styling](js/styling.js) 57 | * [this keyword](js/this-keyword.js) 58 | -------------------------------------------------------------------------------- /js/amd-commonjs-es6modules.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modules 3 | * 4 | * Why is modularity important? 5 | * - it encourages the development of small isolated modules with clear interfaces, as opposed to large chunks of monolithic code 6 | * - it helps with testability, as modules can be replaced at runtime with mocks that implement the same interface 7 | * - it improves code maintainability, as smaller and well isolated modules are easier to understand 8 | * 9 | * AMD vs CommonJS vs ES6 Modules 10 | * 11 | * @Reference: 12 | * http://stackoverflow.com/questions/21021621/difference-between-requirejs-and-commonjs 13 | * https://www.airpair.com/javascript/posts/the-mind-boggling-universe-of-javascript-modules 14 | * http://javascript.tutorialhorizon.com/2014/09/01/understanding-nodejs-module-exports-and-require/ 15 | * http://www.2ality.com/2014/09/es6-modules-final.html 16 | * 17 | * Module Pattern: 18 | * http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html 19 | */ 20 | 21 | /** 22 | * AMD - Asynchronous Module Definition 23 | * 24 | * Was specifically designed to suit the browser environment and are loaded asynchronously when they are needed in the application. 25 | * Once they are loaded they’ll be cached so they can be served directly if they’re needed again. 26 | * AMD modules work using native JavaScript, so they don’t require a build tool in order to work. 27 | * While in CommonJS you only have the option to export an object, in AMD you can export any JavaScript type. 28 | * This means you can for example export a constructor function or configuration array. 29 | * Next to loading modules, AMD is also capable to load other files on demand 30 | * eg. HTML templates, CSS, Text, JS and Binary files 31 | * 32 | * Since AMD modules need to be able to fetch dependencies just-in-time, they need a callback wrapper around a module 33 | * which produces slightly more overhead in your module definition. 34 | * 35 | * Multiple modules can be loaded in parallel. 36 | * Asynchronous loading is a complex subject and it can easily create race conditions if not properly designed. 37 | * It isn't possible to guarantee the order of execution of asynchronous modules. 38 | * 39 | * How To Use: 40 | * Your module will publicly expose whatever is being returned on the callback function. 41 | * In order to use your module, the client code needs to refer to it (per file location or alias) on its dependencies array, 42 | * which will map to an argument on its own callback function. 43 | * 44 | */ 45 | 46 | // foo.js 47 | // Define a module called foo 48 | define('foo', function () { 49 | return { 50 | method: function () { 51 | return 'food method result'; 52 | } 53 | } 54 | }); 55 | 56 | // bar.js 57 | // Define a module called 'bar', which is dependent on the 'foo' module 58 | define('bar', ['foo'], function (Foo) { 59 | return { 60 | barMethod: function () { 61 | return 'bar method result'; 62 | }, 63 | fooMethod: function () { 64 | return Foo.method(); 65 | } 66 | }; 67 | }); 68 | 69 | // Require the bar module and use it within the require wrapper 70 | require(['bar'], function (bar) { 71 | // Do something with fetched dependency 72 | bar.barMethod(); 73 | bar.fooMethod(); 74 | }); 75 | 76 | /** 77 | * CommonJS 78 | * 79 | * CommonJS are designed from the ground up to suit the (NodeJS) server environment 80 | * Since CommonJS modules don’t have to fetch modules just-in-time, there is no need to put a wrapper with a callback structure around a module. 81 | * This makes a module look more clear and tidy. These modules are also called “naked” modules. 82 | * 83 | * CommonJS modules don’t work natively in the browser environment. 84 | * Instead, they rely on a build step, which evaluates the require calls, and alters the module code by parsing dependent modules. 85 | * CommonJS modules are always included directly and can’t be fetched just-in-time. 86 | * 87 | * It was adopted as the official module format for Node.js and NPM components. 88 | * This means that any module defined in CommonJS will have access to the whole NPM ecosystem. 89 | * 90 | * How To Use: 91 | * Your module file will publicly expose whatever is assigned to module.exports while everything else is private. 92 | * In order to use your module, the client code needs to use the require(dependency) function, referencing your module per file location or alias. 93 | * 94 | */ 95 | 96 | // foo.js 97 | // Define a module called 'foo' 98 | var foo = function () { 99 | return 'foo method result'; 100 | }; 101 | 102 | // expose foo to other modules 103 | exports.method = foo; 104 | 105 | // bar.js 106 | // Define a module called 'bar', which is dependent on the 'foo' module. 107 | var Foo = require('foo'); 108 | var barMethod = function () { 109 | return 'barMethod result'; 110 | }; 111 | var fooMethod = function () { 112 | return Foo.method(); 113 | }; 114 | 115 | exports.barMethod = barMethod; 116 | exports.fooMethod = fooMethod; 117 | 118 | 119 | // Require the bar module 120 | var bar = require('bar'); 121 | // Do something with the fetched dependency 122 | bar.barMethod(); 123 | bar.fooMethod(); 124 | 125 | /** 126 | * Hybrid 127 | * 128 | * Some AMD loaders also offer a hybrid format between AMD and CommonJS modules. 129 | * It scans the contents at runtime to determine what modules to preload, so even though it seems to be synchronous, it’s not. 130 | * 131 | */ 132 | 133 | define(function (require, exports, module) { 134 | var math = require('lib/math'); 135 | exports.max = math.max; 136 | exports.add = math.add; 137 | }); 138 | 139 | 140 | /** 141 | * ES6 Modules 142 | * 143 | * ES6 modules will support both synchronous and asynchronous loading within the same syntax. 144 | * And even better, they will work both on the browser and on the server. 145 | */ 146 | 147 | // EXPORTING 148 | // exporter.js 149 | export function someMethod() { 150 | // Do some stuff 151 | } 152 | 153 | export var another = {}; 154 | 155 | // IMPORTING 156 | // importer.js 157 | import { someMethod, another as newName } from './exporter'; 158 | 159 | someMethod(); 160 | // typeof newName == 'object'; 161 | 162 | 163 | // DEFAULT IMPORT AND EXPORT 164 | // export-default.js 165 | export default function foo() { 166 | console.log('foo'); 167 | } 168 | 169 | // import-default.js 170 | import customName from './export-default'; 171 | customName(); // -> 'foo' 172 | 173 | 174 | // ALL SUPPORTED SYNTAX 175 | import 'jquery'; // import a module without any import bindings 176 | import $ from 'jquery'; // import the default export of a module 177 | import { $ } from 'jquery'; // import a named export of a module 178 | import { $ as jQuery } from 'jquery'; // import a named export to a different name 179 | 180 | export var x = 42; // export a named variable 181 | export function foo() {}; // export a named function 182 | 183 | export default 42; // export the default export 184 | export default function foo() {}; // export the default export as a function 185 | 186 | var encrypt = {}; 187 | var decrypt = {}; 188 | export { encrypt }; // export an existing variable 189 | export { decrypt as dec }; // export a variable as a new name 190 | export { encrypt as en } from 'crypto'; // export an export from another module 191 | export * from 'crypto'; // export all exports from another module 192 | 193 | import * as crypto from 'crypto'; // import an entire module instance object 194 | 195 | // Note that any valid declaration can be exported. In ES6, this includes class, const, and let. 196 | -------------------------------------------------------------------------------- /js/array-concat-push.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array push() and concat() 3 | * Performance: concat() is ~40% faster than push() 4 | * 5 | * Reference: 6 | * http://gunnariauvinen.com/difference-between-concat-and-push-in-javascript/ 7 | * http://davidwalsh.name/combining-js-arrays 8 | * 9 | * Tip: push() adds elements to the end of the array. To add it to the beginning, use unshift() 10 | * 11 | * Perf comparison: 12 | * https://jsperf.com/array-prototype-push-apply-vs-concat/20 13 | */ 14 | 15 | // PUSHES ONE ARRAY INTO ANOTHER 16 | // Array.push() modifies the original array it's pushed to 17 | // The return value of the push operation is the length of the returned array. 18 | (function () { 19 | var testArr = [1, 2, 3]; 20 | var res = testArr.push(4, 5, 6); 21 | 22 | console.log(res); // 6 23 | console.log(testArr); // [1, 2, 3, 4, 5, 6] 24 | })(); 25 | 26 | // MERGES ARRAYS 27 | // Array.concat() returns a new array as the result. The original array is unchanged. 28 | // Gotcha: In the case of objects, instead of copying objects into the new array, the references are copied instead. 29 | (function () { 30 | var test = [1, 2, 3]; // [1, 2, 3] 31 | var example = [{test: 'test value'}, 'a', 'b', 4, 5]; 32 | var concatExample = test.concat(example); // [1, 2, 3, { test: 'test value'}, 'a', 'b', 4, 5] 33 | 34 | // Modifying values 35 | example[0].test = 'a changed value'; 36 | console.log(concatExample[3].test); // Object { test: "a changed value"} 37 | example[1] = 'dog'; 38 | console.log(concatExample[4]); // 'a' 39 | })(); 40 | 41 | // MERGE ARRAY USING push() 42 | // Clever use of apply() to join 2 arrays 43 | (function () { 44 | var a = [1, 2]; 45 | var b = ['x', 'y']; 46 | 47 | // WE DONT WANT a.push(b) since it returns [1, 2, ['x', 'y']]; 48 | a.push.apply(a, b); 49 | console.log(a); // [1, 2, 'x', 'y'] 50 | // Alternative: a = a.concat(b); 51 | })(); -------------------------------------------------------------------------------- /js/array-every-some.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array every() and some() (instead of forEach) 3 | * 4 | * Another limitation with forEach is that you can’t break out of the loop (and no, using exceptions doesn’t count). 5 | * As a result, I’ve seen developers either revert back to for loops when needing to be able to break out, 6 | * or needlessly iterate over extraneous array elements. 7 | * 8 | * A better solution exists in the form of the lesser known every() and some() array iteration methods. 9 | * every iterates until the provided callback returns false, and some iterates until the provided callback returns true. 10 | * 11 | * Both every and some have the same browser support as forEach. 12 | * 13 | * @Reference: 14 | * http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ 15 | * https://coderwall.com/p/_ggh2w/the-array-native-every-filter-map-some-foreach-methods 16 | * 17 | */ 18 | 19 | // some() breaks once it returns true 20 | (function () { 21 | // God of cricket 22 | var ar = ['Lara', 'Sachin', 'De Villiers']; 23 | ar.some(function (v) { 24 | if (v === 'Sachin') { 25 | return true; 26 | } 27 | console.log('Great cricketers: ' + v); 28 | }); 29 | })(); 30 | 31 | // every() breaks once it returns false 32 | (function () { 33 | // Music Composers 34 | var ar = ['Hans Zimmer', 'Bill Clinton', 'Clint Mansell']; 35 | ar.every(function (v) { 36 | if (v === 'Bill Clinton') { 37 | return false; 38 | } 39 | console.log('Great Composers: ' + v); 40 | }); 41 | })(); 42 | 43 | // every() and some() in an example 44 | (function () { 45 | function isBigEnough(element) { 46 | return element >= 10; 47 | } 48 | 49 | function isBigEnough2(element) { 50 | return element >= 1; 51 | } 52 | 53 | var passed = [2, 5, 8, 1, 4].some(isBigEnough); 54 | console.log('some: For [2, 5, 8, 1, 4] are the values larger or equal to 10 ? ' + passed); 55 | // some: For [2, 5, 8, 1, 4] are the values larger or equal to 10 ? false 56 | 57 | var passed = [12, 5, 8, 1, 4].some(isBigEnough); 58 | console.log('some: For [12, 5, 8, 1, 4] are the values larger or equal to 10 ? ' + passed); 59 | // some: For [12, 5, 8, 1, 4] are the values larger or equal to 10 ? true 60 | 61 | var passed = [12, 5, 8, 1, 4].every(isBigEnough); 62 | console.log('every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 10 ? ' + passed); 63 | // every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 10 ? false 64 | 65 | var passed = [12, 5, 8, 1, 4].every(isBigEnough2); 66 | console.log('every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 1 ? ' + passed); 67 | // every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 1 ? true 68 | 69 | })(); -------------------------------------------------------------------------------- /js/array-filter-map-reduce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array filter(), map() and reduce() 3 | * 4 | * @Reference: 5 | * http://danmartensen.svbtle.com/javascripts-map-reduce-and-filter 6 | * http://elijahmanor.com/reducing-filter-and-map-down-to-reduce/ 7 | * http://cryto.net/~joepie91/blog/2015/05/04/functional-programming-in-javascript-map-filter-reduce/ 8 | * 9 | * Advanced: 10 | * Implementing filter, map, reduce internals in JS: 11 | * http://matthewodette.com/map-filter-and-fold-in-javascript/ 12 | */ 13 | 14 | /** Normal for() loop 15 | * for loops still definitely have a place when working with large arrays 16 | * (e.g. over 1,000 elements) or needing to break the traversal if a condition is met. 17 | */ 18 | (function () { 19 | var array = [1, 2, 3, 4]; 20 | var models = []; 21 | for (var i = 0; i < array.length; i++) { 22 | if (array.indexOf(array[i]) % 2 === 0) { 23 | models.push(array[i]); 24 | } 25 | } 26 | })(); 27 | 28 | /** Array.map() 29 | * 30 | * Use when: 31 | * You want to translate/map all elements in an array to another set of values. 32 | * 33 | * What it does: 34 | * Traverses the array from left to right invoking a callback function on each element with parameters. 35 | * For each callback the value returned becomes the element in the new array. 36 | * After all the elements have been traversed the map() returns the new array with all the translated elements. 37 | * 38 | * eg. Convert Farenheit into Celcius 39 | * 40 | * Format: 41 | * array.map(function(elem, index, array) { 42 | * ... 43 | * }, thisArg); 44 | * 45 | * elem: element value 46 | * index: index in each traversal, moving left to right 47 | * array: original array invoking the method 48 | * thisArg: (Optional) object that will be referred to as 'this' in the callback 49 | */ 50 | 51 | (function () { 52 | var farenheit = [0, 32, 45, 55, 67, 79, 94, 105]; 53 | var celcius = farenheit.map(function (elem) { 54 | return Math.round((elem - 32) * 5 / 9); 55 | }); 56 | 57 | console.log(celcius); // [-18, 0, 7, 13, 19, 26, 34, 41] 58 | })(); 59 | 60 | /** Array.filter() 61 | * 62 | * Use when: 63 | * You want to remove unwanted elements based on a condition. 64 | * 65 | * What it does: 66 | * Like map() it traverses the array from left to right invoking a callback function on each element. 67 | * The returned value must be a boolean identifying whether the element will be kept or discarded. 68 | * After all the elements have been traversed filter() returns a new array with all elements that returned true. 69 | * It has the same parameters as map(). 70 | * 71 | * eg. Remove duplicates from an array 72 | * 73 | * Format: 74 | * array.filter(function(elem, index, array) { 75 | * ... 76 | * }, thisArg); 77 | * 78 | * elem: element value 79 | * index: index in each traversal, moving left to right 80 | * array: original array invoking the method 81 | * thisArg: (optional) object that will be referred to as 'this' in the callback 82 | */ 83 | 84 | (function () { 85 | var arr = [1, 2, 3, 4, 5, 3, 7, 2]; 86 | var uniqueArr = arr.filter(function (elem, i, arr) { 87 | return arr.indexOf(elem) === i; 88 | }); 89 | 90 | console.log(uniqueArr); 91 | })(); 92 | 93 | /** Array.reduce() 94 | * 95 | * Use when: 96 | * You want to find a cumulative or concatenated value based on elements across the array. 97 | * 98 | * What it does: 99 | * Like map() it traverses the array from left to right invoking a callback function on each element. 100 | * The value returned is the cumulative value passed from callback to callback. 101 | * After all elements have been traversed reduce() returns the cumulative value. 102 | * 103 | * eg. Sum up countries orbital rocket launches in 2014. 104 | * 105 | * Format: 106 | * array.reduce(function(prevVal, elem, index, array) { 107 | * ... 108 | * }, initialValue); 109 | * 110 | * prevVal: Cumulative value returned through each callback 111 | * elem: element value 112 | * index: index of traversal, moving left to right 113 | * array: original array invoking the method 114 | * initialValue: (Optional) object used as first argument in the first (leftmost) callback. 115 | * 116 | */ 117 | (function () { 118 | var rockets = [ 119 | {country: 'Russia', launches: 32}, 120 | {country: 'US', launches: 23}, 121 | {country: 'China', launches: 16}, 122 | {country: 'Europe(ESA)', launches: 7}, 123 | {country: 'India', launches: 4}, 124 | {country: 'Japan', launches: 3} 125 | ]; 126 | 127 | var sum = rockets.reduce(function (prevVal, elem) { 128 | return prevVal + elem.launches; 129 | }, 0); 130 | 131 | console.log(sum); 132 | })(); 133 | -------------------------------------------------------------------------------- /js/array-foreach.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array foreach() 3 | * 4 | * @Reference: 5 | * http://stackoverflow.com/questions/23614054/javascript-nuances-of-myarray-foreach-vs-for-loop 6 | * http://javascriptplayground.com/blog/2012/06/writing-javascript-polyfill/ 7 | * http://www.2ality.com/2011/04/iterating-over-arrays-and-objects-in.html 8 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach 9 | * 10 | */ 11 | 12 | // Polyfill for forEach() 13 | Array.prototype.forEach = function (callback, thisArg) { 14 | if (typeof callback !== 'function') { 15 | throw new TypeError(callback + ' is not a function.'); 16 | } 17 | var len = this.length; // Array length 18 | for (var i = 0; i < len; i++) { 19 | callback.call(thisArg, this[i], i, this); 20 | } 21 | }; 22 | 23 | // Example of forEach() 24 | function logArrayElements(currElement, currIndex, originalArray) { 25 | console.log('a[' + currIndex + '] = ' + currElement); 26 | } 27 | 28 | // Note there is no member at 2 so it isn't visited 29 | [2, 5, , 9].forEach(logArrayElements); 30 | // logs: 31 | // a[0] = 2 32 | // a[1] = 5 33 | // a[3] = 9 -------------------------------------------------------------------------------- /js/array-pass-by-val-reference.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Understanding arrays - Pass by reference vs Pass by value 3 | * 4 | * @Reference: 5 | * http://orizens.com/wp/topics/javascript-arrays-passing-by-reference-or-by-value/ 6 | */ 7 | 8 | // Pass by reference 9 | // By default - Arrays are passed to a function by reference 10 | var a = [3, 'my new post', {345}]; 11 | 12 | function renderData(a) { 13 | a.push(4); 14 | } 15 | 16 | renderData(a); 17 | alert(a); // will output the new a - [3, 'my new post', {345}, 4] 18 | 19 | // Pass by value 20 | // This can be done by using the native array method – “slice()” 21 | // 22 | // Basically, the slice() operation clones the array and returns the reference to the new array. Also note that: 23 | // - For object references (and not the actual object), slice copies object references into the new array. 24 | // Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays. 25 | // explain: 26 | !function() { 27 | 28 | // object 29 | var obj = {"abc": 456}; 30 | var arr = [obj].slice(); // [{"abc": 456}] 31 | obj.abc = 4567; 32 | console.log(arr, obj); // [{"abc": 4567}] {"abc": 4567} 33 | 34 | // array 35 | var oldarr = [456]; 36 | var arr = [oldarr].slice(); // [[456]] 37 | oldarr[0] = 4567; 38 | console.log(arr, oldarr); // [[4567]] [4567] 39 | 40 | }() 41 | 42 | // - For strings and numbers, slice copies strings and numbers into the new array. 43 | // Changes to the string or number in one array does not affect the other array. 44 | // explain: 45 | !function() { 46 | 47 | // string in array 48 | var oldarr = ['abc']; 49 | var arr = oldarr.slice(); // ['abc'] 50 | oldarr[0] = 'abcd'; 51 | console.log(arr, oldarr); // ['abc'] ['abcd'] 52 | 53 | // number in array 54 | var oldarr = [123, 456, 789]; 55 | var arr = oldarr.slice(0, 2); // [123, 456] 56 | oldarr[1] = 123456789; 57 | console.log(arr, oldarr); // [123, 456] [123, 123456789, 789] 58 | 59 | }() 60 | 61 | var a = [3, 'my new post', {345}]; 62 | 63 | function renderData(a) { 64 | a.push(4); 65 | } 66 | 67 | renderData(a.slice()); 68 | alert(a); // will output the original a - [3, 'my new post', {345}] 69 | 70 | // If you did want to copy array's object references by value -- Use JSON.parse(JSON.stringify(array)) 71 | // Note: It does have a few caveats with copying functions/dates as values. 72 | // For more info check: https://github.com/vasanthk/js-bits/blob/master/js/object-clone.js 73 | var tempArray = JSON.parse(JSON.stringify(mainArray)); -------------------------------------------------------------------------------- /js/array-reduce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Iterating over an array with reduce() 3 | * 4 | * Iterating over arrays using forEach is a nicer, more modern, and seemingly more functional approach than an old-fashioned for loop. 5 | * I say “seemingly” because any operation performed inside forEach can only return results via side-effects, or by modifying the original array. 6 | * However, a more functional approach is to use other iteration methods available for arrays, such as map and reduce. 7 | * These methods don’t require side-effects, and can treat the original array as immutable. 8 | * 9 | * Both reduce and map have the same browser support as forEach. 10 | * 11 | * When you hear people talking about "Map Reduce" they are just talking about a "pattern": Mapping over a collection and then reducing it. 12 | * 13 | * @Reference: 14 | * http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ 15 | * http://danmartensen.svbtle.com/javascripts-map-reduce-and-filter 16 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce 17 | * https://medium.freecodecamp.com/reduce-f47a7da511a9 18 | */ 19 | 20 | // Using forEach() 21 | 22 | (function () { 23 | var ar = [1, 2, 3, 4, 5]; 24 | var sum = 0; 25 | ar.forEach(function (v) { 26 | sum += v; 27 | }); 28 | console.log(sum); 29 | })(); 30 | 31 | // Using reduce() 32 | 33 | (function () { 34 | var ar = [1, 2, 3, 4, 5]; 35 | // Reduce does not have a variable sum in it's outer scope (like in forEach) 36 | console.log('sum:', ar.reduce(function (sum, v) { 37 | return sum + v; 38 | }, 0)); 39 | // reduce() format: arr.reduce(callback()[, initialValue]) 40 | // callback format: fn(previousValue, currentValue, index, array) 41 | })(); 42 | 43 | 44 | /* Understand reduce 45 | * Array.prototype.reduce(function(prev,curr,index,arr){ 46 | * return result; 47 | * },initprev); 48 | * 49 | * reduce(func,initprev), actually accept 4 params in first function: prev: previous, curr: current, index: current index, arr: original array 50 | * reduce(func,initprev), initprev: set a default value as prev, if there is ot initprev, index will be started from 1, prev = arr[0] 51 | * 52 | * 53 | * Example without initprev,loop times: (arr.length -1) 54 | * [1,2,3].reduce(function(pre,curr,index,arr){ 55 | * console.log("pre:"+pre +" curr:" + curr + " index:"+ index +" arr:" +arr); 56 | * return curr; 57 | * }); 58 | * output: pre:1 curr:2 index:1 arr:1,2,3; pre:2 curr:3 index:2 arr:1,2,3; 59 | * 60 | * Example with initprev, loop times: arr.length 61 | * [1,2,3].reduce(function(pre,curr,index,arr){ 62 | * console.log("pre:"+pre +" curr:" + curr + " index:"+ index +" arr:" +arr); 63 | * return curr; 64 | * },4); 65 | * output: pre:4 curr:1 index:0 arr:1,2,3; pre:1 curr:2 index:1 arr:1,2,3; pre:2 curr:3 index:2 arr:1,2,3; 66 | * 67 | */ 68 | 69 | /* use reduce to change inner array to object*/ 70 | let arr = [ 71 | ['a', 1], 72 | ['b', 2] 73 | ]; 74 | function inrArrToObj(arr){ 75 | const result = arr.reduce(function(prev,curr){ 76 | const obj = { 77 | [curr[0]]:curr[1] 78 | } 79 | prev.push(obj); 80 | return prev; 81 | },[]); 82 | return result; 83 | } 84 | inrArrToObj(arr); 85 | // [{'a':1},{'b':2}] 86 | 87 | /* use reduce to change inner array to object*/ 88 | let arr = [ 89 | ['a', 1], 90 | ['b', 2] 91 | ]; 92 | function inrArrToObj(arr){ 93 | const result = arr.reduce(function(prev,curr){ 94 | prev[curr[0]] = curr[1]; 95 | return prev; 96 | },{}); 97 | return result; 98 | }; 99 | 100 | inrArrToObj(arr); 101 | // output: {a:1,b:2} 102 | 103 | -------------------------------------------------------------------------------- /js/array-slice-splice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array slice() vs splice() 3 | * 4 | * 1. The splice() method returns the removed item(s) in an array and slice() method returns the selected element(s) in an array, as a new array object. 5 | * 6 | * 2. The splice() method changes the original array and slice() method doesn’t change the original array. 7 | * 8 | * 3. The splice() method can take n number of arguments: 9 | * Argument 1: Index, Required. An integer that specifies at what position to add /remove items, Use negative values to specify the position from the end of the array. 10 | * Argument 2: Optional. The number of items to be removed. If set to 0(zero), no items will be removed. And if not passed, all item(s) from provided index will be removed. 11 | * Argument 3…n: Optional. The new item(s) to be added to the array. 12 | * 13 | * 4. The slice() method can take 2 arguments: 14 | * Argument 1: Required. An integer that specifies where to start the selection (The first element has an index of 0). Use negative numbers to select from the end of an array. If this is undefined, slice begins from index 0 15 | * Argument 2: Optional. An integer that specifies where to end the selection. If omitted, all elements from the start position and to the end of the array will be selected. Use negative numbers to select from the end of an array. 16 | * 17 | * @Reference: 18 | * http://www.tothenew.com/blog/javascript-splice-vs-slice/ 19 | */ 20 | 21 | // Array.splice() 22 | // Format: array.splice(start, deleteCount[, item1[, item2[, ...]]]) 23 | var array = [1, 2, 3, 4, 5]; 24 | console.log(array.splice(2)); 25 | // shows [3, 4, 5], returned removed item(s) as a new array object. 26 | 27 | console.log(array); 28 | // shows [1, 2], original array altered. 29 | 30 | var array2 = [6, 7, 8, 9, 0]; 31 | console.log(array2.splice(2, 1)); 32 | // shows [8] 33 | 34 | console.log(array2.splice(2, 0)); 35 | //shows [] , as no item(s) removed. 36 | 37 | console.log(array2); 38 | // shows [6,7,9,0] 39 | 40 | var array3 = [11, 12, 13, 14, 15]; 41 | console.log(array3.splice(2, 1, "Hello", "World")); 42 | // shows [13] 43 | 44 | console.log(array3); 45 | // shows [11, 12, "Hello", "World", 14, 15] 46 | 47 | // -6 -5 -4 -3 -2 -1 48 | // | | | | | | 49 | var array4 = [16, 17, 18, 19, 20]; 50 | // | | | | | | 51 | // 0 1 2 3 4 5 52 | 53 | console.log(array4.splice(-2, 1, "me")); 54 | // shows [19] 55 | 56 | console.log(array4); 57 | // shows [16, 17, 18, "me", 20] 58 | 59 | 60 | // If Argument(1) is NaN, it is treated as if it were 0. 61 | var array5 = [21, 22, 23, 24, 25]; 62 | console.log(array5.splice(NaN, 4, "NaN is Treated as 0")); 63 | // shows [21,22,23,24] 64 | 65 | console.log(array5); 66 | // shows ["NaN is Treated as 0",25] 67 | 68 | 69 | // If 2nd argument is less than 0 or equal to NaN, it is treated as if it were 0. 70 | var array6 = [26, 27, 28, 29, 30]; 71 | console.log(array6.splice(2, -5, "Hello")); 72 | // shows [] 73 | 74 | console.log(array6); 75 | // shows [26,27,"Hello",28,29,30] 76 | 77 | console.log(array6.splice(3, NaN, "World")); 78 | // shows [] 79 | 80 | console.log(array6); 81 | // shows [26,27,"Hello","World",28,29,30] 82 | 83 | 84 | // If Argument(1) or Argument(2) is greater than Array’s length, either argument will use the Array’s length. 85 | var array7 = [31, 32, 33, 34, 35]; 86 | console.log(array7.splice(23, 3, "Add Me")); 87 | // shows [] 88 | 89 | console.log(array7); 90 | // shows [31,32,33,34,35,"Add Me"] 91 | 92 | console.log(array7.splice(2, 34, "Add Me Too")); 93 | // shows [33,34,35,"Add Me"] 94 | 95 | console.log(array7); 96 | // shows [31,32,"Add Me Too"] 97 | 98 | 99 | // The slice() method can take 2 arguments: 100 | // arr.slice([begin[, end]]) 101 | // slice extracts up to but not including end argument. 102 | var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']; 103 | var citrus = fruits.slice(1, 3); 104 | // citrus contains ['Orange','Lemon'] 105 | 106 | var array = [1, 2, 3, 4, 5]; 107 | console.log(array.slice(2)); 108 | // shows [3, 4, 5], returned selected element(s). 109 | 110 | console.log(array.slice(-2)); 111 | // shows [4, 5], returned selected element(s). 112 | console.log(array); 113 | // shows [1, 2, 3, 4, 5], original array remains intact. 114 | 115 | var array2 = [6, 7, 8, 9, 0]; 116 | console.log(array2.slice(2, 4)); 117 | // shows [8, 9] 118 | 119 | console.log(array2.slice(-2, 4)); 120 | // shows [9] 121 | 122 | console.log(array2.slice(-3, -1)); 123 | // shows [8, 9] 124 | 125 | console.log(array2); 126 | // shows [6, 7, 8, 9, 0] 127 | 128 | // If either argument is NaN, it is treated as if it were 0. 129 | var array3 = [11, 12, 13, 14, 15]; 130 | console.log(array3.slice(NaN, NaN)); 131 | // shows [] 132 | 133 | console.log(array3.slice(NaN, 4)); 134 | // shows [11,12,13,14] 135 | 136 | console.log(array3); 137 | // shows [11,12,13,14,15] 138 | 139 | // If either argument is greater than the Array’s length, either argument will use the Array’s length 140 | var array4 = [16, 17, 18, 19, 20]; 141 | console.log(array4.slice(23, 24)); 142 | // shows [] 143 | 144 | console.log(array4.slice(23, 2)); 145 | // shows [] 146 | 147 | console.log(array4.slice(2, 23)); 148 | // shows [18,19,20] 149 | 150 | console.log(array4); 151 | // shows [16,17,18,19,20] 152 | 153 | // If begin is undefined slice starts from 0 154 | var array5 = [21, 22, 23, 24, 25]; 155 | console.log(array5.slice(undefined, 2)); 156 | // shows [21, 22] 157 | 158 | //slice can be used to copy an array by ommiting both begin and end 159 | var array6 = array5.slice(); 160 | console.log(array6); 161 | //shows [21, 22, 23, 24, 25] 162 | -------------------------------------------------------------------------------- /js/bind-function.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function.prototype.bind() 3 | * 4 | * The bind function actually returns a new function, with the `this` value of the new function set to what you provide as the argument. 5 | * 6 | * Polyfill implementation below (for < IE9) 7 | * 8 | * @Reference: 9 | * https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/ 10 | * http://stackoverflow.com/a/10115970/1672655 11 | * http://ejohn.org/apps/learn/#86 12 | * 13 | * Complex Scenario with promises: 14 | * http://adgllorente.com/2016/03/to-bind-or-not-to-bind/ 15 | */ 16 | 17 | // Polyfill for bind() 18 | Function.prototype.bind = function () { 19 | var fn = this; 20 | var args = Array.prototype.slice.call(arguments); 21 | var context = args.shift(); 22 | 23 | return function () { 24 | return fn.apply(context, args.concat(Array.prototype.slice.call(arguments))); 25 | }; 26 | }; 27 | 28 | // What Problem Are We Actually Looking To Solve? 29 | var myObj = { 30 | specialFunction: function () { 31 | }, 32 | anotherSpecialFunction: function () { 33 | }, 34 | getAsyncData: function (cb) { 35 | cb(); 36 | }, 37 | render: function () { 38 | var that = this; 39 | this.getAsyncData(function () { 40 | that.specialFunction(); 41 | that.anotherSpecialFunction(); 42 | }); 43 | } 44 | }; 45 | 46 | myObj.render(); 47 | // If we had left our function calls as this.specialFunction(), then we would have received the following error: 48 | // Uncaught TypeError: Object [object global] has no method 'specialFunction' 49 | 50 | 51 | // Solving it with bind() 52 | // We need to keep the context of the myObj object referenced for when the callback function is called. 53 | // Calling that.specialFunction() enables us to maintain that context and correctly execute our function. 54 | // However, this could be neatened somewhat by using Function.prototype.bind(). 55 | // Let’s rewrite our example: 56 | var myObj = { 57 | specialFunction: function () { 58 | }, 59 | anotherSpecialFunction: function () { 60 | }, 61 | getAsyncData: function (cb) { 62 | cb(); 63 | }, 64 | render: function () { 65 | this.getAsyncData(function () { 66 | this.specialFunction(); 67 | this.anotherSpecialFunction(); 68 | }.bind(this)); 69 | } 70 | }; 71 | 72 | // Note: 73 | // One important thing to remember is that if you use bind on a prototype method, it creates an instance-level method, 74 | // which bypasses the advantages of having methods on the prototype. It’s not wrong, just something to be aware of. 75 | 76 | // Use Cases 77 | // 78 | // 1) Anywhere with callback functions - eg. Click handlers, setTimeout etc. 79 | // With bind(), the `this` context is available when the callback fn is called later on. 80 | var Button = function (content) { 81 | this.content = content; 82 | }; 83 | 84 | Button.prototype.click = function () { 85 | console.log(this.content + ' clicked'); 86 | }; 87 | 88 | var myButton = new Button('OK'); 89 | myButton.click(); 90 | 91 | var looseClick = myButton.click; 92 | looseClick(); // not bound, 'this' is not myButton - it is the global object 93 | 94 | var boundClick = myButton.click.bind(myButton); 95 | boundClick(); // bound, 'this' is myButton 96 | // Which prints out: 97 | // OK clicked 98 | // undefined clicked 99 | // OK clicked 100 | 101 | 102 | // One use is to track clicks (or to perform an action after a click) that might require us to store information in an object, like so: 103 | var logger = { 104 | x: 0, 105 | updateCount: function () { 106 | this.x++; 107 | console.log(this.x); 108 | } 109 | }; 110 | 111 | document.querySelector('button').addEventListener('click', function () { 112 | logger.updateCount(); 113 | }); 114 | 115 | // Use Cases 116 | // 117 | // 2) For Partial Application 118 | // You can also add extra parameters after the 1st parameter and bind will pass in those values to the original function 119 | // before passing in the extra parameters you pass to the bound function: 120 | 121 | // Example showing binding some parameters 122 | var sum = function (a, b) { 123 | return a + b; 124 | }; 125 | 126 | var add5 = sum.bind(null, 5); 127 | console.log(add5(10)); // 15 128 | 129 | 130 | // Comparative study of these three methods 131 | // bind() vs call() vs apply() 132 | 133 | // what is bind ? 134 | // The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. 135 | // what is call ? 136 | // The call() method calls a function with a given this value and arguments provided individually. 137 | // what is apply ? 138 | // The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object). 139 | // 140 | // first , We usually compare call with apply at the same time , why ? 141 | // Syntax( call ): 142 | // fun.call(thisArg[, arg1[, arg2[, ...]]]) 143 | // Syntax( apply ): 144 | // fun.apply(thisArg, [argsArray]) 145 | // 146 | // ============= 147 | // 148 | // same point: this.Arg , if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed. 149 | // difference: other args. call use many arg for the object , apply() use an array or an array-like 150 | // call eg: 151 | // Area of a circle 152 | var π = 3.14; 153 | var s = function(r) { 154 | return this.π*r*r; 155 | } 156 | 157 | function pi() { 158 | this.π = Math.PI; 159 | return this; 160 | } 161 | s(1); // 3.14 162 | s.call(pi(), 1); // 3.141592653589793… 163 | 164 | // Sometimes we use it like this 165 | function toArray() { 166 | return [].slice.call(arguments); 167 | } 168 | toArray(1, 2, 3); 169 | 170 | // apply eg: 171 | // This method is learned in lodash. 172 | !function() { 173 | function apply(fun, thisArg, args) { 174 | var length = args.length; 175 | switch() { 176 | case 0: return fun.call(thisArg); 177 | case 1: return fun.call(thisArg, args[0]); 178 | case 2: return fun.call(thisArg, args[0], args[1]); 179 | case 3: return fun.call(thisArg, args[0], args[1], args[2]); 180 | } 181 | return fun.apply(thisArg, args); 182 | } 183 | }() 184 | 185 | // second, The previous use of bind is in the jquery 186 | // $(document).bind('click', function() { 187 | // console.log(document.title); 188 | // }) 189 | // 190 | 191 | // but this bind is Function.prototype.bind(); 192 | // Partial Functions (分离函数) 193 | !function() { 194 | function list() { 195 | return Array.prototype.slice.call(arguments); 196 | } 197 | 198 | var list1 = list(1, 2, 3); // [1, 2, 3] 199 | 200 | // Create a function with a preset leading argument 201 | var leadingThirtysevenList = list.bind(undefined, 37); 202 | 203 | var list2 = leadingThirtysevenList(); // [37] 204 | var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3] 205 | } 206 | 207 | // @Reference 208 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind 209 | // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind -------------------------------------------------------------------------------- /js/bitwise-operators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitwise operators in JavaScript 3 | * 4 | * @Reference: 5 | * http://michalbe.blogspot.com/2013/03/javascript-less-known-parts-bitwise.html 6 | * http://www.w3schools.com/jsref/jsref_operators.asp 7 | * http://stackoverflow.com/questions/654057/where-would-i-use-a-bitwise-operator-in-javascript 8 | */ 9 | 10 | // Various bitwise operators 11 | var a = 5; 12 | var b = 13; 13 | 14 | // a | b - OR 15 | // if in any of the given numbers corresponding bit is '1', then the result is '1' 16 | console.log('or', a | b); // 13 17 | 18 | // a & b - AND 19 | // if in both of the given numbers corresponding bit is '1', then the result is '1' 20 | console.log('and', a & b); // 5 21 | 22 | // a ^ b - XOR 23 | // if in one of the given numbers (not both) corresponding bit is '1', then the result is '1' 24 | console.log('xor', a ^ b); // 8 25 | 26 | // ~a - NOT 27 | // inverts all the bits 28 | console.log('not', ~a); // -6 29 | 30 | // a >> b - RIGHT SHIFT 31 | // shift binary representation of 'a' for 'b' bits to the right, discarding bits shifted off 32 | console.log('rs', a >> b); // 0 33 | 34 | // a << b - LEFT SHIFT 35 | // shift binary representation of 'a' for 'b' bits to the right, shifting in zeros from the right 36 | console.log('ls', a << b); // 40960 37 | 38 | // a >>> b - ZERO FILLED RIGHT SHIFT 39 | // shift binary representation of 'a' for 'b' bits to the right, discarding bits shifted off, 40 | // and shifting in zeros from the left. 41 | console.log('zfrs', a >>> b); // 0 42 | 43 | 44 | // Practical Use cases of Bitwise operators in JavaScript 45 | var hex = 'ffaadd'; 46 | var rgb = parseInt(hex, 16); // value is 1675421 47 | 48 | // & 0xFF ensures that if bytes are longer than 8 bits, the rest of the bits are cleared. 49 | // http://stackoverflow.com/a/14713134/1672655 50 | var red = (rgb >> 16) & 0xFF; // returns 255 51 | var green = (rgb >> 8) & 0xFF; // 170 52 | var blue = rgb & 0xFF; // 221 53 | 54 | // In JavaScript, you can use a double bitwise negation (~~n) as a replacement for Math.floor(n) 55 | // (if n is a positive number) or parseInt(n, 10) (even if n is negative). n|n and n&n always yield the same results as ~~n. 56 | var n = Math.PI; 57 | n; // 3.141592653589793 58 | Math.floor(n); // 3 59 | parseInt(n, 10); // 3 60 | ~~n; // 3 61 | n | n; // 3 62 | n & n; // 3 63 | 64 | // ~~n works as a replacement for parseInt() with negative numbers… 65 | ~~(-n); // -3 66 | (-n) | (-n); // -3 67 | (-n) & (-n); // -3 68 | parseInt(-n, 10); // -3 69 | // …although it doesn’t replace Math.floor() for negative numbers 70 | Math.floor(-n); // -4 71 | 72 | // To convert integers to binary 73 | // To check dec number's binary value, we use .toString() method with base argument - '2' for binary 74 | var number = 5; 75 | console.log(number.toString(2)); // 101 76 | 77 | // Swap variables (Using XOR) 78 | // details: http://en.wikipedia.org/wiki/XOR_swap_algorithm 79 | var a = 73; 80 | var b = 89; 81 | a^=b; 82 | b^=a; 83 | a^=b; 84 | console.log('a', a); 85 | console.log('b', b); -------------------------------------------------------------------------------- /js/call-apply-function.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function.prototype.call & Function.prototype.apply 3 | * 4 | * Execute functions with a given context and arguments. 5 | * 6 | * @Reference: 7 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply 8 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call 9 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments 10 | * http://javascriptissexy.com/javascript-apply-call-and-bind-methods-are-essential-for-javascript-professionals/ 11 | */ 12 | 13 | // What can we do with this function? 14 | function logFullName(firstName, lastName) { 15 | console.log(firstName + lastName); 16 | } 17 | 18 | // Executing logFullName with apply takes the arguments as an Array 19 | logFullName.apply(undefined, ['Jhon', 'Doe']) // Jhon Doe 20 | 21 | // Executing logFullName with call takes individual arguments 22 | logFullName.call(undefined, 'jhon', 'doe') // jhon doe 23 | 24 | // The first parameter of call && apply is the context the function is going 25 | // to be executed with 26 | function logFullNameWithContext() { 27 | console.log(this.firstName, this.lastName); 28 | } 29 | 30 | var me = { 31 | firstName: 'Jhon', 32 | lastName: 'Doe', 33 | fullName: function() { 34 | logFullNameWithContext.call(this); 35 | } 36 | }; 37 | 38 | me.fullName() // 'Jhon Doe' 39 | 40 | // We can do crazy stuff like supporting 41 | // both array and individual arguments in our functions 42 | function sumAll() { 43 | if (!arguments.length) return; 44 | 45 | if (Array.isArray(arguments[0])) { 46 | // call sumAll with the array argument 47 | return sumAll.apply(this, arguments[0]); 48 | } 49 | 50 | // arguments is an array like objects, we call slice on it to get an Array 51 | return Array.prototype.slice.call(arguments).reduce(function(prev, curr) { 52 | return prev + curr; 53 | }); 54 | } 55 | 56 | sumAll([1,2,3]) // 6 57 | sumAll(1,2,3) // 6 58 | sumAll.call(undefined, 1, 2, 3) // 6 59 | 60 | // We can also expose functions that let the user choose the context 61 | // of their callbacks 62 | function requestSomething(cb, context) { 63 | var something = 'something!'; 64 | 65 | // calling the function with the given context. If no context is 66 | // provided, context will be undefined so cb will never have access to the 67 | // requestSomething context 68 | cb.call(context, something); 69 | } 70 | 71 | requestSomething(function(something) { 72 | console.log(something); 73 | console.log(this); 74 | }, { hello: 'World!'}); // this prints: something! Object 75 | 76 | 77 | // One of most useful things we can do with apply and call is borrowing methods 78 | 79 | Array.prototype.forEach.call('Jhon', function(char) { 80 | console.log(char); 81 | }); // this prints each char individually 82 | 83 | // Also, apply can help us with 84 | // variadic functions (varying number of parameters) 85 | 86 | // like finding the minimum of an array 87 | Math.min.apply(undefined, [1,2,3,5]) // 1 88 | 89 | // or concatenating two arrays 90 | var a = [1,2,3,4]; 91 | a.concat.apply(a, [5,6,7,8]) // [1,2,3,4,5,6,7,8] 92 | -------------------------------------------------------------------------------- /js/closures.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Closures 3 | * 4 | * Closure is when a function 'remembers' its lexical scope even when the function is executing outside that lexical scope. ~ Kyle Simpson 5 | * 6 | * Closures are useful in hiding the implementation of functionality while still revealing the interface. 7 | * 8 | * Why use Closures? 9 | * http://howtonode.org/why-use-closure 10 | * 11 | * @Reference: 12 | * http://stackoverflow.com/questions/2728278/what-is-a-practical-use-for-a-closure-in-javascript 13 | * https://medium.freecodecamp.com/lets-learn-javascript-closures-66feb44f6a44#.lwnf9bay4 14 | * http://www.bennadel.com/blog/2134-a-random-exploration-of-closure-use-cases-in-javascript.htm 15 | * https://medium.com/written-in-code/practical-uses-for-closures-c65640ae7304#.ukk9dpjxs 16 | * https://medium.com/@nickbalestra/javascripts-lexical-scope-hoisting-and-closures-without-mystery-c2324681d4be#.bg7fk0chp 17 | * https://www.safaribooksonline.com/library/view/javascript-the-good/9780596517748/ch04s15.html 18 | * https://community.risingstack.com/explaining-javascript-closure-scope-chain-examples/ 19 | */ 20 | 21 | // EXAMPLE 1 22 | function foo() { 23 | var bar = 'bar'; 24 | 25 | function baz() { 26 | console.log(bar); 27 | } 28 | 29 | bam(baz); 30 | } 31 | 32 | function bam(baz) { 33 | // Prints 'bar' -- because baz() which is called inside bam's lexical scope has access to `bar` inside foo() 34 | baz(); // bar 35 | } 36 | foo(); 37 | 38 | // EXAMPLE 2 39 | (function foo() { 40 | var bar = 'bar'; 41 | 42 | setTimeout(function () { 43 | console.log(bar); // Prints `bar` -- Due to closures - coz setTimout's callback fn has access to foo's lexical scope. 44 | }, 1000) 45 | })(); 46 | 47 | // EXAMPLE 3 48 | (function foo() { 49 | var bar = 'bar'; 50 | 51 | $('#btn').click(function () { 52 | console.log(bar); // Prints `bar` 53 | }); 54 | })(); 55 | 56 | 57 | // PRACTICAL USE CASES 58 | 59 | // 1. To enforce public/private methods. [Classic Module Pattern] 60 | 61 | /** 62 | * As you can see there, a is now an object, with a method publicfunction ( a.publicfunction() ) which calls privatefunction, 63 | * which only exists inside the closure. 64 | * 65 | * You can NOT call privatefunction directly (i.e. a.privatefunction() ), just publicfunction(). 66 | */ 67 | var a = (function () { 68 | var privateFunction = function () { 69 | console.log('Accessed private method'); 70 | }; 71 | 72 | return { 73 | publicFunction: function () { 74 | privateFunction(); 75 | } 76 | } 77 | })(); 78 | a.publicFunction(); // Accessed private method. 79 | 80 | /** 81 | * For example, imagine you are writing a class of date utility methods and you want to allow users to lookup 82 | * weekday names by index but you don't want them to be able to modify the array of names you use under the hood. 83 | * 84 | * In dateUtil(), note that the days array could simply be stored as a property of the dateUtil object but then it would be 85 | * visible to users of the script and they could even change it if they wanted, without even needing your source code. 86 | * However, since it's enclosed by the anonymous function which returns the date lookup function it is 87 | * only accessible by the lookup function so it is now tamper-proof. 88 | */ 89 | 90 | var dateUtil = { 91 | weekdayShort: (function () { 92 | var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; 93 | return function (x) { 94 | if ((x != parseInt(x)) || (x < 1) || (x > 7)) { 95 | throw new Error("invalid weekday number"); 96 | } 97 | return days[x - 1]; 98 | }; 99 | }()) 100 | }; 101 | 102 | // 2. To create memoizers. 103 | 104 | /** 105 | * You've probably heard of or even implemented the Fibonacci series function a couple of times. 106 | * 107 | * Closures can turn a simple Fibonacci function into a masterpiece. 108 | * 109 | * We are going to start with a classic Fibonacci implementation. 110 | */ 111 | var fibonacci = function (n) { 112 | return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); 113 | }; 114 | 115 | /** 116 | * This one works fine but is awfully slow. It's computing the same value several times. 117 | * 118 | * We can use a memoizer (a closure) to save each computed value for future uses. 119 | */ 120 | var fibonacci = (function ( ) { 121 | var memo = [0, 1]; 122 | var fib = function (n) { 123 | var result = memo[n]; 124 | if (typeof result !== 'number') { 125 | result = fib(n - 1) + fib(n - 2); 126 | memo[n] = result; 127 | } 128 | return result; 129 | }; 130 | return fib; 131 | }( )); 132 | 133 | console.log(fibonacci(100)); 134 | /** 135 | * Check Crockford's (book)[https://www.safaribooksonline.com/library/view/javascript-the-good/9780596517748/ch04s15.html "Safari Books Online"] 136 | * to find a way to generalize this function into one that memoizes other recursive functions. 137 | */ 138 | -------------------------------------------------------------------------------- /js/coercion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Coercion: 3 | * Converting a value from one type to another is often called "type casting," when done explicitly, 4 | * and "coercion" when done implicitly (forced by the rules of how a value is used) 5 | * 6 | * == vs === 7 | * The identity (===) operator behaves identically to the equality (==) operator except no type conversion is done, 8 | * and the types must be the same to be considered equal. 9 | * 10 | * The == operator will compare for equality after doing any necessary type conversions. 11 | * The === operator will not do the conversion, so if two values are not the same type === will simply return false. 12 | * It's this case where === will be faster, and may return a different result than ==. In all other cases performance will be the same. 13 | * 14 | * Links: 15 | * http://stackoverflow.com/questions/359494/does-it-matter-which-equals-operator-vs-i-use-in-javascript-comparisons 16 | * http://davidwalsh.name/fixing-coercion#isnt-coercion-already-dead 17 | * http://bytearcher.com/articles/equality-comparison-operator-javascript/ 18 | * http://rainsoft.io/the-legend-of-javascript-equality-operator/ 19 | * http://bytearcher.com/articles/equality-comparison-operator-javascript/ 20 | */ 21 | 22 | // Coercion in JS 23 | (function () { 24 | var x = 42; 25 | var y = x + ""; // implicit coercion! 26 | console.log(y); // "42" 27 | 28 | var z = String(x); // explicit coercion! 29 | console.log(z); // "42" 30 | })(); 31 | 32 | // Equality checks - Crazyyy Sh*t!!! 33 | (function () { 34 | console.log('' == '0'); // false 35 | console.log(0 == ''); // true 36 | console.log(0 == '0'); // true 37 | 38 | console.log(false == 'false'); // false 39 | console.log(false == '0'); // true 40 | 41 | console.log(false == undefined); // false 42 | console.log(false == null); // false 43 | console.log(null == undefined); // true 44 | 45 | console.log(' \t\r\n ' == 0); // true 46 | 47 | // Array 48 | var a = [1, 2, 3]; 49 | var b = [1, 2, 3]; 50 | 51 | console.log(a == b); // false 52 | console.log(a === b); // false 53 | 54 | // Object 55 | var c = {x: 1, y: 2}; 56 | var d = {x: 1, y: 2}; 57 | 58 | console.log(c == d); // false 59 | console.log(c === d); // false 60 | 61 | // String 62 | var e = "text"; 63 | var f = "te" + "xt"; 64 | 65 | console.log(e == f); // true 66 | console.log(e === f); // true 67 | 68 | // Here the == operator is checking the values of the two objects and returning true, 69 | // but the === is seeing that they're not the same type and returning false. 70 | console.log("abc" == new String("abc")); // true 71 | console.log("abc" === new String("abc")); // false 72 | })(); 73 | -------------------------------------------------------------------------------- /js/conditional-function-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Conditional Function declaration 3 | * 4 | * @TLDR: Invalid in JavaScript. So, avoid it! 5 | * 6 | * @Info: 7 | * ECMA-262 spec: A Block is defined as one or more Statements, and a FunctionDeclaration is not a Statement. 8 | * Hence, function declarations inside an if/else conditional is invalid. 9 | * 10 | * @Note: 11 | * - Browsers deal with it in different ways. Few of them hoist the fn and few don't (thinks of it as a fn expression) 12 | * - In 'strict' mode this will throw an error. 13 | * 14 | * @Reference: 15 | * ECMA-262: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=98 16 | */ 17 | 18 | // Case 1 19 | (function() { 20 | if (false) { 21 | function test () { 22 | alert("Works"); 23 | } 24 | } 25 | test(); // alerts "Works"... ohh boy! 26 | // Output is printed in most browsers due to hoisting of functions, although it is invalid in JavaScript 27 | }()); 28 | 29 | 30 | // Case 2 31 | (function() { 32 | if (false) { 33 | var test = function () { 34 | alert("Works"); 35 | } 36 | } 37 | test(); // Error: 'undefined' is not a function 38 | // Error is thrown, because the variable is hoisted, but not the function assigned to it. 39 | // Warning - Named function expressions are still hoisted in < IE9 (IE bug/inconsistency). 40 | }()); -------------------------------------------------------------------------------- /js/currying.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Currying 3 | * Currying refers to the process of transforming a function with multiple arity (# or args a fn accepts) into the same function with less arity. 4 | * 5 | * Briefly, currying is a way of constructing functions that allows partial application of a function’s arguments. 6 | * What this means is that you can pass all of the arguments a function is expecting and get the result, 7 | * or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments. It really is that simple. 8 | * 9 | * Currying vs Partial Application 10 | * “Currying is the decomposition of a polyadic function into a chain of nested unary functions. 11 | * Thus decomposed, you can partially apply one or more arguments, although the curry operation itself does not apply any arguments to the function.” 12 | * 13 | * “Partial application is the conversion of a polyadic function into a function taking fewer arguments arguments by providing one or more arguments in advance.” 14 | * 15 | * @Reference: 16 | * http://www.sitepoint.com/currying-in-functional-javascript/ 17 | * http://www.2ality.com/2011/09/currying-vs-part-eval.html 18 | * https://medium.com/@kbrainwave/currying-in-javascript-ce6da2d324fe#.nhp2e7pcm 19 | * https://medium.com/@kevincennis/currying-in-javascript-c66080543528#.bnk4cy1m0 20 | * http://raganwald.com/2013/03/07/currying-and-partial-application.html 21 | * http://ejohn.org/blog/partial-functions-in-javascript/ 22 | * http://stackoverflow.com/questions/113780/javascript-curry-what-are-the-practical-applications 23 | * http://conceptf1.blogspot.com/2014/03/currying-in-javascript.html 24 | * https://www.youtube.com/watch?v=iZLP4qOwY8I 25 | * https://egghead.io/lessons/javascript-what-is-currying 26 | * https://hughfdjackson.com/javascript/why-curry-helps/ 27 | */ 28 | 29 | // Simple Greet function -- Non Curried 30 | var greet = function (greeting, name) { 31 | console.log(greeting + ', ' + name); 32 | }; 33 | greet('Hello', 'Vasa'); // 'Hello, Vasa' 34 | 35 | // Curried version 36 | var greetCurried = function (greeting) { 37 | return function (name) { 38 | console.log(greeting + ', ' + name); 39 | } 40 | }; 41 | 42 | // This tiny adjustment to the way we wrote the function lets us create a new function for any type of greeting, 43 | // and pass that new function the name of the person that we want to greet 44 | var greetHello = greetCurried("Hello"); 45 | greetHello("Vasa"); //"Hello, Vasa" 46 | greetHello("Vignesh"); //"Hello, Vignesh" 47 | 48 | // We can also call the original curried function directly, just by passing each of the parameters 49 | // in a separate set of parentheses, one right after the other: 50 | greetCurried("Hi there")("Vasa"); //"Hi there, Vasa" 51 | 52 | 53 | // Currying traditional functions -- SIMPLE VERSION 54 | // 55 | // The only problem with the currying approach is the syntax. As you build these curried functions up, you need to keep nesting returned functions, 56 | // and call them with new functions that require multiple sets of parentheses, each containing its own isolated argument. It can get messy. 57 | // To address that problem, one approach is to create a quick and dirty currying function that will take the name of an existing function that was written without all the nested returns. 58 | // A currying function would need to pull out the list of arguments for that function, and use those to return a curried version of the original function: 59 | 60 | // Partial Application -- Few arguments passed initially + allowing more args to be passed later on. 61 | function curryIt(uncurriedFn) { 62 | var parameters = Array.prototype.slice.call(arguments, 1); // Omit 0th argument (which is the uncurriedFn and start from index 1) 63 | return function () { 64 | return uncurriedFn.apply(this, parameters.concat( 65 | Array.prototype.slice.call(arguments, 0) 66 | )); 67 | }; 68 | } 69 | 70 | // Usage 71 | var greeter = function (greeting, separator, emphasis, name) { 72 | console.log(greeting + separator + name + emphasis); 73 | }; 74 | var greetHello = curryIt(greeter, "Hello", ", ", "."); 75 | greetHello("Heidi"); //"Hello, Heidi." 76 | greetHello("Eddie"); //"Hello, Eddie." 77 | 78 | 79 | // curryIt --- BETTER VERSION 80 | // Reference: https://medium.com/@kevincennis/currying-in-javascript-c66080543528#.bnk4cy1m0 81 | function curryIt(fn) { 82 | var arity = fn.length; 83 | return (function resolver() { 84 | var memory = Array.prototype.slice.call(arguments); 85 | return function () { 86 | var local = memory.slice(), next; 87 | Array.prototype.push.apply(local, arguments); 88 | next = local.length >= arity ? fn : resolver; 89 | return next.apply(null, local); 90 | }; 91 | }()); 92 | } 93 | 94 | // EXAMPLE 95 | var l = 2, b = 3, h = 4; 96 | var curriedVol = curryIt(vol); 97 | var area = curriedVol(l)(b); 98 | var volume = area(h); 99 | console.log('Volume: ', volume); 100 | 101 | function vol(l, b, h) { 102 | return l * b * h; 103 | } 104 | 105 | // My version -- It worked in the first run :) 106 | function curryIt(fn) { 107 | var arity = fn.length; 108 | var params = []; 109 | return function handler() { 110 | var args = Array.prototype.slice.call(arguments); 111 | Array.prototype.push.apply(params, args); // OR params.push.apply(this, args); 112 | 113 | if (params.length === arity) { 114 | return fn.apply(this, params); 115 | } else { 116 | return handler; 117 | } 118 | } 119 | } 120 | 121 | // ES6 Example 122 | const one = document.getElementById('one'); 123 | const two = document.getElementById('two'); 124 | const three = document.getElementById('three'); 125 | 126 | const f = a => b => c => a.addEventListener(b, (event) => { 127 | event.target.style.backgroundColor = c; 128 | }); 129 | 130 | const oneEventColor = f(one); 131 | const twoEventColor = f(two); 132 | 133 | oneEventColor('mouseover')('blue'); 134 | twoEventColor('mouseout')('green'); 135 | 136 | // Currying challenge: 137 | // https://github.com/frantic/friday/blob/master/currying.js 138 | // http://blog.vjeux.com/2015/javascript/140byt-es-curried-add-function.html 139 | function add() { 140 | var s = [].reduce.call(arguments, function (sum, curr) { 141 | return sum + curr; 142 | }); 143 | var f = function () { 144 | return add.apply(0, [s].concat([].slice.call(arguments))) 145 | }; 146 | f.valueOf = function () { 147 | return s 148 | }; 149 | return f; 150 | } -------------------------------------------------------------------------------- /js/dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DOM API 3 | * 4 | * The DOM represents a document as a tree. The tree is made up of parent-child relationships, a parent can have one or many children nodes. 5 | * The idea of DOM is that every node is an object. We can get a node and change its properties 6 | * 7 | * @Reference: 8 | * http://javascript.info/tutorial/dom 9 | * http://www.quirksmode.org/dom/ 10 | * http://domenlightenment.com/ 11 | */ 12 | 13 | // BASIC ELEMENT SELECTORS 14 | document.getElementById("IDName"); // Selects the first element with the ID passed. It is invalid to use multiple IDs in HTML, however if duplicated, it selects the first one. 15 | document.getElementsByClassName("ClassName"); // Selects the elements with the Class name passed. It returns an NodeList array. 16 | document.getElementsByName("Name"); // Selects the elements with the Name passed, returns a NodeList array. 17 | document.getElementsByTagName("TagName"); // Selects the elements with the passed Tag, returns a NodeList array. 18 | document.querySelector("#IDName or .ClassName"); // Selects the first element that matched the selector value. 19 | document.querySelectorAll("#IDName or .ClassName"); // Selects all the element that it finds with the passed name and type. It returns a NodeList array. 20 | 21 | // ROOT ELEMENT 22 | console.log(document.documentElement); 23 | 24 | // In the world of DOM, an “element not found” or “no such element” is always null. 25 | // It is impossible to reference elements that are not yet rendered at the time of script execution. 26 | // For eg. if you access document.body in -- it returns null, since the is not yet loaded. 27 | 28 | // CHILD ELEMENTS 29 | 30 | // childNodes 31 | // All child nodes are referenced including whitespace ones. 32 | console.log(document.body.childNodes); 33 | 34 | // children 35 | // Sometimes we need to browse only element nodes, skipping text nodes. That’s what the children property is for. 36 | // It contains all element nodes. 37 | console.log(document.body.children); 38 | 39 | // firstChild and lastChild - Similar to childNodes, it includes text nodes. 40 | // For excluding text nodes, use firstElementChild and lastElementChild 41 | console.log(document.body.firstElementChild); 42 | console.log(document.body.lastElementChild); 43 | 44 | // parentNode, previousSibling and nextSibling 45 | // For excluding text nodes, use previousElementSibling and nextElementSibling 46 | console.log(document.body.parentNode); 47 | console.log(document.body.previousElementSibling); 48 | console.log(document.body.nextElementSibling); 49 | 50 | // STRUCTURE AND CONTENT PROPERTIES 51 | 52 | // nodeType 53 | // The most important ones are ELEMENT_NODE with number 1 and TEXT_NODE, which has number 3. 54 | var childNodes = document.body.childNodes; 55 | console.log(childNodes[0].nodeType != 1); 56 | 57 | // nodeName, tagName 58 | // Both nodeName and tagName contain the name of an element node. 59 | // In HTML any nodeName is uppercased, no matter which case you use in the document. 60 | // For element nodes, nodeName and tagName are the same. 61 | // But nodeName property also exists on non-element nodes. eg. alert(document.nodeName) // #document 62 | console.log(document.body.tagName); // BODY 63 | 64 | // innerHTML 65 | // It allows to access node contents in the text form. innerHTML works only for element nodes. 66 | // Gotcha: `innerHTML` can't be appended 67 | // Syntactically, is possible to append to innerHTML with elem.innerHTML += "New text", like below: 68 | // But what actually is done: 69 | // 1) Old content is wiped 70 | // 2) The new value innerHTML is parsed and inserted. 71 | document.body.innerHTML += "
Hi !
"; 72 | document.body.innerHTML += "How you doing?"; 73 | 74 | // nodeValue 75 | // The innerHTML works only for element nodes. For other types of nodes, there is a nodeValue property, which keeps the content. 76 | // eg. text nodes and comments 77 | document.body.childNodes[i].nodeValue = 'Test'; 78 | 79 | // PROPERTIES 80 | 81 | // DOM node is an object. So it can store custom properties and methods just like any JavaScript object. 82 | // Custom DOM properties: 83 | // 1) May have any value.Property names case-sensitive 84 | // 2) Don’t affect HTML 85 | // 3) Also, custom properties show up in for..in mixed with native properties: 86 | document.body.sayHi = function () { 87 | alert(this.nodeName); 88 | }; 89 | document.body.sayHi(); // BODY 90 | 91 | document.body.custom = 5; 92 | var list = []; 93 | for (var key in document.body) { 94 | list.push(key); 95 | } 96 | alert(list.join('\n')); 97 | 98 | // ATTRIBUTES 99 | 100 | // DOM nodes provide access to HTML attributes using the following standard methods: 101 | // elem.hasAttribute(name) - checks if the attribute exists 102 | // elem.getAttribute(name) - gets an attribute value 103 | // elem.setAttribute(name, value) - sets an attribute 104 | // elem.removeAttribute(name) - removes an attribute 105 | // 106 | // In contrast with properties, attributes: 107 | // 1) May be only strings. 108 | // 2) Names not case-sensitive, because HTML attributes are not case-sensitive 109 | // 3) They show up in innerHTML (unless it’s older IE) 110 | // 4) You can list all attributes using an array-like attributes property of the element. 111 | var div = document.body.children[0]; 112 | alert(div.getAttribute('ABOUT')); // case insensitive 113 | div.setAttribute('Test', 123); 114 | alert(document.body.innerHTML); 115 | 116 | // PROPERTIES AND ATTRIBUTES SYNCHRONIZATION. 117 | 118 | // Every type of DOM nodes has standard properties. 119 | // Standard DOM properties are synchronized with attributes. 120 | 121 | // id 122 | document.body.setAttribute('id', 'la-la-la'); 123 | alert(document.body.id); // la-la-la 124 | 125 | // href 126 | var a = document.body.children[0]; 127 | a.href = '/'; 128 | alert('attribute:' + a.getAttribute('href')); // '/' 129 | alert('property:' + a.href); // IE: '/', others: full URL 130 | 131 | // Gotcha: There are other attributes, which are synced, but not copied. For example input.checked: 132 | // The value of input.checked property is either true or false, but the attribute has whatever you put into it. 133 | var input = document.body.children[0]; 134 | alert(input.checked); // true 135 | alert(input.getAttribute('checked')); // empty string 136 | 137 | // value 138 | // There are also built-in properties which are synced one-way only. 139 | // For example, the input.value is synchronized from the attribute: 140 | var input = document.body.children[0]; 141 | input.setAttribute('value', 'new'); 142 | alert(input.value); // 'new', input.value changed 143 | 144 | // The "value" attribute keeps the original value after the property was updated, 145 | // for example when a visitor typed in something. The original value can be used to check if the input is changed, or to reset it. 146 | var input = document.body.children[0]; 147 | input.value = 'new'; 148 | alert(input.getAttribute('value')); // 'markup', not changed! 149 | 150 | // class/className 151 | // Because "class" is a reserved word in JavaScript, the name of the corresponding property for the "class" attribute is className. 152 | // To avoid IE quirks, just always use className property to manage classes, not the attribute. 153 | document.body.setAttribute('class', 'big red bloom'); 154 | alert(document.body.className); // big red bloom 155 | 156 | // To live well with any IE, use attributes correctly. 157 | // Or, in other words, try using properties all the time, until you really need an attribute. 158 | // 159 | // And the only times you really need an attribute are: 160 | // 1) To get a custom HTML attribute, because it is not synced to DOM property. 161 | // 2) To get an “original value” of the standard HTML attribute, like . 162 | 163 | // Attributes as DOM nodes 164 | // In attributes collection, every attribute is represented by a special kind of DOM node. It has name, value and other properties. 165 | var span = document.body.children[0]; 166 | alert(span.attributes['style'].value); // "color:blue;" 167 | alert(span.attributes['id'].value); // "my" 168 | 169 | // MODIFYING THE DOCUMENT 170 | 171 | // Creating elements 172 | // 1) Creates a new DOM element of type node: 173 | var div = document.createElement('div'); 174 | // 2) Creates a new DOM element of type text: 175 | var textElem = document.createTextNode('Robin was here'); 176 | // Cloning 177 | // An element can also be cloned: 178 | textElem.cloneNode(true); //Clones an element deeply, with all descendants. 179 | textElem.cloneNode(false); //Clones an element only, with attributes, but without children. 180 | 181 | // Adding elements 182 | // To do something with the element, you need to call the corresponding method of its parent: 183 | document.body.appendChild(textElem); //Appends elem to the children of parentElem. 184 | 185 | // parentElem.insertBefore(elem, nextSibling) 186 | // Inserts elem into the children of parentElem before the element nextSibling. 187 | // Link: http://stackoverflow.com/a/2007473/1672655 188 | var div = document.body.children[0]; 189 | var span = document.createElement('span'); 190 | span.innerHTML = 'A new span!'; 191 | div.insertBefore(span, div.firstChild); 192 | // Gotcha: insertBefore with second argument null works as appendChild. 193 | elem.insertBefore(newElem, null); // same as 194 | elem.appendChild(newElem); 195 | 196 | // Removing nodes 197 | // There are two main methods for removing nodes from DOM: 198 | // parentElem.removeChild(elem) - Remove the elem from the children of parentElem. 199 | // parentElem.replaceChild(elem, currentElem) - Replace the child element of parentElem, referenced by currentElem with the elem. 200 | 201 | // Note: If you want to move an element, you don’t have to remove it first. 202 | // elem.appendChild/insertBefore remove elem from it’s previous place automatically. 203 | // The following example moves the last child to the top of children list: 204 | var first = document.body.children[0]; 205 | var last = document.body.children[1]; 206 | document.body.insertBefore(last, first); 207 | // The removal occurs automatically when insertion methods are called for a node which already has a parent. 208 | 209 | // insertAfter custom function 210 | var elem = document.createElement('div'); 211 | elem.innerHTML = '**Child**'; 212 | function insertAfter(elem, refElem) { 213 | return elem.parentNode.insertBefore(elem, refElem.nextSibling); 214 | } 215 | insertAfter(elem, document.body.firstChild); 216 | insertAfter(elem, document.body.lastChild); 217 | 218 | // Gotcha 219 | // For an arbitrary document, we do the following: 220 | var aList1 = document.getElementsByTagName('a'); 221 | var aList2 = document.querySelectorAll('a'); 222 | document.body.appendChild(document.createElement('a')); 223 | alert(aList1.length - aList2.length); 224 | 225 | // What will be the output? Why? 226 | // Solution 227 | // The output will be 1, because getElementsByTagName is a live collection, which gets auto populated with the new a. It’s length increases by 1. 228 | // Contrary to this, querySelector returns a static list of nodes. It references same elements no matter what we do with the document. So, it’s length remains the same. 229 | 230 | // TABLE 231 | // 232 | // 233 | // 234 | //
one two
three four
235 | 236 | var table = document.body.children[0]; 237 | alert(table.rows[0].cells[0].innerHTML); // "one" 238 | 239 | // FORMS 240 | 241 | //Select option 242 | //
243 | // 247 | //
248 | var form = document.forms.my; 249 | var select = form.elements.genre; 250 | var value = select.options[select.selectedIndex].value; 251 | alert(value); // blues 252 | 253 | // The SELECT also provides selectedIndex property which keeps the index of the selected option. Useful if the select is not multiple. 254 | //
255 | // 259 | //
260 | 261 | var form = document.forms.temp; 262 | var select = form.elements.genre; 263 | var value = select.options[select.selectedIndex].value; 264 | alert(value); // blues 265 | -------------------------------------------------------------------------------- /js/event-bubbling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Event bubbling and capturing 3 | * 4 | * @Reference: 5 | * http://javascript.info/tutorial/bubbling-and-capturing 6 | * https://www.sitepoint.com/event-bubbling-javascript/ 7 | * http://stackoverflow.com/questions/4616694/what-is-event-bubbling-and-capturing 8 | * http://javascript.info/tutorial/mouse-events 9 | * 10 | */ 11 | 12 | // Stopping event bubble 13 | element.onclick = function (event) { 14 | event = event || window.event; // cross-browser event 15 | if (event.stopPropagation) { 16 | // W3C standard variant 17 | event.stopPropagation() 18 | } else { 19 | // IE variant 20 | event.cancelBubble = true 21 | } 22 | }; 23 | 24 | // If the element has several handlers on same event, then handlers are independent. All of them get executed.. 25 | // For example, if there are two onclick handlers on the same link, then stopping bubbling in one of them has no effect on the other one. 26 | // Also, the browser doesn’t guarantee the order in which they trigger. 27 | 28 | // CAPTURING 29 | // In all browsers, except IE<9, there are two stages of event processing. 30 | // The event first goes down - that’s called capturing, and then bubbles up. 31 | // This behavior is standardized in W3C specification. 32 | 33 | // All methods of event handling ignore the capturing phase. 34 | // Using addEventListener with last argument true is only the way to catch the event at capturing. 35 | 36 | // elem.addEventListener( type, handler, phase ); 37 | // phase = true 38 | // The handler is set on the capturing phase. 39 | // phase = false 40 | 41 | // The default browser action 42 | 43 | // 1) Event special method event.preventDefault() for W3C-compliant browsers and event.returnValue = false for IE<9. 44 | // Or, in a single line: 45 | event.preventDefault ? event.preventDefault() : (event.returnValue = false); 46 | 47 | // 2) Return false from the handler 48 | element.onclick = function (event) { 49 | return false; 50 | }; 51 | 52 | // Note: Bubbling and default action 53 | // Browser default action is independent from bubbling. 54 | // Cancelling default action does not stop bubbling and vise versa. 55 | // However, jQuery has it’s own event-handling layer. It wraps over the handler, 56 | // and if the handler returns false, then both bubbling is stopped and the default action is prevented. 57 | 58 | // Sample Events 59 | document.getElementById('btn').onclick(alert('Works')); // Triggered by a mouse click: mousedown and then mouseup over an element 60 | document.getElementById('btn').oncontextmenu(alert('Works')); // Triggered by a right-button mouse click over an element. 61 | document.getElementById('btn').dblclick(alert('Works')); // Triggered by two clicks within a short time over an element 62 | -------------------------------------------------------------------------------- /js/event-delegation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Event Delegation in Vanilla JS 3 | * 4 | * JSFiddle: 5 | * https://jsfiddle.net/vasanthkay/sokgevhr/7/ 6 | * 7 | * @Reference: 8 | * Excellent Article: 9 | * http://codepen.io/32bitkid/post/understanding-delegated-javascript-events 10 | * https://www.ericponto.com/blog/2015/04/02/event-delegation-with-matches/ 11 | */ 12 | 13 | //HTML CODE 14 | // 15 | // 27 | 28 | 29 | // HELPER FUNCTION 30 | function delegate(criteria, listener) { 31 | return function (e) { 32 | var el = e.target; 33 | do { 34 | if (!criteria(el)) { 35 | continue; 36 | } 37 | e.delegateTarget = el; 38 | listener.call(this, e); 39 | return; 40 | } while ((el = el.parentNode)); 41 | }; 42 | } 43 | 44 | // Example of Event Delegation 45 | // Custom filter to check for required DOM elements 46 | var buttonsFilter = function (elem) { 47 | return (elem instanceof HTMLElement) && elem.matches(".btn"); 48 | // OR 49 | // For < IE9 50 | // return elem.classList && elem.classList.contains('btn'); 51 | }; 52 | 53 | var buttonHandler = function (e) { 54 | var button = e.delegateTarget; 55 | var hasActiveClass = button.classList.contains('active'); 56 | 57 | if (!hasActiveClass(button)) { 58 | button.classList.add('active'); 59 | } else { 60 | button.classList.remove('active'); 61 | } 62 | }; 63 | 64 | // Add the event listener 65 | document.addEventListener("click", delegate(buttonsFilter, buttonHandler)); 66 | 67 | 68 | -------------------------------------------------------------------------------- /js/event-handling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Event Handling in Vanilla JS 3 | * 4 | * @Reference: 5 | * http://gomakethings.com/ditching-jquery/#event-listeners 6 | * http://www.quirksmode.org/dom/events/index.html 7 | * http://www.jstips.co/en/DOM-event-listening-made-easy/ 8 | */ 9 | 10 | 11 | var elem = document.querySelector('.some-class'); 12 | elem.addEventListener('click', function (e) { 13 | // Do stuff 14 | }, false); // the final param, `useCapture` indicates that the user wishes to initiate capture. 15 | 16 | // Passing multiple variables to event handlers 17 | var elem = document.querySelector('.some-class'); 18 | var someFunction = function (var1, var2, var3, event) { 19 | // do stuff 20 | }; 21 | elem.addEventListener('click', someFunction.bind(null, var1, var2, var3), false); 22 | elem.addEventListener('mouseover', someFunction.bind(null, var1, var2, var3), false); 23 | 24 | // Delegate events to the document. 25 | var eventHandler = function () { 26 | // Get clicked element 27 | var toggle = event.target; 28 | 29 | // If clicked element is the one you're looking for, run your methods 30 | if (toggle.hasAttribute('data-example') || toggle.classList.contains('sample-class')) { 31 | event.preventDefault(); // Prevent default click event 32 | someMethod(); 33 | } 34 | }; 35 | 36 | document.addEventListener('click', eventHandler, false); 37 | 38 | // Better delegate function 39 | function delegate(criteria, listener) { 40 | return function (e) { 41 | var el = e.target; 42 | do { 43 | if (!criteria(el)) { 44 | continue; 45 | } 46 | e.delegateTarget = el; 47 | listener.call(this, e); 48 | return; 49 | } while ((el = el.parentNode)); 50 | }; 51 | } 52 | 53 | // Handle click function - ES6 54 | function handleEvent(eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) { 55 | const element = onElement || document.documentElement; 56 | 57 | function handler(event) { 58 | if (typeof withCallback === 'function') { 59 | withCallback.call(thisArg, event) 60 | } 61 | } 62 | 63 | handler.destroy = function () { 64 | return element.removeEventListener(eventName, handler, useCapture) 65 | }; 66 | 67 | element.addEventListener(eventName, handler, useCapture); 68 | return handler; 69 | } 70 | 71 | // Anytime you need 72 | const handleClick = handleEvent('click', { 73 | onElement: element, 74 | withCallback: (event) => { 75 | console.log('Tada!') 76 | } 77 | }); 78 | 79 | // And anytime you want to remove it 80 | handleClick.destroy(); -------------------------------------------------------------------------------- /js/factory-functions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Factory Functions 3 | * 4 | * @Reference: 5 | * https://www.youtube.com/watch?v=ImwrezYhw4w 6 | * http://atendesigngroup.com/blog/factory-functions-javascript 7 | * 8 | */ 9 | 10 | // ES6 classes vs Factory functions 11 | // With classes -- Be wary 12 | class Dog { 13 | constructor() { 14 | this.sound = 'woof'; 15 | } 16 | 17 | talk() { 18 | console.log(this.sound); 19 | } 20 | } 21 | 22 | const sniffles = new Dog(); 23 | sniffles.talk(); // Outputs: 'woof' 24 | 25 | // Here's the issue 26 | $('button').click(sniffles.talk); // This will not work since - the `this` in talk() now refers to the DOM element selected by $(button) and not sniffles. 27 | 28 | // Workaround -- explicit binding 29 | $('button').click(sniffles.talk.bind(sniffles)); 30 | 31 | // Or in ES6 -- `this` inside an arrow function is always inherited from the enclosing scope. 32 | $('button').click(() => sniffles.talk()); 33 | 34 | 35 | 36 | // Factory functions 37 | const dog = () => { 38 | const sound = 'woof'; 39 | return { 40 | talk: () => console.log(sound) // We are not using `this` at all. 41 | }; 42 | }; 43 | 44 | const sniffles = dog(); 45 | sniffles.talk(); // Outputs: 'woof' 46 | 47 | $('button').click(sniffles.talk); // Works -- Outputs: 'woof' 48 | 49 | 50 | 51 | // Constructor functions vs Factory functions 52 | 53 | // The basic difference is that a constructor function is used with the new keyword 54 | // (which causes JavaScript to automatically create a new object, set `this` within the function to that object, and return the object): 55 | var objFromConstructor = new ConstructorFunction(); 56 | 57 | // A factory function is called like a "regular" function: 58 | var objFromFactory = factoryFunction(); 59 | // But for it to be considered a "factory" it would need to return a new instance of some object: 60 | // you wouldn't call it a "factory" function if it just returned a boolean or something. 61 | // This does not happen automatically like with new, but it does allow more flexibility for some cases. 62 | // In a really simple example the functions referenced above might look something like this: 63 | 64 | function ConstructorFunction() { 65 | this.someProp1 = "1"; 66 | this.someProp2 = "2"; 67 | } 68 | ConstructorFunction.prototype.someMethod = function() { /* whatever */ }; 69 | 70 | function factoryFunction() { 71 | var obj = { 72 | someProp1 : "1", 73 | someProp2 : "2", 74 | someMethod: function() { /* whatever */ } 75 | // someMethod() inside obj would lead to each object returned hold a different copy of someMethod which is something that we might not want. 76 | // This is where using `new` and `prototype` inside the factory function would help. 77 | }; 78 | // other code to manipulate obj in some way here 79 | return obj; 80 | } 81 | 82 | 83 | // Factory functions: Encapsulation using private properties 84 | function Car () { 85 | // private variable 86 | var location = 'Denver'; // PRIVATE 87 | function year() { // PRIVATE 88 | self.year = new Date().getFullYear(); 89 | } 90 | 91 | var self = { 92 | make: 'Honda', 93 | model: 'Accord', 94 | color: '#cc0000', 95 | paint: function(color){ 96 | self.color = color; 97 | } 98 | }; 99 | 100 | if (!self.year){ 101 | year(); 102 | } 103 | 104 | return self; 105 | } 106 | 107 | var myCar = Car(); 108 | 109 | 110 | 111 | // Factory functions: Dynamic objects 112 | // Since we can have public/private functions we can use if/else statements to easily manipulate our object structure. 113 | // This gives ultimate flexibility to allow the root function ambiguity and allow parameters to determine what the object returned should be. 114 | function Address (param) { 115 | var self = {}; 116 | 117 | if (param === 'dev'){ 118 | self = { 119 | state: 'Colorado', 120 | saveToLog: function(){ 121 | // write info to a log file 122 | } 123 | }; 124 | } else { 125 | self = { 126 | state: 'Colorado' 127 | }; 128 | } 129 | 130 | return self; 131 | } 132 | 133 | var devAddress = Address('dev'); 134 | var productionAddress = Address(); -------------------------------------------------------------------------------- /js/floating-point-precision.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Numeric Type in JavaScript (Floating Point) 3 | * 4 | * @TLDR: There is only one numeric type, Number - a 64-bit floating point (Same as Java's double). Has weird rounding errors. 5 | * 6 | * @Info 7 | * - The crux of the problem is that numbers are represented in this format as a whole number times a power of two. 8 | * rational numbers (such as 0.1, which is 1/10) whose denominator is not a power of two cannot be exactly represented. 9 | * - 0.1 cannot be represented as accurately in base-2 as in base-10 due to the missing prime factor of 5. 10 | * Just as 1/3 takes an infinite number of digits to represent in decimal, but is "0.1" in base-3, 11 | * 0.1 takes an infinite number of digits in base-2 where it does not in base-10. 12 | * - For 0.1 in the standard binary64 format, the representation can be written exactly as 13 | * 0.1000000000000000055511151231257827021181583404541015625 in decimal 14 | * - In contrast, the rational number 0.1, which is 1/10, can be written exactly as 0.1 in decimal. 15 | * 16 | * @Note: 17 | * - The best suggestions I've seen to handle floating points is to use properly tested libraries like sinfuljs, mathjs or BigDecimal.js for handling them. 18 | * - Another oft-repeated advice is to use the built-in toPrecision() and toFixed() methods on numbers. 19 | * A big warning to anyone thinking of using them -- those methods return strings. 20 | * 21 | * @Reference 22 | * http://stackoverflow.com/questions/1458633/how-to-deal-with-floating-point-number-precision-in-javascript 23 | * http://stackoverflow.com/questions/588004/is-floating-point-math-broken 24 | * 25 | */ 26 | 27 | (function() { 28 | console.log(0.1 + 0.2); // prints 0.30000000000000004 29 | console.log((0.1 + 0.2) === 0.3); // prints false 30 | 31 | // Workaround: Format your result to some fixed number of significant digits, like this: 32 | // (Math.floor(y/x) * x).toFixed(2) OR parseFloat(a).toFixed(2) 33 | })(); -------------------------------------------------------------------------------- /js/for-in-with-hasOwnProperty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * for... in statement 3 | * 4 | * The for...in statement iterates over the enumerable properties of an object, in arbitrary order. 5 | * 6 | * Gotcha: When used with Arrays 7 | * Array indexes are just enumerable properties with integer names and are otherwise identical to general Object properties. 8 | * There is no guarantee that for...in will return the indexes in any particular order and it will return all enumerable properties, including those with non–integer names and those that are inherited. 9 | * Because the order of iteration is implementation-dependent, iterating over an array may not visit elements in a consistent order. 10 | * Therefore it is better to use a for loop with a numeric index (or Array.prototype.forEach() or the for...of loop) when iterating over arrays where the order of access is important. 11 | * 12 | */ 13 | 14 | // The following function takes as its argument an object. It then iterates over all the object's enumerable properties and returns a string of the property names and their values. 15 | var obj = {a:1, b:2, c:3}; 16 | 17 | for (var prop in obj) { 18 | console.log("obj." + prop + " = " + obj[prop]); 19 | } 20 | 21 | // Output: 22 | // "obj.a = 1" 23 | // "obj.b = 2" 24 | // "obj.c = 3" 25 | 26 | 27 | 28 | // The following function illustrates the use of hasOwnProperty(): the inherited properties are not displayed. 29 | var triangle = {a:1, b:2, c:3}; 30 | 31 | function ColoredTriangle() { 32 | this.color = "red"; 33 | } 34 | 35 | ColoredTriangle.prototype = triangle; 36 | 37 | var obj = new ColoredTriangle(); 38 | 39 | for (var prop in obj) { 40 | if( obj.hasOwnProperty( prop ) ) { 41 | console.log("obj." + prop + " = " + obj[prop]); 42 | } 43 | } 44 | 45 | // Output: 46 | // "obj.color = red" -------------------------------------------------------------------------------- /js/getOwnPropertyNames-vs-keys.js: -------------------------------------------------------------------------------- 1 | /** 2 | * What's the difference between Object.getOwnPropertyNames and Object.keys 3 | * 4 | * There is a little difference. 5 | * Object.getOwnPropertyNames(a) returns all own properties of the object a. 6 | * Object.keys(a) returns all enumerable own properties. 7 | * 8 | * It means that if you define your object properties without making some of them enumerable: false these two methods will give you the same result. 9 | */ 10 | 11 | var a = {}; 12 | Object.defineProperties(a, { 13 | one: {enumerable: true, value: 'one'}, 14 | two: {enumerable: false, value: 'two'} 15 | }); 16 | Object.keys(a); // ["one"] 17 | Object.getOwnPropertyNames(a); // ["one", "two"] 18 | 19 | // If you define a property without providing property attributes descriptor (meaning you don't use Object.defineProperties), for example: 20 | a.test = 21; 21 | // then such property becomes an enumerable automatically and both methods produce the same array. -------------------------------------------------------------------------------- /js/getters-setters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Getters and Setters 3 | * 4 | * Getters and setters have been available in Chrome since day one, in Firefox since version 2, 5 | * version 3 of Safari, Internet Explorer 9 and up, and in all mobile browsers. 6 | * 7 | * @Reference 8 | * http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ 9 | * http://javascriptplayground.com/blog/2013/12/es5-getters-setters/ 10 | */ 11 | 12 | // Getters and Setters as explicit methods 13 | (function () { 14 | function wrapValue(value) { 15 | return { 16 | getValue: function () { 17 | return value; 18 | }, 19 | setValue: function (newValue) { 20 | value = newValue; 21 | } 22 | }; 23 | } 24 | 25 | var x = wrapValue(5); 26 | console.log(x.getValue()); // output 5 27 | x.setValue(7); 28 | console.log(x.getValue()); // output 7 29 | })(); 30 | 31 | // Using getters and setters -- Conventional way 32 | (function () { 33 | function wrapValue(_value) { 34 | return { 35 | get value() { 36 | return _value; 37 | }, 38 | set value(newValue) { 39 | _value = newValue; 40 | } 41 | }; 42 | } 43 | 44 | var x = wrapValue(5); 45 | console.log(x.value); // output 5 46 | x.value = 7; 47 | console.log(x.value); // output 7 48 | })(); 49 | 50 | // Using getters and setters -- Using Object.defineProperty 51 | // When you define a property this way, you can do much more than just define a setter or getter. You may also pass following keys: 52 | // configurable (false by default): if this is true, the property's configuration will be modifiable in future. 53 | // enumerable (false by default): if true, the property will appear when looping over the object (for (var key in obj)). 54 | (function() { 55 | var person = { 56 | firstName: 'Jimmy', 57 | lastName: 'Smith' 58 | }; 59 | 60 | Object.defineProperty(person, 'fullName', { 61 | get: function() { 62 | return this.firstName + ' ' + this.lastName; 63 | }, 64 | set: function(name) { 65 | var words = name.split(' '); 66 | this.firstName = words[0] || ''; 67 | this.lastName = words[1] || ''; 68 | } 69 | }); 70 | })(); 71 | -------------------------------------------------------------------------------- /js/logical-operations-with-string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Logical Operator with string, when true returns the string. 3 | */ 4 | 5 | (function () { 6 | var a = true; 7 | var b = 'Yes'; 8 | var c = 'It\'s me'; 9 | 10 | console.log(a && b); // Prints 'Yes' 11 | console.log(a && b && c); // Prints 'It's me' 12 | console.log(a && b || c); // Prints 'Yes' 13 | })(); -------------------------------------------------------------------------------- /js/method-overloading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Method/Function Overloading 3 | * A way of mapping a single function call to multiple functions based upon the arguments they accept 4 | * 5 | * @Reference: 6 | * http://ejohn.org/blog/javascript-method-overloading/ 7 | * http://ejohn.org/apps/learn/#90 8 | * 9 | * Explanation: 10 | * http://stackoverflow.com/a/30989908/1672655 11 | * http://stackoverflow.com/a/18122417/1672655 12 | * 13 | * Best practises: http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices 14 | */ 15 | 16 | function addMethod(object, name, fn) { 17 | 18 | var old = object[name]; 19 | // Get the old function corresponding to this name. Will be "undefined" 20 | // the first time "addMethod" is called. 21 | 22 | object[name] = function () { 23 | // Now, assign object[name] to a new function. 24 | // The critical part of this function is that "old" is captured inside of 25 | // this function and will be available any time the function is called. 26 | 27 | if (fn.length == arguments.length) { 28 | // if the number of parameters belonging to the function we've added 29 | // matches what was passed in, call "fn" 30 | return fn.apply(this, arguments); 31 | } else if (typeof old == 'function') { 32 | // Otherwise if there's another function with this name 33 | // call it instead. 34 | return old.apply(this, arguments); 35 | } 36 | }; 37 | } 38 | 39 | function Ninjas() { 40 | var ninjas = ["Dean Edwards", "Sam Stephenson", "Alex Russell"]; 41 | addMethod(this, "find", function () { 42 | return ninjas; 43 | }); 44 | addMethod(this, "find", function (name) { 45 | var ret = []; 46 | for (var i = 0; i < ninjas.length; i++) 47 | if (ninjas[i].indexOf(name) == 0) 48 | ret.push(ninjas[i]); 49 | return ret; 50 | }); 51 | addMethod(this, "find", function (first, last) { 52 | var ret = []; 53 | for (var i = 0; i < ninjas.length; i++) 54 | if (ninjas[i] == (first + " " + last)) 55 | ret.push(ninjas[i]); 56 | return ret; 57 | }); 58 | } 59 | 60 | 61 | // USAGE 62 | // 63 | // var ninjas = new Ninjas(); 64 | // ninjas.find().length == 3 65 | // ninjas.find("Sam").length == 1 66 | // ninjas.find("Dean", "Edwards").length == 1 67 | // ninjas.find("Alex", "X", "Russell") == null -------------------------------------------------------------------------------- /js/mixins.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JavaScript Mixins - What are they? 3 | * 4 | * In general computer science, a mixin is a class that defines a set of functions relating to a type (e.g. Person, Circle, Observer). 5 | * Mixins classes are usually considered abstract in that they will not themselves be instantiated 6 | * – instead their functions are copied (or ‘borrowed’) by concrete classes as a means of ‘inheriting’ behaviour without entering into a formal relationship with the behaviour provider. 7 | * 8 | * OK but this is JavaScript, and we have no classes (atleast until ES5). 9 | * This is actually a good thing because it means we can use objects (instances) instead, which offer clarity and flexibility: 10 | * our mixin can be a regular object, a prototype, a function – whatever, and the mixin process becomes transparent and obvious. 11 | * 12 | * @Reference: 13 | * https://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/ 14 | * https://lostechies.com/derickbailey/2012/10/07/javascript-mixins-beyond-simple-object-extension/ 15 | * http://raganwald.com/2014/04/10/mixins-forwarding-delegation.html 16 | * http://bob.yexley.net/dry-javascript-with-mixins/ 17 | * https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.osy5v7ih0 18 | * http://addyosmani.com/resources/essentialjsdesignpatterns/book/#mixinpatternjavascript 19 | */ 20 | 21 | // build a mixin function to take a target that receives the mixin, 22 | // a source that is the mixin, and a list of methods / attributes to 23 | // copy over to the target 24 | 25 | function mixInto(target, source, methodNames) { 26 | 27 | // ignore the actual args list and build from arguments so we can 28 | // be sure to get all of the method names 29 | var args = Array.prototype.slice.apply(arguments); 30 | target = args.shift(); 31 | source = args.shift(); 32 | methodNames = args; 33 | 34 | var method; 35 | var length = methodNames.length; 36 | for (var i = 0; i < length; i++) { 37 | method = methodNames[i]; 38 | 39 | // build a function with a closure around the source 40 | // and forward the method call to the source, passing 41 | // along the method parameters and setting the context 42 | target[method] = function () { 43 | var args = Array.prototype.slice(arguments); 44 | source[method].apply(source, args); 45 | } 46 | 47 | } 48 | 49 | } 50 | 51 | // make use of the mixin function 52 | var myApp = new Marionette.Application(); 53 | mixInto(myApp, Marionette.EventBinder, "bindTo", "unbindFrom", "unbindAll"); 54 | 55 | 56 | /** 57 | * Mixin Design Pattern 58 | * 59 | * Link: http://jsfiddle.net/quFa9/21/ 60 | */ 61 | 62 | // Detailed explanation of Mixin Design Pattern in JavaScript can be found here: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#mixinpatternjavascript 63 | 64 | /* Car Class */ 65 | var Car = function (settings) { 66 | this.model = settings.model || 'no model provided'; 67 | this.colour = settings.colour || 'no colour provided'; 68 | }; 69 | 70 | /* Mixin Class */ 71 | var Mixin = function () { }; 72 | Mixin.prototype = { 73 | driveForward: function () { 74 | console.log('drive forward'); 75 | }, 76 | driveBackward: function () { 77 | console.log('drive backward'); 78 | } 79 | }; 80 | 81 | /* Augment existing class with a method from another class */ 82 | function augment(receivingClass, givingClass) { 83 | /* only provide certain methods */ 84 | if (arguments[2]) { 85 | var i, len = arguments.length; 86 | for (i = 2; i < len; i++) { 87 | receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]; 88 | } 89 | } 90 | /* provide all methods */ 91 | else { 92 | var methodName; 93 | for (methodName in givingClass.prototype) { 94 | /* check to make sure the receiving class doesn't have a method of the same name as the one currently being processed */ 95 | if (!receivingClass.prototype[methodName]) { 96 | receivingClass.prototype[methodName] = givingClass.prototype[methodName]; 97 | } 98 | } 99 | } 100 | } 101 | 102 | /* Augment the Car class to have the methods 'driveForward' and 'driveBackward' */ 103 | augment(Car, Mixin, 'driveForward', 'driveBackward'); 104 | 105 | /* Create a new Car */ 106 | var vehicle = new Car({model: 'Ford Escort', colour: 'blue'}); 107 | 108 | /* Test to make sure we now have access to the methods */ 109 | vehicle.driveForward(); 110 | vehicle.driveBackward(); -------------------------------------------------------------------------------- /js/new-keyword.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Examining the 'new' keyword and what it does 3 | * 4 | * new ConstructorFunction(arg1, arg2); 5 | * 6 | * It does 4 things: 7 | * 1. It creates a new object. The type of this object is simply object. 8 | * 2. It sets this new object's internal, inaccessible, [[prototype]] property to be the constructor function's external, accessible, prototype object (every function automatically has a prototype property). 9 | * 3. It executes the `ConstructorFunction`, using the newly created object whenever 'this' is mentioned. 10 | * 4. It returns the newly created object, unless the constructor function returns a non-primitive value. In this case, that non-primitive value will be returned. 11 | * 12 | * Once this is done, if an undefined property of the new object is requested, the script will check the object's [[prototype]] object for the property instead. 13 | * Functions, in addition to the hidden [[prototype]] property, also have a property called prototype, and it is this that you can access, and modify, to provide inherited properties and methods for the objects you make. 14 | * 15 | * The most difficult part about this is point number 2. Every object (including functions) has this internal property called [[prototype]]. 16 | * It can only be set at object creation time, either with new, with Object.create, or based on the literal (functions default to Function.prototype, numbers to Number.prototype, etc.). 17 | * It can be read with Object.getPrototypeOf(someObject) or via __proto__ or this.constructor.prototype. 18 | * 19 | * @Reference: 20 | * http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript 21 | * http://zeekat.nl/articles/constructors-considered-mildly-confusing.html 22 | * https://css-tricks.com/understanding-javascript-constructors/ 23 | * https://john-dugan.com/object-oriented-javascript-pattern-comparison/ 24 | */ 25 | 26 | // When we do this: 27 | function Foo() { 28 | this.kind = 'foo'; 29 | } 30 | 31 | var foo = new Foo(); 32 | foo.kind; //=> ‘foo’ 33 | 34 | 35 | // Behind the scenes it is like doing something like this: 36 | function Foo() { 37 | // this is not valid, just for illustration 38 | var this = {}; // Step 1 39 | this.__proto__ = Foo.prototype; // Step 2 40 | this.kind = 'foo'; // Step 3 41 | return this; // Step 4 42 | } 43 | 44 | 45 | // Example 46 | ObjMaker = function() { 47 | this.a = 'first'; 48 | }; 49 | // ObjMaker is just a function, there's nothing special about it that makes it a constructor. 50 | 51 | 52 | ObjMaker.prototype.b = 'second'; 53 | // Like all functions, ObjMaker has an accessible prototype property that we can alter. 54 | // i just added a property 'b' to it. Like all objects, ObjMaker also has an inaccessible [[prototype]] property that we can't do anything with. 55 | 56 | 57 | obj1 = new ObjMaker(); 58 | // 3 things just happened. 59 | // A new, empty object was created called obj1. At first obj1 was the same as {}. 60 | // The [[prototype]] property of obj1 was then set to the current object value of the ObjMaker.prototype 61 | // Note: If ObjMaker.prototype is later assigned a new value, obj1's [[prototype]] will not change, 62 | // but you can alter the properties of ObjMaker.prototype to add to both the prototype and [[prototype]]. 63 | // The ObjMaker function was executed, with obj1 in place of `this`. So obj1.a was set to 'first'. 64 | 65 | 66 | obj1.a; 67 | // returns 'first' 68 | 69 | 70 | obj1.b; 71 | // obj1 doesn't have a property called 'b', so JavaScript checks its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype 72 | // ObjMaker.prototype has a property called 'b' with value 'second' 73 | // returns 'second' 74 | 75 | 76 | 77 | // It's like class inheritance because now, any objects you make using new ObjMaker() will also appear to have inherited the 'b' property. 78 | // If you want something like a subclass, then you do this: 79 | SubObjMaker = function () {}; 80 | SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated! 81 | // Because we used 'new', the [[prototype]] property of SubObjMaker.prototype 82 | // is now set to the object value of ObjMaker.prototype. 83 | // The modern way to do this is with Object.create(), which was added in ECMAScript 5: 84 | // SubObjMaker.prototype = Object.create(ObjMaker.prototype); 85 | 86 | SubObjMaker.prototype.c = 'third'; 87 | obj2 = new SubObjMaker(); 88 | // [[prototype]] property of obj2 is now set to SubObjMaker.prototype 89 | // Remember that the [[prototype]] property of SubObjMaker.prototype 90 | // is ObjMaker.prototype. So now obj2 has a prototype chain! 91 | // obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype 92 | 93 | obj2.c; 94 | // returns 'third', from SubObjMaker.prototype 95 | 96 | obj2.b; 97 | // returns 'second', from ObjMaker.prototype 98 | 99 | obj2.a; 100 | // returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype 101 | // was created with the ObjMaker function, which assigned a for us 102 | 103 | 104 | -------------------------------------------------------------------------------- /js/number-maxmin-val.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Native JS 3 | * 4 | * Number.MAX_VALUE and Number.MIN_VALUE 5 | * 6 | * Number.MAX_VALUE: 1.7976931348623157e+308 7 | * Number.MIN_VALUE: 5e-324 8 | */ 9 | 10 | (function () { 11 | console.log(Number.MAX_VALUE > 0); // true 12 | console.log(Number.MIN_VALUE < 0); // false... WTF 13 | })(); 14 | -------------------------------------------------------------------------------- /js/object-clone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object copy by value (Clone) 3 | * 4 | * @Reference: 5 | * http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object/ 6 | * http://stackoverflow.com/a/728694/1672655 7 | */ 8 | 9 | (function () { 10 | var obj = { 11 | name: 'vasa', 12 | role: 'Ninja' 13 | }; 14 | 15 | // A trick to clone an object (or copy by value) 16 | var clonedObj = JSON.parse(JSON.stringify(obj)); 17 | 18 | // In ES6 19 | var clone = Object.assign({}, obj); 20 | 21 | // With jQuery 22 | // Shallow copy 23 | var copiedObjShallow = jQuery.extend({}, obj); 24 | // Deep copy 25 | var copiedObjDeep = jQuery.extend(true, {}, obj); 26 | 27 | // Object.assign() polyfill 28 | // http://stackoverflow.com/a/34283281/1672655 29 | 30 | if (!Object.assign) { 31 | Object.defineProperty(Object, 'assign', { 32 | enumerable: false, 33 | configurable: true, 34 | writable: true, 35 | value: function (target) { 36 | 'use strict'; 37 | if (target === undefined || target === null) { 38 | throw new TypeError('Cannot convert first argument to object'); 39 | } 40 | 41 | var to = Object(target); 42 | for (var i = 1; i < arguments.length; i++) { 43 | var nextSource = arguments[i]; 44 | if (nextSource === undefined || nextSource === null) { 45 | continue; 46 | } 47 | nextSource = Object(nextSource); 48 | 49 | var keysArray = Object.keys(nextSource); 50 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { 51 | var nextKey = keysArray[nextIndex]; 52 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); 53 | if (desc !== undefined && desc.enumerable) { 54 | to[nextKey] = nextSource[nextKey]; 55 | } 56 | } 57 | } 58 | return to; 59 | } 60 | }); 61 | } 62 | })(); -------------------------------------------------------------------------------- /js/object-constructor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Objects: In Detail 3 | * An object is an unordered list of primitive data types (and sometimes reference data types) that is stored as a series of name-value pairs. 4 | * Each item in the list is called a property (functions are called methods). 5 | * 6 | * @Reference: 7 | * http://javascriptissexy.com/javascript-objects-in-detail/ 8 | * https://css-tricks.com/understanding-javascript-constructors/ 9 | * http://stackoverflow.com/a/14172862/1672655 10 | */ 11 | 12 | // Object Data Properties Have Attributes 13 | // Each data property (object property that store data) has not only the name-value pair, but also 3 attributes (the three attributes are set to true by default): 14 | //— 
Configurable: If false, any attempts to delete the property or change its attributes (Writable, Configurable, or Enumerable) will fail. 15 | //— Enumerable: If true, the property will be iterated over when a user does for (var prop in obj){} (or similar). 16 | //— Writable: If false, the value of the property can not be changed. 17 | 18 | 19 | // Constructor Pattern for Creating Objects 20 | function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) { 21 | 22 | this.color = theColor; 23 | this.sweetness = theSweetness; 24 | this.fruitName = theFruitName; 25 | this.nativeToLand = theNativeToLand; 26 | 27 | this.showName = function () { 28 | console.log("This is a " + this.fruitName); 29 | }; 30 | 31 | this.nativeTo = function () { 32 | this.nativeToLand.forEach(function (eachCountry) { 33 | console.log("Grown in:" + eachCountry); 34 | }); 35 | }; 36 | } 37 | 38 | 39 | // With this pattern in place, it is very easy to create all sorts of fruits. Thus: 40 | var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]); 41 | mangoFruit.showName(); // This is a Mango.​ 42 | mangoFruit.nativeTo(); 43 | // Grown in:South America​ 44 | // Grown in:Central America​ 45 | // Grown in:West Africa​ 46 | 47 | var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]); 48 | pineappleFruit.showName(); // This is a Pineapple. 49 | // If you had to change the showName function, you only had to do it in one location. 50 | // The pattern encapsulates all the functionalities and characteristics of all the fruits in by making just the single Fruit function with inheritance. 51 | 52 | 53 | // An inherited property is defined on the object’s prototype property. For example: 54 | someObject.prototype.firstName = 'rich'; 55 | 56 | 57 | // An own property is defined directly on the object itself, for example: 58 | // Let’s create an object first: 59 | var aMango = new Fruit (); 60 | // Now we define the mangoSpice property directly on the aMango object. 61 | // Because we define the mangoSpice property directly on the aMango object, it is an own property of aMango, not an inherited property. 62 | aMango.mangoSpice = 'some value'; 63 | 64 | 65 | 66 | // Prototype Pattern for Creating Objects 67 | function Fruit () { 68 | 69 | } 70 | 71 | Fruit.prototype.color = "Yellow"; 72 | Fruit.prototype.sweetness = 7; 73 | Fruit.prototype.fruitName = "Generic Fruit"; 74 | Fruit.prototype.nativeToLand = "USA"; 75 | 76 | Fruit.prototype.showName = function () { 77 | console.log("This is a " + this.fruitName); 78 | }; 79 | 80 | Fruit.prototype.nativeTo = function () { 81 | console.log("Grown in:" + this.nativeToLand); 82 | }; 83 | 84 | // And this is how we call the Fruit() constructor in this prototype pattern: 85 | var mangoFruit = new Fruit (); 86 | mangoFruit.showName(); //​ 87 | mangoFruit.nativeTo(); 88 | // This is a Generic Fruit​ 89 | // Grown in:USA 90 | 91 | 92 | 93 | // Accessing Inherited Properties 94 | // Properties inherited from Object.prototype are not enumerable, so the for/in loop does not show them. 95 | // However, inherited properties that are enumerable are revealed in the for/in loop iteration. 96 | // For example: 97 | 98 | var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"}; 99 | //Use of the for/in loop to access the properties in the school object​ 100 | for (var eachItem in school) { 101 | console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation​ 102 | } 103 | 104 | // Create a new HigherLearning function that the school object will inherit from.​ 105 | function HigherLearning () { 106 | this.educationLevel = "University"; 107 | } 108 | /* SIDE NOTE: 109 | The educationLevel property is not actually inherited by objects that use the HigherLearning constructor; 110 | Instead, the educationLevel property is created as a new property on each object that uses the HigherLearning constructor. 111 | The reason the property is not inherited is because we use of the "this" keyword to define the property. 112 | */ 113 | 114 | // Implement inheritance with the HigherLearning constructor​ 115 | var school = new HigherLearning (); 116 | school.schoolName = "MIT"; 117 | school.schoolAccredited = true; 118 | school.schoolLocation = "Massachusetts"; 119 | 120 | 121 | //Use of the for/in loop to access the properties in the school object​ 122 | for (var eachItem in school) { 123 | console.log(eachItem); // Prints educationLevel, schoolName, schoolAccredited, and schoolLocation​ 124 | } 125 | 126 | 127 | 128 | // Deleting Properties of an Object 129 | // To delete a property from an object, you use the delete operator. 130 | // You cannot delete properties that were inherited, nor can you delete properties with their attributes set to configurable. 131 | // You must delete the inherited properties on the prototype object (where the properties were defined). 132 | // Also, you cannot delete properties of the global object, which were declared with the var keyword. 133 | // The delete operator returns true if the delete was successful. 134 | // And surprisingly, it also returns true if the property to delete was nonexistent or the property could not be deleted (such as non-configurable or not owned by the object). 135 | 136 | var christmasList = {mike:"Book", jason:"sweater" }; 137 | delete christmasList.mike; // deletes the mike property​ 138 | 139 | for (var people in christmasList) { 140 | console.log(people); 141 | } 142 | // Prints only jason​ 143 | // The mike property was deleted​ 144 | 145 | delete christmasList.toString; // returns true, but toString not deleted because it is an inherited method​ 146 | 147 | // Here we call the toString method and it works just fine—wasn’t deleted ​ 148 | christmasList.toString(); //"[object Object]"​ 149 | 150 | // You can delete a property of an instance if the property is an own property of that instance. 151 | // For example, we can delete the educationLevel property from the school's object we created above because the educationLevel property is defined on the instance: 152 | // we used the "this" keyword to define the property when we declare the HigherLearning function. 153 | // We did not define the educationLevel property on the HigherLearning function's prototype.​ 154 | 155 | console.log(school.hasOwnProperty("educationLevel")); // true 156 | // educationLevel is an own property on school, so we can delete it​ 157 | delete school.educationLevel; // true 158 | 159 | // The educationLevel property was deleted from the school instance​ 160 | console.log(school.educationLevel); // undefined 161 | 162 | // But the educationLevel property is still on the HigherLearning function​ 163 | var newSchool = new HigherLearning (); 164 | console.log(newSchool.educationLevel); // University​ 165 | 166 | // If we had defined a property on the HigherLearning function's prototype, such as this educationLevel2 property:​ 167 | HigherLearning.prototype.educationLevel2 = "University 2"; 168 | 169 | // Then the educationLevel2 property on the instances of HigherLearning would not be own property. ​ 170 | // The educationLevel2 property is not an own property on the school instance​ 171 | console.log(school.hasOwnProperty("educationLevel2")); // false 172 | console.log(school.educationLevel2); // University 2​ 173 | 174 | // Let's try to delete the inherited educationLevel2 property​ 175 | delete school.educationLevel2; // true (always returns true, as noted earlier) 176 | 177 | // The inherited educationLevel2 property was not deleted​ 178 | console.log(school.educationLevel2); // University 2​ 179 | 180 | 181 | // Object.defineProperty Function 182 | // The Object.defineProperty() can be used inside of a constructor to help perform all necessary property setup. 183 | 184 | function Book(name) { 185 | Object.defineProperty(this, 'name', { 186 | get: function() { 187 | return 'Book: ' + name; 188 | }, 189 | set: function(newName) { 190 | name = newName; 191 | }, 192 | configurable: false 193 | }); 194 | } 195 | 196 | var myBook = new Book('Single Page Web Applications'); 197 | console.log(myBook.name); // Book: Single Page Web Applications 198 | 199 | // we cannot delete the name property because "configurable" is set to false 200 | delete myBook.name; 201 | console.log(myBook.name); // Book: Single Page Web Applications 202 | 203 | // but we can change the value of the name property 204 | myBook.name = "Testable JavaScript"; 205 | console.log(myBook.name); // Book: Testable JavaScript 206 | // In this code we used accessor properties inside the Object.defineProperty(). 207 | // Accessor properties don’t include any properties or methods, but they define a getter to call when the property is read, and a setter to call when the property is written to. 208 | // A getter is expected to return a value, while a setter receives the value being assigned to the property as an argument. 209 | // This constructor allows us to set or change the name property of instances, but we are not allowed to delete it -------------------------------------------------------------------------------- /js/object-create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object.create() 3 | * 4 | * JavaScript provides multiple methods for creating new object instances. 5 | * To this day, the new operator appears to remain the most popular method, 6 | * even though it’s arguably the most problematic and least flexible approach. 7 | * 8 | * The Object.create method provides an improved alternative to the new operator, 9 | * with the following benefits: 10 | * - You can explicitly specify, at object creation time, the object that will be the prototype of the newly created object. 11 | * - You can create objects that have no prototype by specifying null as the prototype. 12 | * This is something that can’t otherwise be done. This can be useful when using an object as a dictionary, for example. 13 | * - You can easily specify the properties of the newly created object, 14 | * including their descriptors: configurable, enumerable, and writable. 15 | * 16 | * Enumerable: I can access to all of them using a for..in loop. Also, enumerable property keys of an object are returned using Object.keys method. 17 | * Writable: I can modify their values, I can update a property just assigning a new value to it: ob.a = 1000; 18 | * Configurable: If set to false, the properties can't be removed using the delete operator. I can modify the behavior of the property, so I can make them non-enumerable, non-writable or even non-configurable if I feel like doing so. 19 | * 20 | * Object.create() has been available in all browsers since IE9. 21 | * 22 | * @Reference: 23 | * http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ 24 | * http://arqex.com/967/javascript-properties-enumerable-writable-configurable 25 | * http://stackoverflow.com/questions/2709612/using-object-create-instead-of-new 26 | */ 27 | 28 | (function () { 29 | // Object.create. 30 | // When you first encounter this method, you might wonder why JavaScript needs another way to create objects, when it already has the object literal syntax and constructor functions? 31 | // Where Object.create differs from those options is that lets you provide, as the first argument to the method, an object that will become the new object’s prototype. 32 | // Remember that there is a difference between an object’s public prototype property and its internal [[Prototype]] property. 33 | // When JavaScript is looking up properties on an object, it uses the latter, but traditionally the only standardised way to control it for a new object has been to use the pattern applied by __extends. 34 | // You create a new function with a public prototype property, then apply the new operator on the function to create a new object. 35 | // When the new operator is used with a function, the runtime sets the [[Prototype]] property of the new object to the object referenced by the public prototype property of the function. 36 | // While this approach to controlling the [[Prototype]] works, it is a little opaque and wasteful, requiring the declaration of a new function simply for the purpose of controlling this internal property. 37 | // With Object.create, the extra function is no longer required, as the [[Prototype]] can be controlled directly. 38 | // A dead simple example would be. 39 | 40 | var animal = {legs: 4}; 41 | var dog; 42 | 43 | dog = Object.create(animal); 44 | dog.legs == 4; // True 45 | })(); 46 | 47 | 48 | (function () { 49 | var x = Object.create(null, {prop: {value: 3, writable: false}}); 50 | console.log(x.prop); // output 3 51 | x.prop = 5; 52 | console.log(x.prop); // still output 3, since writable is false 53 | })(); 54 | 55 | 56 | // Traditional constructor usage - `new` keyword. 57 | (function () { 58 | var UserA = function (nameParam) { 59 | this.name = nameParam; 60 | this.studentYear = 'sophomore'; 61 | }; 62 | 63 | UserA.prototype.sayHello = function () { 64 | console.log('Hello ' + this.name); 65 | }; 66 | 67 | var bob = new UserA('bob'); 68 | bob.sayHello(); 69 | })(); 70 | 71 | // With Object.create() 72 | // http://stackoverflow.com/a/2709811/1672655 73 | (function () { 74 | var userB = { 75 | sayHello: function () { 76 | console.log('Hello ' + this.name); 77 | } 78 | }; 79 | 80 | // Object.create() lets you initialize object properties using its second argument 81 | var bob = Object.create(userB, { 82 | 'studentYear': { 83 | value: 'sophomore', 84 | enumerable: true // writable:false, configurable(deletable):false by default 85 | }, 86 | 'name': { 87 | value: 'Bob', 88 | enumerable: true 89 | } 90 | }); 91 | })(); 92 | 93 | /** 94 | * Simple Object.create() polyfill 95 | */ 96 | /** 97 | * Object.create() 98 | * 99 | * The crux of the matter with this Object.create method is that you pass into it an object that you want to inherit from, 100 | * and it returns a new object that inherits from the object you passed into it. 101 | */ 102 | Object.create = function (o) { 103 | //It creates a temporary constructor F()​ 104 | function F() { 105 | } 106 | 107 | //And set the prototype of the this constructor to the parametric (passed-in) o object​ 108 | //so that the F() constructor now inherits all the properties and methods of o​ 109 | F.prototype = o; 110 | 111 | //Then it returns a new, empty object (an instance of F())​ 112 | //Note that this instance of F inherits from the passed-in (parametric object) o object. ​ 113 | //Or you can say it copied all of the o object's properties and methods​ 114 | return new F(); 115 | }; -------------------------------------------------------------------------------- /js/object-defineProperty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object.defineProperty() 3 | * 4 | * Enumerable: 5 | * I can access to all of them using a for..in loop. Also, enumerable property keys of an object are returned using Object.keys method. 6 | * 7 | * Writable: 8 | * I can modify their values, I can update a property just assigning a new value to it: ob.a = 1000; 9 | * 10 | * Configurable: 11 | * I can modify the behavior of the property, so I can make them non-enumerable, non-writable or even non-configurable 12 | * if I feel like doing so. Configurable properties are the only ones that can be removed using the delete operator. 13 | * 14 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty 15 | * http://x-team.com/2015/02/es5-object-defineproperty-method/ 16 | * http://arqex.com/967/javascript-properties-enumerable-writable-configurable 17 | */ 18 | 19 | var ob = {}; 20 | // Adding a property to ob using Object.defineProperty 21 | Object.defineProperty(ob, 'c', { 22 | value: 3, 23 | enumerable: false, 24 | writable: false, 25 | configurable: false 26 | }); 27 | 28 | console.log(ob.c); // => 3 29 | 30 | Object.getOwnPropertyDescriptor(ob, 'c'); 31 | // => {value: 3, enumerable: false, writable: false, configurable: false} 32 | 33 | // It is also possible to define the properties on object creation if you instantiate it using the method Object.create( prototype, 34 | // properties ). It accepts an object with property descriptors as the second parameter, and it can be used as follows 35 | 36 | var ob = Object.create(Object.prototype, { 37 | a: {writable: true, enumerable: true, value: 1}, 38 | b: {enumerable: true, value: 2} 39 | }); 40 | console.log(ob); // => {a:1, b:2} 41 | 42 | var ob = Object.create(Object.prototype, { 43 | a: {writable: true, enumerable: true, value: 1}, 44 | b: {enumerable: true, value: 2} 45 | }); 46 | console.log(ob); // => {a:1, b:2} 47 | 48 | // Our task is to create a Person constructor which takes two parameters: firstName and lastName. 49 | // Our object will expose four attributes: firstName, lastName, fullName and species. 50 | // The first three will react to changes, and the last one species will have a constant value of 'human' 51 | 52 | // Object.defineProperty (> IE8) 53 | var Person = function (first, last) { 54 | this.firstName = first; 55 | this.lastName = last; 56 | }; 57 | 58 | Object.defineProperty(Person, 'species', { 59 | writable: false, 60 | value: 'human' 61 | }); 62 | 63 | Object.defineProperty(Person, 'fullName', { 64 | get: function () { 65 | return this.firstName + ' ' + this.lastName; 66 | }, 67 | set: function (value) { 68 | var splitString = value.trim().split(' '); 69 | 70 | if (splitString.length === 2) { 71 | this.firstName = splitString[0]; 72 | this.lastName = splitString[1]; 73 | } 74 | } 75 | }); 76 | 77 | var woman = new Person('Kate', 'Khowalski'); 78 | 79 | console.log(woman.firstName); // 'Kate' 80 | console.log(woman.lastName); // 'Khowalski' 81 | console.log(woman.fullName); //'Kate Khowalski 82 | console.log(woman.species); // human 83 | 84 | /* 85 | * Change name 86 | */ 87 | 88 | woman.firstName = 'Yulia'; 89 | console.log(woman.firstName); // 'Yulia' 90 | console.log(woman.lastName); // 'Khowalski' 91 | console.log(woman.fullName); // 'Yulia Khowalski' 92 | woman.species = 'fish'; 93 | console.log(woman.species); // human - No, you can't change this properity. 94 | 95 | /* 96 | * Change fullName 97 | */ 98 | 99 | woman.fullName = 'Joana Stevens'; 100 | console.log(woman.firstName); //Joana 101 | console.log(woman.lastName); //Stevens -------------------------------------------------------------------------------- /js/object-freeze.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Object.freeze() method freezes an object: that is, prevents new properties from being added to it; 3 | * prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. 4 | * In essence the object is made effectively immutable. The method returns the object being frozen. 5 | * 6 | * Gotcha: 7 | * If the frozen object has values that are objects, they can still be modified, unless they are frozen as well. 8 | * The freeze is SHALLOW. 9 | * 10 | * @Reference: 11 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze 12 | * http://adripofjavascript.com/blog/drips/immutable-objects-with-object-freeze.html 13 | * 14 | */ 15 | 16 | var obj = { 17 | prop: function() {}, 18 | foo: 'bar' 19 | }; 20 | 21 | // New properties may be added, existing properties may be changed or removed 22 | obj.foo = 'baz'; 23 | obj.lumpy = 'woof'; 24 | delete obj.prop; 25 | 26 | // Freeze 27 | Object.freeze(obj); 28 | 29 | // Check if frozen 30 | console.log(Object.isFrozen(obj) === true); // True 31 | 32 | // Now any changes will fail (throw errors in strict mode). 33 | obj.foo = 'quux'; // silently does nothing 34 | obj.quaxxor = 'the friendly duck'; // silently doesn't add the property 35 | 36 | 37 | 38 | /** 39 | * Freeze is shallow. 40 | * Let's make a deepFreeze() function 41 | */ 42 | 43 | obj1 = { 44 | internal: {} 45 | }; 46 | 47 | Object.freeze(obj1); 48 | obj1.internal.a = 'aValue'; 49 | 50 | console.log(obj1.internal.a); // aValue 51 | 52 | // To make the object fully immutable, freeze each object in obj1 53 | function deepFreeze(obj) { 54 | // Retrieve the property names defined on obj 55 | var propNames = Object.getOwnPropertyNames(obj); 56 | 57 | // Freeze properties before freezing self 58 | propNames.forEach(function (name) { 59 | var prop = obj[name]; 60 | 61 | // Freeze prop if it is an object 62 | if (typeof prop == 'object' && prop !== null && !Object.isFrozen(prop)) { 63 | deepFreeze(prop); 64 | } 65 | }); 66 | 67 | // Freeze self 68 | return Object.freeze(obj); 69 | } 70 | 71 | // Test deepFreeze 72 | var obj2 = { 73 | internal: {} 74 | }; 75 | 76 | deepFreeze(obj2); 77 | obj2.internal.a = 'anotherValue'; 78 | console.log(obj2.internal.a); // undefined 79 | -------------------------------------------------------------------------------- /js/object-keys.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Iterating through Object properties 3 | * 4 | * Use Object.keys() 5 | * 6 | * Iterating over object properties is such a common occurrence in JavaScript that there is a dedicated statement for it: 7 | * for…in. Yet, as is shown in Crockford’s book, for…in is a problematic construct that usually requires a hasOwnProperty 8 | * conditional to weed out undesired properties. 9 | * A better, cleaner solution is to use Object.keys to generate an array of a given object’s own enumerable properties, 10 | * and then iterate over that array. 11 | * 12 | * This approach also allows you to sort or otherwise modify the array of property names before iterating over it. 13 | * Object.keys has been available in browsers since IE9. 14 | * 15 | * @Reference 16 | * http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ 17 | */ 18 | 19 | 20 | // With for..in 21 | (function () { 22 | var x = {hello: 1, there: 2, world: 3}; 23 | for (var key in x) { 24 | if (x.hasOwnProperty(key)) { 25 | console.log(key, x[key]); 26 | } 27 | } 28 | // Output three lines: hello 1, there 2, world 3 29 | })(); 30 | 31 | // With Object.keys() 32 | (function () { 33 | var x = {hello: 1, there: 2, world: 3}; 34 | Object.keys(x).forEach((function (key) { 35 | console.log(key, x[key]); 36 | })); 37 | // Output three lines: hello 1, there 2, world 3 38 | })(); -------------------------------------------------------------------------------- /js/object-oriented.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object Oriented Programming in JavaScript 3 | * 4 | * Prototype-based programming is an OOP model that doesn't use classes, but rather it first accomplishes the behavior of any class and then reuses it (equivalent to inheritance in class-based languages) 5 | * by decorating (or expanding upon) existing prototype objects. (Also called classless, prototype-oriented, or instance-based programming.) 6 | * 7 | * Inheritance 8 | * Inheritance is a way to create a class as a specialized version of one or more classes (JavaScript only supports single inheritance). 9 | * The specialized class is commonly called the child, and the other class is commonly called the parent. 10 | * In JavaScript you do this by assigning an instance of the parent class to the child class, and then specializing it. 11 | * In modern browsers you can also use Object.create to implement inheritance. 12 | * 13 | * Polymorphism 14 | * Polymorphism is the presentation of one interface for multiple data types. 15 | * For example, integers, floats, and doubles are implicitly polymorphic: regardless of their different types, they can all be added, subtracted, multiplied, and so on. 16 | * In the case of OOP, by making the class responsible for its code as well as its own data, polymorphism can be achieved in that each class has its own function that (once called) behaves properly for any object. 17 | * 18 | * Encapsulation 19 | * Encapsulation is the packing of data and functions into one component (for example, a class) and then controlling access to that component to make a "blackbox" out of the object. 20 | * Because of this, a user of that class only needs to know its interface (that is, the data and functions exposed outside the class), not the hidden implementation. 21 | * This allows us to abstract or localize specific set of functionalities on objects. 22 | * 23 | * Why Encapsulation? 24 | * When you simply want to create an object just to store some data, and it is the only object of its kind, you can use an object literal and create your object. 25 | * This is quite common and you will use this simple pattern often. 26 | * However, whenever you want to create objects with similar functionalities (to use the same methods and properties), 27 | * you encapsulate the main functionalities in a Function and you use that Function’s constructor to create the objects. This is the essence of encapsulation. 28 | * 29 | * @Reference: 30 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript 31 | * http://javascriptissexy.com/oop-in-javascript-what-you-need-to-know/ 32 | * http://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor 33 | * http://www.toptal.com/javascript/javascript-prototypes-scopes-and-performance-what-you-need-to-know 34 | */ 35 | 36 | /** 37 | * ENCAPSULATION 38 | */ 39 | 40 | // Combination Constructor/Prototype Pattern 41 | function User (theName, theEmail) { 42 | this.name = theName; 43 | this.email = theEmail; 44 | this.quizScores = []; 45 | this.currentScore = 0; 46 | } 47 | 48 | // Overwriting the prototype object -- Not recommended because it breaks the prototype chain 49 | // But, let's try it out for understanding purposes. 50 | User.prototype = { 51 | // The one disadvantage of overwriting the prototype is that the constructor property no longer points to the prototype, 52 | // so we have to set it manually. Hence this line: 53 | constructor: User, 54 | saveScore:function (theScoreToAdd) { 55 | this.quizScores.push(theScoreToAdd) 56 | }, 57 | showNameAndScores:function () { 58 | var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet"; 59 | return this.name + " Scores: " + scores; 60 | }, 61 | changeEmail:function (newEmail) { 62 | this.email = newEmail; 63 | return "New Email Saved: " + this.email; 64 | } 65 | }; 66 | 67 | // A User ​ 68 | firstUser = new User("Richard", "Richard@examnple.com"); 69 | firstUser.changeEmail("RichardB@examnple.com"); 70 | firstUser.saveScore(15); 71 | firstUser.saveScore(10); 72 | 73 | firstUser.showNameAndScores(); //Richard Scores: 15,10​ 74 | 75 | // Another User​ 76 | secondUser = new User("Peter", "Peter@examnple.com"); 77 | secondUser.saveScore(18); 78 | secondUser.showNameAndScores(); //Peter Scores: 18 79 | 80 | /** 81 | * Object.create() 82 | * 83 | * The crux of the matter with this Object.create method is that you pass into it an object that you want to inherit from, 84 | * and it returns a new object that inherits from the object you passed into it. 85 | */ 86 | Object.create = function (o) { 87 | //It creates a temporary constructor F()​ 88 | function F() { 89 | } 90 | //And set the prototype of the this constructor to the parametric (passed-in) o object​ 91 | //so that the F() constructor now inherits all the properties and methods of o​ 92 | F.prototype = o; 93 | 94 | //Then it returns a new, empty object (an instance of F())​ 95 | //Note that this instance of F inherits from the passed-in (parametric object) o object. ​ 96 | //Or you can say it copied all of the o object's properties and methods​ 97 | return new F(); 98 | }; 99 | 100 | // Sample usage 101 | // We have a simple cars object​ 102 | var cars = { 103 | type:"sedan", 104 | wheels:4 105 | }; 106 | 107 | // We want to inherit from the cars object, so we do:​ 108 | var toyota = Object.create (cars); // now toyota inherits the properties from cars​ 109 | console.log(toyota.type); // sedan 110 | 111 | 112 | /** 113 | * Object Orient programming example 114 | */ 115 | // Define the Person constructor 116 | var Person = function(firstName) { 117 | this.firstName = firstName; 118 | }; 119 | 120 | // Add a couple of methods to Person.prototype 121 | Person.prototype.walk = function(){ 122 | console.log("I am walking!"); 123 | }; 124 | 125 | Person.prototype.sayHello = function(){ 126 | console.log("Hello, I'm " + this.firstName); 127 | }; 128 | 129 | // Define the Student constructor 130 | function Student(firstName, subject) { 131 | // Call the parent constructor, making sure (using Function#call) 132 | // that "this" is set correctly during the call 133 | Person.call(this, firstName); 134 | 135 | // Initialize our Student-specific properties 136 | this.subject = subject; 137 | } 138 | 139 | // Create a Student.prototype object that inherits from Person.prototype. 140 | // Note: A common error here is to use "new Person()" to create the 141 | // Student.prototype. That's incorrect for several reasons, not least 142 | // that we don't have anything to give Person for the "firstName" 143 | // argument. The correct place to call Person is above, where we call 144 | // it from Student. 145 | Student.prototype = Object.create(Person.prototype); // See note below 146 | 147 | // Set the "constructor" property to refer to Student 148 | Student.prototype.constructor = Student; 149 | 150 | // Replace the "sayHello" method 151 | Student.prototype.sayHello = function(){ 152 | console.log("Hello, I'm " + this.firstName + ". I'm studying " 153 | + this.subject + "."); 154 | }; 155 | 156 | // Add a "sayGoodBye" method 157 | Student.prototype.sayGoodBye = function(){ 158 | console.log("Goodbye!"); 159 | }; 160 | 161 | // Example usage: 162 | var student1 = new Student("Janet", "Applied Physics"); 163 | student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics." 164 | student1.walk(); // "I am walking!" 165 | student1.sayGoodBye(); // "Goodbye!" 166 | 167 | // Check that instanceof works correctly 168 | console.log(student1 instanceof Person); // true 169 | console.log(student1 instanceof Student); // true 170 | 171 | 172 | -------------------------------------------------------------------------------- /js/object-prototype.js: -------------------------------------------------------------------------------- 1 | /** 2 | * How does JavaScript .prototype work? 3 | * 4 | * Every JavaScript object has an internal property called [[Prototype]]. 5 | * If you look up a property via obj.propName or obj['propName'] and the object does not have such a property - 6 | * which can be checked via obj.hasOwnProperty('propName') - the runtime looks up the property in the object referenced by [[Prototype]] instead. 7 | * If the prototype-object also doesn't have such a property, its prototype is checked in turn, 8 | * thus walking the original object's prototype-chain until a match is found or its end is reached with Object.prototype. 9 | * 10 | * Some JavaScript implementations allow direct access to the [[Prototype]] property, eg via a non-standard property named __proto__. 11 | * In general, it's only possible to set an object's prototype during object creation: 12 | * If you create a new object via new Func(), the object's [[Prototype]] property will be set to the object referenced by Func.prototype. 13 | * This allows to simulate classes in JavaScript, although JavaScript's inheritance system is - as we have seen - prototypical, and not class-based: 14 | * 15 | * Just think of constructor functions as classes and the properties of the prototype 16 | * (ie of the object referenced by the constructor function's prototype property) as shared members, 17 | * ie members which are the same for each instance. 18 | * In class-based systems, methods are implemented the same way for each instance, 19 | * so methods are normally added to the prototype, whereas an object's fields are instance-specific and therefore 20 | * added to the object itself during construction. 21 | * 22 | * Two different things can be called "prototype": 23 | * the prototype property, as in obj.prototype 24 | * the prototype internal property, denoted as [[Prototype]] in ES5. 25 | * - It can be retrieved via the ES5 Object.getPrototypeOf(). 26 | * - Firefox makes it accessible through the __proto__ property as an extension. ES6 now mentions some optional requirements for __proto__. 27 | * 28 | * __proto__ is used for the dot . property lookup as in obj.property. 29 | * .prototype is not used for lookup directly, only indirectly as it determines __proto__ at object creation with new. 30 | * 31 | * Lookup order is: 32 | * 1. obj properties added with obj.p = ... or Object.defineProperty(obj, ...) 33 | * 2. properties of obj.__proto__ 34 | * 3. properties of obj.__proto__.__proto__, and so on 35 | * 4. if some __proto__ is null, return undefined. (Note: At the topmost level Object.__proto__ is null) 36 | * 37 | * This is the so-called prototype chain. 38 | * You can avoid . lookup with obj.hasOwnProperty('key') and Object.getOwnPropertyNames(f) 39 | * 40 | * Prototype is important in JavaScript because JavaScript does not have classical inheritance based on Classes (as most object oriented languages do), 41 | * and therefore all inheritance in JavaScript is made possible through the prototype property. JavaScript has a prototype-based inheritance mechanism. 42 | * 43 | * Object.prototype Properties Inherited by all Objects 44 | * All objects in JavaScript inherit properties and methods from Object.prototype. 45 | * These inherited properties and methods are constructor, hasOwnProperty (), isPrototypeOf (), propertyIsEnumerable (), toLocaleString (), toString (), and valueOf (). 46 | * ECMAScript 5 also adds 4 accessor methods to Object.prototype. 47 | * 48 | * @Reference: 49 | * http://stackoverflow.com/questions/572897/how-does-javascript-prototype-work/23877420 50 | * https://medium.com/@will_gottchalk/javascript-interview-questions-javascript-is-a-prototypal-language-what-do-i-mean-by-this-76937a9aa42a#.23dpi96xy 51 | * https://css-tricks.com/understanding-javascript-constructors/ 52 | * http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/ 53 | * http://sporto.github.io/blog/2013/02/22/a-plain-english-guide-to-javascript-prototypes/ 54 | * https://davidwalsh.name/javascript-objects 55 | * http://stackoverflow.com/a/32740085/1672655 56 | * https://community.risingstack.com/javascript-prototype-chain-inheritance/ 57 | */ 58 | 59 | // There are two main ways to set obj.__proto__: 60 | 61 | // 1. new: 62 | var F = function() {}; 63 | var f = new F(); 64 | // imagine: 65 | // f.__proto__ = F.prototype; 66 | 67 | // then new has set: 68 | f.__proto__ === F.prototype; 69 | 70 | //This is where .prototype gets used. 71 | 72 | 73 | 74 | // 2. Object.create: 75 | var g = Object.create(proto); 76 | // imagine: 77 | // g.__proto__ = proto 78 | 79 | // sets: 80 | g.__proto__ === proto; 81 | 82 | 83 | 84 | // 1. prototype property 85 | // 86 | // Every JavaScript function has a prototype property (this property is empty by default), 87 | // and you attach properties and methods on this prototype property when you want to implement inheritance. 88 | // This prototype property is not enumerable; that is, it isn’t accessible in a for/in loop. 89 | // The prototype property is used primarily for inheritance; you add methods and properties on a function’s prototype property 90 | // to make those methods and properties available to instances of that function. 91 | function PrintStuff (myDocuments) { 92 | this.documents = myDocuments; 93 | } 94 | 95 | // We add the print () method to PrintStuff prototype property so that other instances (objects) can inherit it:​ 96 | PrintStuff.prototype.print = function () { 97 | console.log(this.documents); 98 | }; 99 | 100 | // Create a new object with the PrintStuff () constructor, thus allowing this new object to inherit PrintStuff's properties and methods.​ 101 | var newObj = new PrintStuff ("I am a new Object and I can print."); 102 | 103 | // newObj inherited all the properties and methods, including the print method, from the PrintStuff function. 104 | // Now newObj can call print directly, even though we never created a print () method on it.​ 105 | newObj.print (); //I am a new Object and I can print. 106 | 107 | 108 | 109 | // 2. prototype attribute 110 | // 111 | // Think of the prototype attribute as a characteristic of the object; this characteristic tells us the object’s “parent”. 112 | // In simple terms: An object’s prototype attribute points to the object’s “parent”—the object it inherited its properties from. 113 | // The prototype attribute is normally referred to as the prototype object, and it is set automatically when you create a new object. 114 | // Every object inherits properties from some other object, and it is this other object that is the object’s prototype attribute or “parent.” 115 | // (You can think of the prototype attribute as the lineage or the parent). 116 | 117 | 118 | // Prototype Attribute of Objects Created with new Object () or Object Literal 119 | 120 | // The userAccount object inherits from Object and as such its prototype attribute is Object.prototype.​ 121 | var userAccount = new Object (); 122 | 123 | // This demonstrates the use of an object literal to create the userAccount object; the userAccount object inherits from Object; 124 | // therefore, its prototype attribute is Object.prototype just as the userAccount object does above.​ 125 | var userAccount = {name: 'Mike'}; 126 | 127 | 128 | // Prototype Attribute of Objects Created With a Constructor Function 129 | function Account () { 130 | 131 | } 132 | var userAccount = new Account (); 133 | // userAccount initialized with the Account () constructor and as such its prototype attribute (or prototype object) is Account.prototype. 134 | 135 | 136 | // Prototype chain - Simulating multiple inheritance 137 | // @Reference: http://markdalgleish.com/2012/10/a-touch-of-class-inheritance-in-javascript/ 138 | 139 | // Our 'actor' object has some properties... 140 | var actor = { 141 | canAct: true, 142 | canSpeak: true 143 | }; 144 | 145 | // 'silentActor' inherits from 'actor' 146 | var silentActor = Object.create(actor); 147 | silentActor.canSpeak = false; 148 | 149 | // 'busterKeaton' inherits from 'silentActor' 150 | var busterKeaton = Object.create(silentActor); 151 | 152 | Object.getPrototypeOf(busterKeaton); // silentActor 153 | Object.getPrototypeOf(silentActor); // actor 154 | Object.getPrototypeOf(actor); // Object 155 | 156 | // Modifying the chain 157 | 158 | // The interesting thing is that the ‘actor’ and ‘silentActor’ objects are still live in the system and can be modified at runtime. 159 | // So, for a contrived example, if all silent actors lost their jobs, we could do the following: 160 | silentActor.isEmployed = false; 161 | 162 | // So now... 163 | busterKeaton.isEmployed; // false 164 | 165 | // Setting up Multiple inheritance using the `new` keyword 166 | // Set up Actor 167 | function Actor() {} 168 | Actor.prototype.canAct = true; 169 | 170 | // Set up SilentActor to inherit from Actor: 171 | function SilentActor() {} 172 | SilentActor.prototype = Object.create(Actor.prototype); 173 | 174 | // We can now add new properties to the SilentActor prototype: 175 | SilentActor.prototype.canSpeak = false; 176 | 177 | // So instances can act, but can't speak: 178 | var charlie = new SilentActor(); 179 | charlie.canAct; // true 180 | charlie.canSpeak; // false 181 | 182 | -------------------------------------------------------------------------------- /js/object-reference.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tricky code with object reference 3 | * 4 | * @Reference: 5 | * http://ejohn.org/apps/learn/#13 6 | * http://ejohn.org/apps/learn/#14 7 | * http://stackoverflow.com/questions/22216159/an-object-null-and-behaviour-in-javascript 8 | */ 9 | 10 | // Program 1 11 | (function () { 12 | var ninja = { 13 | yell: function (n) { 14 | return n > 0 ? ninja.yell(n - 1) + "a" : "hiy"; 15 | } 16 | }; 17 | console.log(ninja.yell(4) == "hiyaaaa"); 18 | 19 | var samurai = {yell: ninja.yell}; 20 | var ninja = null; 21 | 22 | try { 23 | console.log(samurai.yell(4)); 24 | } catch (e) { 25 | console.log(false, "Uh, this isn't good! Where'd ninja.yell go?"); 26 | } 27 | // Program 1 doesn't work because inside the ninja.yell function, you are referring to ninja again: 28 | // return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 29 | // So, if later on your are assigning null to ninja, this code will throw an error because null doesn't have a property yell. 30 | })(); 31 | 32 | // Program 2 33 | (function () { 34 | var ninja = { 35 | yell: function yell(n) { // We are using a named function here, instead of an anonymous fn in Program 1. 36 | return n > 0 ? yell(n - 1) + "a" : "hiy"; // Calling `yell` instead of `ninja.yell` as in Program 1. 37 | } 38 | }; 39 | console.log(ninja.yell(4) == "hiyaaaa"); 40 | 41 | var samurai = {yell: ninja.yell}; // ninja.yell already assigned before ninja={} 42 | var ninja = null; 43 | 44 | try { 45 | console.log(samurai.yell(4)); 46 | } catch (e) { 47 | console.log(false, "Uh, this isn't good! Where'd ninja.yell go?"); 48 | } 49 | // Program 2 works because, instead of referring to the object that holds the function (ninja), 50 | // you are giving the function a name and directly refer to that name. 51 | })(); 52 | -------------------------------------------------------------------------------- /js/oloo-pattern.js: -------------------------------------------------------------------------------- 1 | /** 2 | * OLOO (objects linked to other objects) pattern explored 3 | * 4 | * @Reference: 5 | * https://gist.github.com/getify/d0cdddfa4673657a9941 6 | * https://gist.github.com/getify/5572383 7 | * https://gist.github.com/getify/9895188 8 | * https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md 9 | */ 10 | 11 | // CONSTRUCTOR SYNTAX VS OLOO 12 | // Constructor form 13 | function Foo() { 14 | } 15 | Foo.prototype.y = 11; 16 | 17 | function Bar() { 18 | } 19 | // Object.create(proto[, propertiesObject]) method creates a new object with the specified prototype object and properties. 20 | Bar.prototype = Object.create(Foo.prototype); 21 | Bar.prototype.z = 31; 22 | 23 | var x = new Bar(); 24 | console.log(x.y + x.z); // 42 25 | 26 | 27 | // OLOO form 28 | var FooObj = {y: 11}; 29 | 30 | var BarObj = Object.create(FooObj); 31 | BarObj.z = 31; 32 | 33 | var x = Object.create(BarObj); 34 | console.log(x.y + x.z); // 42 35 | 36 | 37 | /** 38 | * CLASS SYNTAX VS OLOO 39 | */ 40 | // ES6 Class style 41 | class Foo { 42 | constructor(x, y, z) { 43 | // Object.assign(target, ...sources) method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object. 44 | Object.assign(this, {x, y, z}); 45 | } 46 | 47 | hello() { 48 | console.log(this.x + this.y + this.z); 49 | } 50 | } 51 | 52 | var instances = []; 53 | for (var i = 0; i < 500; i++) { 54 | instances.push( 55 | new Foo(i, i * 2, i * 3) 56 | ); 57 | } 58 | instances[37].hello(); // 222 59 | 60 | 61 | // OLOO Form 62 | function Foo(x, y, z) { 63 | return { 64 | hello() { 65 | console.log(this.x + this.y + this.z); 66 | }, 67 | x, 68 | y, 69 | z 70 | }; 71 | } 72 | 73 | var instances = []; 74 | 75 | for (var i = 0; i < 500; i++) { 76 | instances.push( 77 | Foo(i, i * 2, i * 3) 78 | ); 79 | } 80 | instances[37].hello(); // 222 -------------------------------------------------------------------------------- /js/setTimeout-inside-loop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * setTimeout() inside a for() loop. 3 | * 4 | * @TLDR: setTimeout is executed after the loop is done, if the time it take to loop is less than the timeout value. 5 | * Also, it accesses the looping variable value only at the time of execution after the timout. 6 | * 7 | * @Info: Use an IIFE to lock the looping variable within for each iteration of the loop inside a closure/ 8 | * 9 | * @Reference: Best explanation out there on Event loops: https://www.youtube.com/watch?v=8aGhZQkoFbQ 10 | * 11 | */ 12 | 13 | (function () { 14 | 15 | // setTimeout() inside a loop. 16 | for (var i = 1; i <= 3; i++) { 17 | setTimeout(function () { 18 | console.log(i); // prints 4 4 4 19 | }, 1000); 20 | } 21 | 22 | // Work all fine if we use `let` keyword in ES6 23 | for (let i = 1; i <= 3; i++) { 24 | setTimeout(function () { 25 | console.log(i); // prints 1 2 3 26 | }, 1000); 27 | } 28 | 29 | // Locking the looped values inside a IIFE (closure). 30 | for (var i = 1; i <= 3; i++) { 31 | (function (index) { 32 | setTimeout(function () { 33 | console.log(index); // prints 1 2 3 34 | }, 1000); 35 | })(i); 36 | } 37 | 38 | // Note: When the IIFE is inside the setTimeout, it prints the correct values. 39 | // However, the values are printed immediately and not after the timout value. 40 | // Essentially rendering the setTimeout useless. 41 | // setTimeout() needs a fn as it's 1st parameter. 42 | for (var i = 1; i <= 3; i++) { 43 | setTimeout((function (index) { 44 | console.log(index); // prints 1 2 3 45 | })(i), 1000); 46 | } 47 | 48 | // You can still use and IIFE inside setTimeout(), but you need to return a function as it's first parameter. 49 | for (var i = 1; i <= 3; i++) { 50 | setTimeout((function (index) { 51 | return function () { 52 | console.log(index); // prints 1 2 3 53 | }; // IIFE needs to return a function that setTimeout can schedule. 54 | })(i), 1000); 55 | } 56 | 57 | // Note: Both setTimeout and setInterval accept and additional params that can be passed to the callback fn. 58 | // Thanks: https://twitter.com/WebReflection/status/701091345679708161 59 | for (var i = 0; i < 10; i++) { 60 | setTimeout(function (i) { 61 | console.log(i); 62 | // This will print 0 1 2 3 4 5 6 7 8 9 63 | }, 1000, i) 64 | } 65 | 66 | // Another way is to just create a separate function. 67 | for (var i = 0; i < 10; i++) { 68 | registerTimeout(i); 69 | } 70 | function registerTimeout (i) { 71 | setTimeout(function () { 72 | console.log(i); 73 | // This will print 0 1 2 3 4 5 6 7 8 9 74 | }, 1000); 75 | } 76 | })(); 77 | 78 | //use call/bind method(1) 79 | for(var i=0;i<5;i++){ 80 | setTimeout(console.log.bind(null,i),1000); 81 | } 82 | //use call/bind method(2) 83 | for(var i=0;i<5;i++){ 84 | //use bind/call to achieve this function, method(1) 85 | for(var i=0;i<10;i++){ 86 | setTimeout(console.log.bind(null,i),1000); 87 | } 88 | //use bind/call to achieve this function, method(2) 89 | for(var i=0;i<10;i++){ 90 | setTimeout(function(index){ 91 | console.log(index); 92 | }.bind(null,i),1000); 93 | } 94 | //use call/bind method(3) 95 | //use bind/call to achieve this function, method(3) 96 | for(var i=0;i<10;i++){ 97 | setTimeout(function(index){ 98 | return function(){ 99 | console.log(index); 100 | } 101 | }.call(null,i),1000); 102 | } 103 | -------------------------------------------------------------------------------- /js/shim-polyfill-monkeypatch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Shim vs Polyfill vs Monkey Patch 3 | * 4 | * Shim: 5 | * In english, shim means -- A thin strip of material used to align parts, make them fit -- which is exactly what it does in code as well. 6 | * A shim is a piece of code that intercepts the API calls and implements a different behavior. 7 | * The idea here is to normalize certain APIs across different environments. 8 | * So, if two browsers implement the same API differently, you could intercept the API calls in one of those browsers and make its behavior align with the other browser. 9 | * Or, if the browser has a bug in one of its APIs, you could again intercept calls to that API, and then circumvent the bug. 10 | * eg. https://github.com/es-shims/es5-shim 11 | * 12 | * Polyfill: 13 | * A polyfill is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively. Flattening the API landscape if you will. 14 | * Thus, a polyfill is a shim for a browser API. You typically check if a broswer supports an API and load a polyfill if it doesn't. That allows you to use the API in either case. 15 | * 16 | * Monkey Patching: 17 | * A good practise is to never modify the source of a library when using it on a given web app. Doing so makes upgrades of the library a nightmare and general maintenance impossible. 18 | * So what do you do while you wait for the library creators to fix their bug? You monkey patch. 19 | * So what is monkey patching? It's the process of replacing methods with updated, "fixing" methods for the original. 20 | * 21 | * @Reference: 22 | * https://github.com/vasanthk/simple-polyfill-array-find-es6 23 | * https://remysharp.com/2010/10/08/what-is-a-polyfill 24 | * http://addyosmani.com/blog/writing-polyfills/ 25 | * http://www.codeproject.com/Articles/369858/Writing-polyfills-in-Javascript 26 | * http://blog.respoke.io/post/111278536998/javascript-shim-vs-polyfill 27 | * https://davidwalsh.name/monkey-patching 28 | * http://benno.id.au/blog/2010/01/01/monkey-patching-javascript 29 | * http://me.dt.in.th/page/JavaScript-override/ 30 | * http://benno.id.au/blog/2010/01/01/monkey-patching-javascript 31 | */ 32 | 33 | /** 34 | * SHIM 35 | * Shim layer for requestAnimationFrame with setTimeout fallback 36 | * 37 | * @Reference: 38 | * http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ 39 | */ 40 | 41 | window.requestAnimFrame = (function () { 42 | return window.requestAnimationFrame || 43 | window.webkitRequestAnimationFrame || 44 | window.mozRequestAnimationFrame || 45 | function (callback) { 46 | window.setTimeout(callback, 1000 / 60); 47 | }; 48 | })(); 49 | 50 | // Usage: 51 | // Instead of setInterval(render, 16) 52 | (function animloop() { 53 | requestAnimFrame(animloop); 54 | render(); 55 | })(); 56 | // Place the rAF *before* the render() to assure as close to 60fps with the setTimeout fallback. 57 | 58 | 59 | /** 60 | * POLYFILL 61 | * A simple polyfill for Array.prototype.forEach() 62 | * 63 | * @Reference: 64 | * http://javascriptplayground.com/blog/2012/06/writing-javascript-polyfill/ 65 | * 66 | */ 67 | 68 | Array.prototype.forEach = function (callback, thisArg) { 69 | if (typeof(callback) !== 'function') { 70 | throw new TypeError(callback + ' is not a function'); 71 | } 72 | var len = this.length; 73 | for (var i = 0; i < len; i++) { 74 | callback.call(thisArg, this[i], i, this); // this[i] is the item, i is the index and `this` is the `this` arg for it. 75 | } 76 | }; 77 | 78 | // Usage 79 | var arr = [1, 2, 3]; 80 | arr.forEach(function (item, index, th) { 81 | console.log(item, index, th); 82 | }); 83 | 84 | // Output 85 | // 1 0 [ 1, 2, 3 ] 86 | // 2 1 [ 1, 2, 3 ] 87 | // 3 2 [ 1, 2, 3 ] 88 | 89 | 90 | /** 91 | * MONKEY PATCHING 92 | * Simple example to monkey patch a method in an object 93 | * 94 | * @Reference: 95 | * https://gist.github.com/vasanthk/5edd3a1f5f1231221fa4 96 | */ 97 | 98 | // Original method 99 | var object = { 100 | method: function (x, y) { 101 | return x + y; 102 | } 103 | }; 104 | 105 | // Add operations before or after 106 | object.method = (function (original) { 107 | return function (x, y) { 108 | // before 109 | // we could here modify 'arguments' to alter original input 110 | console.log(x, '+', y, '?'); 111 | 112 | // execute 113 | var result = original.apply(this, arguments); 114 | 115 | // after 116 | // here we could work on result to alter original output 117 | console.log('=', result); 118 | 119 | // aaaand the result 120 | return result; 121 | } 122 | })(object.method); 123 | -------------------------------------------------------------------------------- /js/string-methods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * String Methods 3 | * 4 | * @Reference: 5 | * http://www.w3schools.com/js/js_string_methods.asp 6 | * http://techiejs.com/Blog/Post/Essential-JavaScript-String-Functions 7 | * https://rapd.wordpress.com/2007/07/12/javascript-substr-vs-substring/ 8 | * http://www.bennadel.com/blog/2159-using-slice-substring-and-substr-in-javascript.htm 9 | * Quirks Mode:http://www.quirksmode.org/js/strings.html 10 | * 11 | */ 12 | 13 | // String.prototype.charAt() 14 | var myName = "Oleg Shalygin"; 15 | //Usage: myName.charAt(index) 16 | var letter = myName.charAt(6); //h 17 | 18 | 19 | //String.prototype.indexOf() 20 | var fullName = "Oleg Shalygin"; 21 | //Usage: fullName.indexOf(searchTerm, startingIndex) 22 | var index = fullName.indexOf("Oleg"); //0 23 | index = fullName.indexOf("Shalygin"); //5 24 | index = fullName.indexOf("l"); //1 25 | index = fullName.indexOf("l",4); //8 26 | index = fullName.indexOf("Girlfriend"); //-1 27 | 28 | 29 | // String.prototype.lastIndexOf() 30 | var storyMode = "Once upon a time, there was a magical foobar that was controlling the universe..."; 31 | console.log(storyMode.lastIndexOf(",")); //16 32 | console.log(storyMode.lastIndexOf(".")); //80 33 | console.log(storyMode.lastIndexOf("!")); //-1 34 | console.log(storyMode.lastIndexOf("foobar")); //38 35 | 36 | 37 | // String.prototype.match() 38 | var someString = "Hello there, my name is aararand and I am a magical foobarus creature"; 39 | var resultsArray = someString.match(/a{2}/); 40 | //resultsArray = ["aa", index: 24, input: "Hello there, my name is aararand and I am a magical foobarus creature"] 41 | var someOtherResultsArray = someString.match(/b{2}/); 42 | //someOtherResultsArray = null 43 | 44 | 45 | // String.prototype.replace() 46 | var someString = "Hello there, my name is aararand and I am a magical foobarus creature"; 47 | var newString = someString.replace(/a{2}/, "lol"); 48 | //newString = "Hello there, my name is lolrarand and I am a magical foobarus creature" 49 | 50 | 51 | // String.prototype.slice() 52 | var story = "Foobarus is a magical unicorn with an ID of 21313 which flies higher than all other unicorns. Unicorns fly? Regardless!"; 53 | var previewStory = story.slice(0, 40); //Foobarus is a magical unicorn with an ID 54 | 55 | 56 | // String.prototype.split() 57 | var story = "Foobarus is a magical unicorn with an ID of 21313 which flies higher than all other unicorns. Unicorns fly? Regardless!"; 58 | var previewStory = story.split("."); 59 | console.log(previewStory[0]); //Foobarus is a magical unicorn with an ID of 21313 which flies higher than all other unicorns 60 | console.log(previewStory[1]); //Unicorns fly? Regardless! 61 | 62 | 63 | // String.prototype.substring() 64 | // NOTE: 65 | // The second argument to substring is the index to stop at (but not include), 66 | // but the second argument to substr is the maximum length to return. 67 | // Syntax: string.substr(start, length); 68 | // Syntax: string.substring(start, stop); 69 | // 70 | // Also, the slice() and substring() methods are roughly the same: 71 | // the only difference is that the slice() method can accept a negative index, relative to the end of the string. 72 | var story = "Foobarus is a magical unicorn with an ID of 21313 which flies higher than all other unicorns. Unicorns fly? Regardless!"; 73 | var theLastFewCharacters = story.substring(story.length - 20); 74 | console.log("..." + theLastFewCharacters); // ..ns fly? Regardless! 75 | 76 | 77 | // String.prototype.toLowerCase() and String.prototype.toUpperCase() 78 | var story = "Foobarus is a magical unicorn with an ID of 21313 which flies higher than all other unicorns. Unicorns fly? Regardless!"; 79 | var allUpperCase = story.toUpperCase(); 80 | var allLowerCase = story.toLowerCase(); 81 | var foobarus = allUpperCase.match(/FOOBARUS/); //["FOOBARUS", index: 0, input: "FOOBARUS IS A MAGICAL UNICORN WITH AN ID OF 21313 …N ALL OTHER UNICORNS. UNICORNS FLY? REGARDLESS!] 82 | 83 | 84 | // String.prototype.trim() 85 | var fullName = " Oleg Shalygin "; 86 | var trimmedFullName = fullName.trim(); //Oleg Shalygin 87 | 88 | 89 | // String.prototype.concat() 90 | var firstName = "Oleg"; 91 | var lastName = "Shalygin"; 92 | var ID = 12321312; 93 | //Usage: firstName.concat(string2, string 3, string 3, ...); 94 | var fullIdentification = firstName.concat(lastName, ":", ID); 95 | console.log(fullIdentification); //OlegShalygin:12321312 96 | -------------------------------------------------------------------------------- /js/styling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styling using Vanilla JS 3 | * 4 | * @Reference: 5 | * http://javascript.info/tutorial/view-and-position 6 | * http://stackoverflow.com/a/21064102/1672655 7 | * https://developer.mozilla.org/en/docs/Web/API/Element/classList 8 | */ 9 | 10 | // className 11 | document.body.className += ' class3'; 12 | 13 | // classList 14 | 15 | var classList = document.body.classList, // returns *live* DOMTokenList collection 16 | i; 17 | 18 | classList.add('class1', 'class2'); 19 | 20 | if (classList.contains('class1') === true) { 21 | classList.remove('class1'); 22 | } 23 | 24 | for (i = 0; i < classList.length; i++) { 25 | console.log(i, classList.item(i)); 26 | } 27 | 28 | // style 29 | document.body.style.backgroundColor = 'green'; 30 | 31 | // cssText 32 | // style.cssText is the only way to add !important. 33 | var div = document.body.children[0]; 34 | div.style.cssText = 'color: red !important; \ 35 | background-color: yellow; \ 36 | width: 100px; \ 37 | text-align: center; \ 38 | blabla: 5; \ 39 | '; 40 | // blabla is ignored 41 | alert(div.style.cssText); 42 | 43 | // Reading the style 44 | // Note: The style gives access only to properties set through it, or with "style" attribute. 45 | // 48 | // 49 | // 52 | // 53 | 54 | // getComputedStyle 55 | // The syntax is: getComputedStyle(element, pseudo) 56 | // element - The element to get a styling for 57 | // pseudo - A pseudo-selector like ‘hover’ or null if not needed. 58 | var computedStyle = getComputedStyle(document.body, null); 59 | alert(computedStyle.marginTop); 60 | 61 | // CSS Box Model 62 | 63 | // clientWidth/Height 64 | // Size of the client area: content area with paddings, but without scrollbars. 65 | 66 | // scrollWidth/Height 67 | // Content area width and height including the scrolled out part. 68 | // scrollWidth/Height is same as clientWidth/Height, but includes full scrollable area. 69 | element.style.height = element.scrollHeight + 'px'; 70 | 71 | // scrollTop/scrollLeft 72 | // Size of scrolled out part: vertical and horizontal. The value is always in pixels. 73 | // scrollLeft/scrollTop are writeable 74 | // Unlike other properties, which are read-only, you can change scrollLeft/scrollTop, and the browser scrolls the element. 75 | // In standards mode, the scroll of the document is in document.documentElement. 76 | // The following code scrolls the document 10px down: 77 | document.documentElement.scrollTop += 10; 78 | 79 | //offsetWidth/Height 80 | //Outer box width/height, full size with borders, but without margins. 81 | 82 | //clientTop/Left 83 | //The indent of client area from box outer corner. In other words, the width of top/left border in pixels. 84 | 85 | //offsetParent, offsetLeft/Top 86 | //Properties offsetLeft and offsetTop reflect a relative shift of an element from its offsetParent. 87 | //The offsetParent is the parent element in the sense of layout. For example, if an element is positioned absolutely, the offsetParent is not it’s DOM parent, but a nearest positioned element (or BODY). 88 | // We could use this to check if an elem is hidden: 89 | function isHidden(elem) { 90 | return !elem.offsetWidth && !elem.offsetHeight; 91 | } 92 | // SUMMARY 93 | // Link: http://javascript.info/files/tutorial/browser/dom/metricSummary.png 94 | // 95 | //clientWidth/clientHeight - width/height of the visible in-border area (can be called a client area. The client area includes padding and doesn’t include scrollbars. 96 | //clientLeft/clientTop - left/top border width or, more generally, a shift of the client area from the top-left corner of the box. 97 | //scrollWidth/scrollHeight - width/height of the scrollable in-border area. Includes padding. Doesn’t include scrollbars. 98 | //scrollLeft/scrollTop - the width/height of the scrolled out part of the document, starting from the top-left corner. 99 | //offsetWidth/offsetHeight - the “outer” width/height of the box as seen from outside, excluding margins. 100 | //offsetParent - the nearest table-cell, body for static positioning or the nearest positioned element for other positioning types. 101 | //offsetLeft/offsetTop - the position in pixels of top-left corner of the box related to it’s offsetParent. 102 | 103 | // elem.getBoundingClientRect() 104 | // It returns a rectangle which encloses the element. The rectangle is given as an object with properties top, left, right, bottom. 105 | // Gotcha: 106 | // The coordinates are given relative to `window`, not the document**. For example, if you scroll this page, 107 | // so that the button goes to the window top, then its `top` coordinate becomes close to `0`, because it is given relative to window. 108 | // To calculate coordinates relative to the document that, we need to take page scroll into account. 109 | function showRect(elem) { 110 | var r = elem.getBoundingClientRect(); 111 | alert("Top/Left: " + r.top + " / " + r.left); 112 | alert("Right/Bottom: " + r.right + " / " + r.bottom); 113 | } 114 | 115 | // Coordinate calculator 116 | // The steps are: 117 | // 1) Get the enclosing rectangle. 118 | // 2) Calculate the page scroll. All browsers except IE<9 support `pageXOffset/pageYOffset`, and in IE when DOCTYPE is set, the scroll can be taken from documentElement(), otherwise from `body` - so we take what we can. 119 | // 3) The document (`html` or `body`) can be shifted from left-upper corner in IE. Get the shift. 120 | // 4) Add scrolls to window-relative coordinates and subtract the shift of `html/body` to get coordinates in the whole document. 121 | function getOffsetRect(elem) { 122 | // (1) 123 | var box = elem.getBoundingClientRect(); 124 | 125 | var body = document.body; 126 | var docElem = document.documentElement; 127 | 128 | // (2) 129 | var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop; 130 | var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft; 131 | 132 | // (3) 133 | var clientTop = docElem.clientTop || body.clientTop || 0; 134 | var clientLeft = docElem.clientLeft || body.clientLeft || 0; 135 | 136 | // (4) 137 | var top = box.top + scrollTop - clientTop; 138 | var left = box.left + scrollLeft - clientLeft; 139 | 140 | return {top: Math.round(top), left: Math.round(left)}; 141 | } 142 | -------------------------------------------------------------------------------- /js/this-keyword.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The elusive `this` keyword in JS 3 | * 4 | * @Reference: 5 | * http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work 6 | * http://stackoverflow.com/a/33979892/1672655 7 | * http://stackoverflow.com/a/17514482/1672655 8 | * https://dev.to/rachelralston/the-this-keyword-in-javascript 9 | * http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ 10 | * http://www.sitepoint.com/mastering-javascripts-this-keyword/ 11 | * http://www.quirksmode.org/js/this.html 12 | * https://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/ 13 | * https://www.codementor.io/javascript/tutorial/understanding--this--in-javascript 14 | * 15 | * Avoiding `this` keyword 16 | * https://luizfar.wordpress.com/2012/04/28/dont-use-this-in-javascript/ 17 | * http://stackoverflow.com/questions/31891931/why-does-jslint-forbid-the-this-keyword 18 | * http://stackoverflow.com/questions/28525393/how-can-i-get-rid-of-the-this-keyword-in-local-functions 19 | * http://stackoverflow.com/questions/30125464/how-to-avoid-the-this-and-new-keywords-in-javascript 20 | * 21 | */ 22 | 23 | // 1. Global this 24 | console.log(this === window); // true 25 | var foo = "bar"; 26 | console.log(this.foo); // "bar" 27 | console.log(window.foo); // "bar" 28 | 29 | // 2. Function this 30 | foo = "bar"; 31 | function testThis() { 32 | this.foo = "foo"; 33 | } 34 | console.log(this.foo); //logs "bar" 35 | testThis(); 36 | console.log(this.foo); //logs "foo" 37 | 38 | // 3. Prototype this 39 | function Thing() { 40 | console.log(this.foo); 41 | } 42 | Thing.prototype.foo = "bar"; 43 | var thing = new Thing(); //logs "bar" 44 | console.log(thing.foo); //logs "bar" 45 | 46 | // 4. Object this 47 | var obj = { 48 | foo: "bar", 49 | logFoo: function () { 50 | console.log(this.foo); 51 | } 52 | }; 53 | obj.logFoo(); //logs "bar" 54 | 55 | // 5. DOM Event this 56 | function Listener() { 57 | document.getElementById("foo").addEventListener("click", 58 | this.handleClick); 59 | } 60 | Listener.prototype.handleClick = function (event) { 61 | console.log(this); //logs "
" 62 | }; 63 | var listener = new Listener(); 64 | document.getElementById("foo").click(); //logs "
" 65 | 66 | // 6. HTML this 67 | 68 | //
69 | // 72 | 73 | // 7. jQuery this 74 | //
75 | //
76 | // 87 | 88 | // 8. Inside call(), apply() and bind() functions 89 | 90 | function add(inc1, inc2) { 91 | return this.a + inc1 + inc2; 92 | } 93 | 94 | var o = {a: 4}; 95 | document.write(add.call(o, 5, 6) + "
"); //15 96 | //above add.call(o,5,6) sets `this` inside 97 | //add() to `o` and calls add() resulting: 98 | // this.a + inc1 + inc2 = 99 | // `o.a` i.e. 4 + 5 + 6 = 15 100 | document.write(add.apply(o, [5, 6]) + "
"); //15 101 | // `o.a` i.e. 4 + 5 + 6 = 15 102 | 103 | var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6 104 | document.write(g() + "
"); //15 105 | 106 | var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ? 107 | document.write(h(6) + "
"); //15 108 | // 4 + 5 + 6 = 15 109 | document.write(h() + "
"); //NaN 110 | //no parameter is passed to h() 111 | //thus inc2 inside add() is `undefined` 112 | //4 + 5 + undefined = NaN 113 | --------------------------------------------------------------------------------