├── 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 | //
one
two
233 | //
three
four
234 | //
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 | //
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 | //
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 | //
16 | //
17 | //
18 | //
19 | //
20 | //
21 | //
22 | //
23 | //
24 | //
25 | //
26 | //
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 |
--------------------------------------------------------------------------------