Skip to content

Instantly share code, notes, and snippets.

@YozhEzhi
Created September 4, 2020 20:34
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/e53cd666404ea7beb43af893b1fdab86 to your computer and use it in GitHub Desktop.
Save YozhEzhi/e53cd666404ea7beb43af893b1fdab86 to your computer and use it in GitHub Desktop.
Д. Крокфорд — JavaScript. Сильные стороны
// ================================================================
// Крокфорд - JS Сильные стороны
// ================================================================
/**
Старый полифил для Object.create():
*/
if (typeof Object.create() !== 'function' {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
}
}
/**
Некоторые ЯП предлагают оптимизацию хвостовой рекурсии: если функция завершается вызовом самой себя, то вызвов заменяется циклом, что ускоряет процесс.
В JS нет оптимизации хвостовой рекурсии. Т.о. глубокая рекурсиия может переполнить стек.
Замыкание - связь функции с контекстом, в котором она была вызвана.
Забавный пример неправильно реализации навешивания обработчика события в цикле:
*/
// ПЛОХОЙ ПРИМЕР:
var addHandlers = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
nodes[i].onclick = function() {
alert(i);
}
}
};
/**
В примере выше обработчик всегда будет выводить последнее значение i.
Потому, что обработчик функции связывется с переменной i, а не со значением переменной i
в момент создания функции.
*/
// ПРАВИЛЬНЫЙ ВАРИАНТ:
var addHandlers = function (nodes) {
var helper = function (i) {
return function () {
alert(i);
}
};
for (var i = 0; i < nodes.length; i++) {
nodes[i].onclick = helper(i);
}
};
/**
Каррирование.
Метод curry создаёт замыкание, которое хранит первоначальную функцию и аргументы
в curry. Замыкание возвращает функцию, при вызове которой возвращается результат вызова
первоначальной функции, в которую передаются все аргументы curry и текущего вызова.
Метод concat - объединяет два массива аргументов.
*/
var result = add.curry(1);
result(6); // 7
Function.method('curry', function () {
var slice = Array.prototype.slice;
var args = slice.apply(arguments);
var that = this;
return function () {
return that.apply(null, args.concat(slice.apply(arguments)));
};
});
/**
Мемоизация.
Функции могут использовать объекты для хранения результатов предыдущих вычислений,
что позволяет избежать ненужной работы.
*/
var fibo = function (n) {
return n < 2 ? n : fibo(n - 1) + fibo(n - 2);
};
/**
Если вывести по циклу 10 первых чисел, то функция вызовется 453 раза:
11 раз в программе и ещё 442 раза она вызовет сама себя.
Можно закэшировать вычисленные данные (мемоизировать).
*/
var fibo = (function () {
var memo = [0, 1];
var fib = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fib(n - 1) + fib(n - 2);
memo[n] = result;
}
return result;
}
return fib;
}());
/**
Теперь функция вызывается 29 раз: 11 раз в программе и 18 раз она вызывает
себя, чтобы получить ранее мемоизированные результаты.
Создадим функцию мемоизации.
Она будет получать исходный массив memo и функцию formula.
Она возвращает рекурсивную функцию, управляющую хранилищем memo и
вызывающую функцию formula по мере необходимости. Функция recur вместе с
параметрами передаётся в функцию formula:
*/
var memoizer = function (memo, formula) {
var recur = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = formula(recur, n);
memo[n] = result;
}
return result;
}
return recur;
};
var fibo = memoizer([1, 1], functiom (recur, n) {
return n * recur(n - 1);
});
/**
Псевдоклассовое наследование.
Вместо того чтобы наследоваться невосредственно от других объектов,
объекты в js создаются с помощью конструктора функций.
Новый объект функции задается свойством prototype, причем его значением является объект, содержащий свойство constructor, значением которого является новый объект функции.
Объект prototype - это место, где хранятся унаследованные черты.
Если вызывать конструктор функции-класса безоператора new, то this связывается не
с новосозданным объектом, а с глобальным контекстом - window.
*/
/**
Прототипизированное наследование.
Сначала можно сделать какие-то полезные объекты, а затем уже на их основе
создать множество похожих объектов. Классификации, т.е. разбиения приложений на
множество вложенных абстрактных классов, можно полностью избежать.
Ниже - пример дифференцированного наследования. Оно основывается на изменении
нового объекта с указанием отличий от объекта-родителя.
*/
var myMammal = {
name: 'Some Name',
getName: function() {
},
says: function() {
return this.saying || '';
}
};
var myCat = Object.create(myMammal);
myCat.name = 'Sonya';
myCat.saying = 'Meow';
myCat.getName = function() {
return this.name + ' says ' + this.says();
}
/**
Функциональное наследование.
*/
var constructor = function (spec, my) {
var that; // приватная переменная;
var my = my || {};
// публичные (открытые) переменные и методы:
that = a new object;
// дополняем that привилегированными методами:
return that;
}
/**
1. Создаётся объект.
Способы создания объекта:
- литерал объекта;
- через конструктор функции с префиксом new;
- методом Object.create;
- вызов любой функции, которая возвращает объект;
2. Определяет приватные методы и переменные экземпляра;
3. Дополняет новый объект методами, которые будут иметь доступ к внутренним
переменным;
4. Возвращает новый объект;
Объект spec содержит всю информацию, необходимую конструктору для создания экземпляра.
Объект my представляет собой контейнер со скрытыми данными, доступными для конструкторов в цепи наследования.
*/
/**
Для перечисления массива желательно использовать for вместо for...in.
For...in не даёт никаких гарантий относительно порядка предоставления элементов.
Также, существуют проблема с необычными свойствами, полученными по цепочке прототипов.
JS вносит путаницы из-за того, что typeof myArray === 'object'.
Есть такой вариант проверки на массив или объект:
*/
var isThisArray = function (value) {
return Object.prototype.toString.apply(value) === '[object Array]';
}
/**
Методы.
Правильный вариант сортировки массива чисел:
*/
let arr = [15, 8, 4, 16, 32, 23];
arr.sort((a, b) => a - b);
// arr === [4, 8, 15, 16, 23, 32];
/**
Сортировка массива чисел и строк:
*/
let arr = ['aa', 'bb', 'a', 8, 15, 4, 42];
arr.sort((a, b) => {
if (a === b) {
return 0;
}
if (typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
});
// arr === [4, 8, 15, 42, 'a', 'aa', 'bb'];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment