Created
September 4, 2020 20:32
-
-
Save YozhEzhi/4f529be497a5b8b6cdcc643482ae33b0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ================================================================ | |
// Apply: Get max item | |
// ================================================================ | |
var arr = [1, 2, 50, 20, 38, 88]; | |
function getMax(arr) { | |
return Math.max.apply(null, arr); // ES6: Math.max(...arr) | |
}; | |
console.log(arr.getMax(arr)); // 88; | |
// ================================================================ | |
// Array: Самый простой способ очистить массив: | |
// ================================================================ | |
arr.length = 0; | |
// ================================================================ | |
// Array: Вызов методов массивов в контексте массивоподобного | |
// объекта (одалживание метода): | |
// ================================================================ | |
function hasTwo() { | |
return Array.prototype.indexOf.call(arguments, 'two') !== -1; | |
} | |
console.log(hasTwo('uno', 'tuo', 'tre')); // false | |
console.log(hasTwo('one', 'two', 'three')); // true | |
// ================================================================ | |
// CLOSURE | |
// ================================================================ | |
// Замыкание - это функция вместе со всеми внешними переменными, | |
// которые ей доступны. | |
// В JS каждая функция, что вызывается создаёт контекст вызова. | |
// Замыкания используются в JS для приватности данных объекта, | |
// в обработчиках событий и в функциях обратного вызова и т.д. | |
// Замыкание предоставляет доступ к внешней области видимости из | |
// внутренней функции. | |
// | |
// Внутренняя функция в примере - создаёт Замыкание на функцию makeCounter. | |
// ================================================================ | |
function makeCounter() { | |
// `i` is only accessible inside `makeCounter`. | |
var i = 0; | |
return function() { | |
console.log(++i); | |
}; | |
} | |
// Note that `counter` and `counter2` each have their own scoped `i`. | |
var counter = makeCounter(); | |
counter(); // logs: 1 | |
var counter2 = makeCounter(); | |
counter2(); // logs: 1 | |
console.log(i); // undefined [it only exists inside makeCounter] | |
// Другое хорошее применение замыканий — создание функций, в свою очередь | |
// тоже создающих функции — то, что некоторые назвали бы приёмом | |
// т.н. метапрограммирования. | |
// Благодаря замыканию возвращаемая функция «запоминает» параметры, | |
// переданные функции создающей, что нам и нужно для подобного рода вещей. | |
var createHelloFunction = function(name) { | |
return function() { | |
alert('Hello, ' + name); | |
}; | |
}; | |
var sayHelloHabrahabr = createHelloFunction('Habrahabr'); | |
sayHelloHabrahabr(); //alerts «Hello, Habrahabr» | |
// Callback: Передача аргументов в функции обратного вызова | |
// По умолчанию в callback-функцию нельзя передать никаких аргументов. | |
// Но можно использовать Замыкание: | |
var sum = function(a, b) { | |
return function() { | |
console.log(a + b); | |
}; | |
}; | |
// теперь можно передавать аргументы: | |
document.querySelector('a').addEventListener('click', sum(1, 5), false); | |
// подобное использование замыканий накладывает ограничения на работу с событиями — каждый | |
// раз в обработчик передается новая анонимная функция. Это означает, что убрать обработчик | |
// с помощью removeEventListener не удастся. | |
// Другой способ передать аргументы в callback — использовать метод функций bind: | |
var log = function(message) { | |
console.log(message); | |
}; | |
var link = document.querySelector('a'); | |
link.addEventListener('click', log.bind(this, 'Hello world'), false); | |
// ================================================================ | |
// Testing: Для измерения производительности отдельных блоков кода | |
// ================================================================ | |
console.time('New Array'); | |
var arr = []; | |
for (var i = 0; i < 100; i++) { | |
arr.push({ i: i }); | |
} | |
console.timeEnd('New Array'); | |
// ================================================================ | |
// Replacing switch statements with Object literals | |
// ================================================================ | |
function getDrink(type) { | |
var drinks = { | |
'coke': 'Coke', | |
'pepsi': 'Pepsi', | |
'lemonade': 'Lemonade', | |
'default': 'Default item' | |
}; | |
return 'The drink I chose was ' + (drinks[type] || drinks['default']); | |
} | |
var drink = getDrink('coke'); | |
console.log(drink); // The drink I chose was Coke | |
// Variation: | |
var type = 'coke'; | |
var drinks = { | |
'coke': function() { | |
return 'Coke'; | |
}, | |
'pepsi': function() { | |
return 'Pepsi'; | |
}, | |
'lemonade': function() { | |
return 'Lemonade'; | |
} | |
}; | |
drinks[type](); | |
// Variation II: | |
function getDrink(type) { | |
var drinks = { | |
'coke': function() { | |
return 'Coke'; | |
}, | |
'pepsi': function() { | |
return 'Pepsi'; | |
}, | |
'lemonade': function() { | |
return 'Lemonade'; | |
} | |
}; | |
return drinks[type](); | |
} | |
// let's call it | |
var drink = getDrink('coke'); | |
console.log(drink); // 'Coke' | |
// Variation III (with default statement): | |
function getDrink(type) { | |
var drinks = { | |
'coke': function() { | |
return 'Coke'; | |
}, | |
'pepsi': function() { | |
return 'Pepsi'; | |
}, | |
'lemonade': function() { | |
return 'Lemonade'; | |
}, | |
'default': function() { | |
return 'Default item'; | |
} | |
}; | |
return (drinks[type] || drinks['default'])(); | |
} | |
// called with "dr pepper" | |
var drink = getDrink('dr pepper'); | |
console.log(drink); // 'Default item' | |
// Variation IV: | |
function getDrink(type) { | |
var drink; | |
var drinks = { | |
'coke': function() { | |
drink = 'Coke'; | |
}, | |
'pepsi': function() { | |
drink = 'Pepsi'; | |
}, | |
'lemonade': function() { | |
drink = 'Lemonade'; | |
}, | |
'default': function() { | |
drink = 'Default item'; | |
} | |
}; | |
// invoke it | |
(drinks[type] || drinks['default'])(); | |
// return a String with chosen drink | |
return 'The drink I chose was ' + drink; | |
} | |
var drink = getDrink('coke'); | |
console.log(drink); // The drink I chose was Coke | |
// ================================================================ | |
// Лучший способ проверки на наличие свойства у объекта | |
// https://toddmotto.com/methods-to-determine-if-an-object-has-a-given-property/ | |
// ================================================================ | |
var toddObject = { | |
name: 'Todd', | |
favouriteDrink: null, | |
cool: false | |
}; | |
'cool' in toddObject; // true | |
// ================================================================ | |
// Fibonacci sequence fastest variant (O(n)) | |
// ================================================================ | |
function fibonacci(num) { | |
var prev = 1, | |
next = 0, | |
temp; | |
while (num >= 0) { | |
temp = prev; | |
prev = prev + next; | |
next = temp; | |
num--; | |
} | |
return next; | |
} | |
// ================================================================ | |
// Функция сравнения объектов | |
// ================================================================ | |
function deepEqual(a, b) { | |
if (a === b) { | |
return true; | |
} | |
if (a === null || typeof(a) != "object" || | |
b === null || typeof(b) != "object") { | |
return false; | |
} | |
var propertiesInA = 0, | |
propertiesInB = 0; | |
for (var property in a) { | |
propertiesInA += 1; | |
} | |
for (var property in b) { | |
propertiesInB += 1; | |
if (!(property in a) || !deepEqual(a[property], b[property])) { | |
return false; | |
} | |
} | |
return propertiesInA == propertiesInB; | |
} | |
// ================================================================ | |
// Why object literals in JavaScript are cool. | |
// https://rainsoft.io/why-object-literals-in-javascript-are-cool/ | |
// ================================================================ | |
/** | |
* You had to use Object.create() in combination with the object | |
* literal to setup the prototype: | |
*/ | |
var myProto = { | |
propertyExists: function(name) { | |
return name in this; | |
} | |
}; | |
var myNumbers = Object.create(myProto); | |
myNumbers.array = [1, 6, 7]; | |
myNumbers.propertyExists('array'); // => true | |
myNumbers.propertyExists('collection'); // => false | |
/** | |
* ES6 solves the problems described above and improves the object | |
* literal with additional goodies: | |
* - setup the prototype on object construction; | |
* - shorthand method declarations; | |
* - make super calls; | |
* - computed property names; | |
*/ | |
/** | |
* 1. Setup the prototype on object construction. | |
* | |
* myNumbers object is created with the prototype myProto using | |
* a special property name __proto__. | |
* The object is created in a single statement, without additional | |
* functions like Object.create(). | |
*/ | |
var myProto = { | |
propertyExists: function(name) { | |
return name in this; | |
} | |
}; | |
var myNumbers = { | |
__proto__: myProto, | |
array: [1, 6, 7], | |
}; | |
myNumbers.propertyExists('array'); // => true | |
myNumbers.propertyExists('collection'); // => false | |
/** | |
* 2.1. Special cases of __proto__ usage. | |
* | |
* Even if __proto__ seems simple, there are some particular scenarios | |
* that you should be aware of. | |
* | |
* It is allowed to use __proto__ only once in the object literal. | |
* On duplication JavaScript throws an error. | |
* | |
* JavaScript constraints to use only an object or null as a value for | |
* __proto__ property. Any attempt to use primitive types (strings, numbers, | |
* booleans) or undefined is simply ignored and does not change object's prototype. | |
* | |
* 2. Shorthand method definition. | |
*/ | |
var collection = { | |
items: [], | |
add(item) { | |
this.items.push(item); | |
}, | |
get(index) { | |
return this.items[index]; | |
} | |
}; | |
collection.add(15); | |
collection.add(3); | |
collection.get(0); // => 15 | |
/** | |
* A nice benefit is that methods declared this way are named functions, | |
* which is useful for debugging purposes. Executing 'collection.add.name' | |
* from previous example returns the function name 'add'. | |
*/ | |
/** | |
* 3. Make 'super' calls. | |
* | |
* 'Super' keyword as way to access inherited properties from the prototype chain. | |
*/ | |
var calc = { | |
sumArray (items) { | |
return items.reduce(function(a, b) { | |
return a + b; | |
}); | |
} | |
}; | |
var numbers = { | |
__proto__: calc, | |
numbers: [4, 6, 7], | |
sumElements() { | |
return super.sumArray(this.numbers); | |
} | |
}; | |
numbers.sumElements(); // => 17 | |
/** | |
* 3.1 'super' usage restriction. | |
* | |
* 'Super' can be used only inside the shorthand method definition | |
* in an object literal. | |
*/ | |
var calc = { | |
sumArray (items) { | |
return items.reduce(function(a, b) { | |
return a + b; | |
}); | |
} | |
}; | |
var numbers = { | |
__proto__: calc, | |
numbers: [4, 6, 7], | |
sumElements: function() { | |
return super.sumArray(this.numbers); | |
} | |
}; | |
// Throws SyntaxError: 'super' keyword unexpected here | |
numbers.sumElements(); | |
/** | |
* The method sumElements is defined as a property: sumElements: function() {...}. | |
* Because super requires to be used only inside shorthand methods, | |
* calling it in such situation throws SyntaxError: 'super' keyword unexpected here. | |
*/ | |
/** | |
* 4. Computed property names. | |
*/ | |
function prefix(prefStr, name) { | |
return prefStr + '_' + name; | |
} | |
var object = { | |
[prefix('number', 'pi')]: 3.14, | |
[prefix('bool', 'false')]: false | |
}; | |
object; // => { number_pi: 3.14, bool_false: false } | |
/** | |
* 4.1 'Symbol' as property name. | |
* | |
* For example, let's use the special property Symbol.iterator | |
* and iterate over the own property names of an object. | |
*/ | |
var object = { | |
number1: 14, | |
number2: 15, | |
string1: 'hello', | |
string2: 'world', | |
[Symbol.iterator]: function *() { | |
var own = Object.getOwnPropertyNames(this); | |
var prop; | |
while(prop = own.pop()) { | |
yield prop; | |
} | |
} | |
} | |
[...object]; // => ['number1', 'number2', 'string1', 'string2'] | |
/** | |
* [Symbol.iterator]: function *() { } defines a property | |
* that is used to iterate over owned properties of the object. | |
* The spread operator [...object] uses the iterator and returns | |
* the list of owned properties. | |
*/ | |
/** | |
* 5. A look into the future: rest and spread properties. | |
* | |
* For example, let's use the special property Symbol.iterator | |
* and iterate over the own property names of an object. | |
*/ | |
var object = { | |
propA: 1, | |
propB: 2, | |
propC: 3 | |
}; | |
let {propA, ...restObject} = object; | |
propA; // => 1 | |
restObject; // => { propB: 2, propC: 3 } | |
/** | |
* Spread properties allows to copy into an object literal | |
* the owned properties from a source object. | |
* In this example the object literal collects into | |
* object additional properties from source object: | |
*/ | |
var source = { | |
propB: 2, | |
propC: 3 | |
}; | |
var object = { | |
propA: 1, | |
...source | |
} | |
object; // => { propA: 1, propB: 2, propC: 3 } | |
// MERGE ARRAY USING push() | |
// Clever use of apply() to join 2 arrays | |
(function () { | |
var a = [1, 2]; | |
var b = ['x', 'y']; | |
// WE DONT WANT a.push(b) since it returns [1, 2, ['x', 'y']]; | |
a.push.apply(a, b); | |
console.log(a); // [1, 2, 'x', 'y'] | |
// Alternative: a = a.concat(b); | |
})(); | |
/** | |
* Array every() and some() (instead of forEach) | |
* | |
* Another limitation with forEach is that you can’t break out of the loop (and no, using exceptions doesn’t count). | |
* As a result, I’ve seen developers either revert back to for loops when needing to be able to break out, | |
* or needlessly iterate over extraneous array elements. | |
* | |
* A better solution exists in the form of the lesser known every() and some() array iteration methods. | |
* every iterates until the provided callback returns false, and some iterates until the provided callback returns true. | |
* | |
* Both every and some have the same browser support as forEach. | |
* | |
* @Reference: | |
* http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/ | |
* https://coderwall.com/p/_ggh2w/the-array-native-every-filter-map-some-foreach-methods | |
* | |
*/ | |
// some() breaks once it returns true | |
(function () { | |
// God of cricket | |
var ar = ['Lara', 'Sachin', 'De Villiers']; | |
ar.some(function (v) { | |
if (v === 'Sachin') { | |
return true; | |
} | |
console.log('Great cricketers: ' + v); | |
}); | |
})(); | |
// every() breaks once it returns false | |
(function () { | |
// Music Composers | |
var ar = ['Hans Zimmer', 'Bill Clinton', 'Clint Mansell']; | |
ar.every(function (v) { | |
if (v === 'Bill Clinton') { | |
return false; | |
} | |
console.log('Great Composers: ' + v); | |
}); | |
})(); | |
// every() and some() in an example | |
(function () { | |
function isBigEnough(element) { | |
return element >= 10; | |
} | |
function isBigEnough2(element) { | |
return element >= 1; | |
} | |
var passed = [2, 5, 8, 1, 4].some(isBigEnough); | |
console.log('some: For [2, 5, 8, 1, 4] are the values larger or equal to 10 ? ' + passed); | |
// some: For [2, 5, 8, 1, 4] are the values larger or equal to 10 ? false | |
var passed = [12, 5, 8, 1, 4].some(isBigEnough); | |
console.log('some: For [12, 5, 8, 1, 4] are the values larger or equal to 10 ? ' + passed); | |
// some: For [12, 5, 8, 1, 4] are the values larger or equal to 10 ? true | |
var passed = [12, 5, 8, 1, 4].every(isBigEnough); | |
console.log('every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 10 ? ' + passed); | |
// every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 10 ? false | |
var passed = [12, 5, 8, 1, 4].every(isBigEnough2); | |
console.log('every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 1 ? ' + passed); | |
// every: For [12, 5, 8, 1, 4] are "ALL" the values larger or equal to 1 ? true | |
})(); | |
/** | |
* Convert raw string to JSON. | |
* @link http://stackoverflow.com/a/16214042 | |
* @param rawString - string that should be parsed to an array; | |
*/ | |
function stringToArray(rawString) { | |
let newJson = rawString.replace(/([a-zA-Z0-9]+?):/g, '"$1":'); | |
newJson = newJson.replace(/'/g, '"'); | |
return JSON.parse(newJson); | |
} | |
/** | |
* Basically, the slice() operation clones the array and returns the | |
* reference to the new array. | |
*/ | |
// For object references (and not the actual object), slice copies object | |
// references into the new array. | |
// Both the original and new array refer to the same object. | |
// object | |
var obj = {"abc": 456}; | |
var arr = [obj].slice(); // [{"abc": 456}] | |
obj.abc = 4567; | |
console.log(arr, obj); // [{"abc": 4567}] {"abc": 4567} | |
// array | |
var oldarr = [456]; | |
var arr = [oldarr].slice(); // [[456]] | |
oldarr[0] = 4567; | |
console.log(arr, oldarr); // [[4567]] [4567] | |
// For strings and numbers, slice copies strings and numbers into the new array. | |
// Changes to the string or number in one array does not affect the other array. | |
// string in array | |
var oldarr = ['abc']; | |
var arr = oldarr.slice(); // ['abc'] | |
oldarr[0] = 'abcd'; | |
console.log(arr, oldarr); // ['abc'] ['abcd'] | |
// number in array | |
var oldarr = [123, 456, 789]; | |
var arr = oldarr.slice(0, 2); // [123, 456] | |
oldarr[1] = 777; | |
console.log(arr, oldarr); // [123, 456] [123, 777, 789] | |
// We could use this method to prevent original array mutation: | |
var oldArr = [3, 'my new post', {345: 1}]; | |
function renderData(arr) { | |
arr.push(444); | |
return arr; | |
} | |
var newArray = renderData(oldArr.slice()); | |
console.log(newArray); // new array [3, 'my new post', {345: 1}, 444] | |
console.log(oldArr); // original [3, 'my new post', {345: 1}] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment