Created
September 4, 2020 20:34
-
-
Save YozhEzhi/e53cd666404ea7beb43af893b1fdab86 to your computer and use it in GitHub Desktop.
Д. Крокфорд — JavaScript. Сильные стороны
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
// ================================================================ | |
// Крокфорд - 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