Skip to content

Instantly share code, notes, and snippets.

@YozhEzhi
Created September 4, 2020 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YozhEzhi/4f529be497a5b8b6cdcc643482ae33b0 to your computer and use it in GitHub Desktop.
Save YozhEzhi/4f529be497a5b8b6cdcc643482ae33b0 to your computer and use it in GitHub Desktop.
// ================================================================
// 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