🥇Рекурсия - не магия! Небольшой шажок в сторону понимания рекурсии - точнее, применения на практике.
В известном алгоритме числа Фибоначчи вторым аргументом выступает... результат внутреннего вызова функции. Вот так всё просто. Возвращать единицу вторым условием нужно, чтобы прекратить рекурсивный цикл.
В известном алгоритме числа Фибоначчи вторым аргументом выступает... результат внутреннего вызова функции. Вот так всё просто. Возвращать единицу вторым условием нужно, чтобы прекратить рекурсивный цикл.
🥇Стали понятнее привязки стилей к элементам DOM!
element.style - одиночное свойство-атрибут элемента;
element.textCSS - атрибуты в виде строки. Перезаписывает ранее установленные одиночные атрибуты;
getComputedCSS(element, pseudo) - стили CSS, примененные к элементу. Возвращает объект, где ключи - названия стилей, а значения - css-свойства, вычисляемые как из style, так и из каскадных таблиц. К любому элементу применены все возможные стили, но большинство идёт по-умолчанию и не просчитывается.
element.style - одиночное свойство-атрибут элемента;
element.textCSS - атрибуты в виде строки. Перезаписывает ранее установленные одиночные атрибуты;
getComputedCSS(element, pseudo) - стили CSS, примененные к элементу. Возвращает объект, где ключи - названия стилей, а значения - css-свойства, вычисляемые как из style, так и из каскадных таблиц. К любому элементу применены все возможные стили, но большинство идёт по-умолчанию и не просчитывается.
🥇Глава по DOM - всё!
А еще сделал мигающий элемент с помощью setInterval.
А еще сделал мигающий элемент с помощью setInterval.
🥇Понятие Callback - функция, переданная в другую функцию для асинхронного - то есть, строго зависимого выполнения.
🥇Впервые придумал алгоритм, использующий рекурсию, вне тренировочных задач! Это осмысленное использование, а значит, большой шаг вперед. Алгоритм эмулирует перебор массива в цикле:
let relay = (array, x, y) => {
if(array[x] <= y) {
console.log(array[x]);
return relay(array, x + 1, y)
}
console.log('This is it!')
}
relay(array, 0, 10)
let relay = (array, x, y) => {
if(array[x] <= y) {
console.log(array[x]);
return relay(array, x + 1, y)
}
console.log('This is it!')
}
relay(array, 0, 10)
🥇Сделано многое - от разбора объекта событий до делегации. Впереди много больше...
-- особенности проверки типов props: для компонента вызываем propTypes с маленькой буквы, для проверки внутри объекта - с большой.
Camper.propTypes = {
name: PropTypes.string.isRequired
}
-- prop - это объект, передаваемый как аргумент в дочерние компоненты. Записывается он как кастомный атрибут (но без data-).
-- состояние - это объект с именем this.state, записанный в конструктор ES6-класса. Другие способы задать state...?
Обращаться надо обязательно через this, касается не только состояния.
-- состояния, пропсы и другое можно получать и управлять ими до return метода render().
Например, сохранить this.state.date в переменную date и вызывать в {JSX} только её.
-- componentDidMount() - метод жизненного цикла, который выполняется после render() - когда компонент уже отрисован (mount) в DOM. В нем следует писать вызовы API и обработчики событий.
-- Замыкая на себя происходящее в DOM, React обеспечивает кроссбраузерность: всё будет работать одинаково, независимо от поддержки браузерами методов кода.
-- Годная практика в Реакт - удалять обработчики событий в componentWillUnmount() - методе жизненного цикла, вызываемом перед удалением компонента из DOM.
Camper.propTypes = {
name: PropTypes.string.isRequired
}
-- prop - это объект, передаваемый как аргумент в дочерние компоненты. Записывается он как кастомный атрибут (но без data-).
-- состояние - это объект с именем this.state, записанный в конструктор ES6-класса. Другие способы задать state...?
Обращаться надо обязательно через this, касается не только состояния.
-- состояния, пропсы и другое можно получать и управлять ими до return метода render().
Например, сохранить this.state.date в переменную date и вызывать в {JSX} только её.
-- componentDidMount() - метод жизненного цикла, который выполняется после render() - когда компонент уже отрисован (mount) в DOM. В нем следует писать вызовы API и обработчики событий.
-- Замыкая на себя происходящее в DOM, React обеспечивает кроссбраузерность: всё будет работать одинаково, независимо от поддержки браузерами методов кода.
-- Годная практика в Реакт - удалять обработчики событий в componentWillUnmount() - методе жизненного цикла, вызываемом перед удалением компонента из DOM.
let z = {
misc: 5,
gett: function() {
return console.log( this.misc );
}
}
/*
Что, по шагам, делает функция bind?
1. принимает аргументы: внешнюю функцию и контекст.
2. возвращает функцию...
3. ...которая возвращает результат вызова внешней функции с привязкой контекста.
*/
function bind(func, context) {
return function() {
return func.apply(context, arguments);
}
}
setTimeout( bind(z.gett, z), 1000)
Что происходит здесь?
В setTimeout объявляется два аргумента. Аргумент - это, фактически, запись в переменную без её объявления.
Если передать в setTimeout функцию, не указывая контекст, она будет записана как переменная-аргумент - и потеряет контест (this).
Можно сказать, функция bind создаёт конструкцию вида:
var argument = function() {
return myFunction.apply(myContext, arguments);
}
Иначе говоря, первым аргументом setTimeout будет результат вызова myFunction в фиксированном контексте.
Встроенный метод .bind работает аналогично, но ему хватает и одного аргумента.
misc: 5,
gett: function() {
return console.log( this.misc );
}
}
/*
Что, по шагам, делает функция bind?
1. принимает аргументы: внешнюю функцию и контекст.
2. возвращает функцию...
3. ...которая возвращает результат вызова внешней функции с привязкой контекста.
*/
function bind(func, context) {
return function() {
return func.apply(context, arguments);
}
}
setTimeout( bind(z.gett, z), 1000)
Что происходит здесь?
В setTimeout объявляется два аргумента. Аргумент - это, фактически, запись в переменную без её объявления.
Если передать в setTimeout функцию, не указывая контекст, она будет записана как переменная-аргумент - и потеряет контест (this).
Можно сказать, функция bind создаёт конструкцию вида:
var argument = function() {
return myFunction.apply(myContext, arguments);
}
Иначе говоря, первым аргументом setTimeout будет результат вызова myFunction в фиксированном контексте.
Встроенный метод .bind работает аналогично, но ему хватает и одного аргумента.
Про Git:
git status = git st - команда проверяет статус репозитория (нет ли чего незакоммиченного).
Если сделаны нежелательные изменения, но еще не закоммичены, на этой стадии можно откатить их командой checkout.
Гит выведет подсказку о доступных на стадии командах.
git add - команда интексации изменений. На этой стадии их тоже можно откатить - гит вновь подскажет команду, если ввести git status.
Подсказка: git add с ключом —all проиндексирует все измененные файлы.
Стадия индексации нужна для гибкости работы.
При попадании в VIM по команде commit не следует паниковать. Просто вводим в первую строку описание коммита, жмем ECS, вводим :wq - и попадём назад в командную строку.
Бинды команд для Гит делаются так:
git config --global alias.shortcut full command
Например, у меня git checkout установлена как git ch.
git status = git st - команда проверяет статус репозитория (нет ли чего незакоммиченного).
Если сделаны нежелательные изменения, но еще не закоммичены, на этой стадии можно откатить их командой checkout.
Гит выведет подсказку о доступных на стадии командах.
git add - команда интексации изменений. На этой стадии их тоже можно откатить - гит вновь подскажет команду, если ввести git status.
Подсказка: git add с ключом —all проиндексирует все измененные файлы.
Стадия индексации нужна для гибкости работы.
При попадании в VIM по команде commit не следует паниковать. Просто вводим в первую строку описание коммита, жмем ECS, вводим :wq - и попадём назад в командную строку.
Бинды команд для Гит делаются так:
git config --global alias.shortcut full command
Например, у меня git checkout установлена как git ch.
Чтобы откатить содержимое файла после коммитов, нужно:
1. Вывести с помощью git log историю коммитов
2. Набрать git ch <хеш нужной стадии> (очень поможет установка сокращенных хешей)
3. Набрать git ch master
Здесь проявляется важность комментирования: чем подробнее и понятнее будет описание коммитов, чем чаще они будут делаться, тем яснее, к какой стадии создания программы удобнее вернуться.
1. Вывести с помощью git log историю коммитов
2. Набрать git ch <хеш нужной стадии> (очень поможет установка сокращенных хешей)
3. Набрать git ch master
Здесь проявляется важность комментирования: чем подробнее и понятнее будет описание коммитов, чем чаще они будут делаться, тем яснее, к какой стадии создания программы удобнее вернуться.
https://githowto.com/ru/aliases - как сделать сокращенные хеши и многое другое. И вообще курс полезный!
Простейший план работы с Git:
- Создается ветка продакшна (по умолчанию "master");
- В течение работы выбираются опорные точки, с которых хотели бы вводить экспериментальный код. Их роль играют коммиты;
- От опорных точек создаются новые ветки с помощью branch;
- По необходимости ответвляемся и от этих веток. Больше бранчей богу бранчей!
- Если экспериментальный код работает корректно, ветка сливается с master;
- Master пушится в удаленный репозиторий.
- Создается ветка продакшна (по умолчанию "master");
- В течение работы выбираются опорные точки, с которых хотели бы вводить экспериментальный код. Их роль играют коммиты;
- От опорных точек создаются новые ветки с помощью branch;
- По необходимости ответвляемся и от этих веток. Больше бранчей богу бранчей!
- Если экспериментальный код работает корректно, ветка сливается с master;
- Master пушится в удаленный репозиторий.
Некоторые сущности Git и полезные приемы:
- Большинство команд зависимо от контекста. Например:
git branch — выводит все текущие ветки;
git branch new-branch — создает новую ветку
- Не обязательно последовательно индексировать и коммитить изменения. Достаточно ввести команду:
git commit -a -m 'commentary'
А лучше добавить алиас и пользоваться шорткатом. Например, вместо предыдущей команды я пишу:
git com
- Конфликты, да, правим ручками. Как бы ни было больно. Для большинства редакторов и IDE есть плагины-помощники, но полностью автоматизировать это невозможно - по той же причине, что ограничивает сборщики мусора.
- Чтобы не париться с VIM, желательно заменить редактор по-умолчанию на свой. Гуглим "Git редактор по-умолчанию".
- Основные команды по работе с ветками:
- merge
- cherry-pike
- rebase
- pull (в сущности, автоматический merge)
Различаются они гибкостью настроек цепочки коммитов. Например, git rebase -i позволяет одновременно видеть список коммитов и переставлять их в дереве.
- Простой алгоритм слияния веток:
1. git commit -a -m 'start commit'
2. git branch new-branch
3. git checkout new-branch
4. git commit -a -m 'experimental code'
5. git checkout master
6. git merge new-branch
- Команда tag - лучший друг и помощник. Перемещаться по дереву удобнее всего именно с её помощью - проще запомнить тег версии, чем даже шесть символов хеша.
- Большинство команд зависимо от контекста. Например:
git branch — выводит все текущие ветки;
git branch new-branch — создает новую ветку
- Не обязательно последовательно индексировать и коммитить изменения. Достаточно ввести команду:
git commit -a -m 'commentary'
А лучше добавить алиас и пользоваться шорткатом. Например, вместо предыдущей команды я пишу:
git com
- Конфликты, да, правим ручками. Как бы ни было больно. Для большинства редакторов и IDE есть плагины-помощники, но полностью автоматизировать это невозможно - по той же причине, что ограничивает сборщики мусора.
- Чтобы не париться с VIM, желательно заменить редактор по-умолчанию на свой. Гуглим "Git редактор по-умолчанию".
- Основные команды по работе с ветками:
- merge
- cherry-pike
- rebase
- pull (в сущности, автоматический merge)
Различаются они гибкостью настроек цепочки коммитов. Например, git rebase -i позволяет одновременно видеть список коммитов и переставлять их в дереве.
- Простой алгоритм слияния веток:
1. git commit -a -m 'start commit'
2. git branch new-branch
3. git checkout new-branch
4. git commit -a -m 'experimental code'
5. git checkout master
6. git merge new-branch
- Команда tag - лучший друг и помощник. Перемещаться по дереву удобнее всего именно с её помощью - проще запомнить тег версии, чем даже шесть символов хеша.
Оценка сложности алгоритмов
Есть три шкалы оценки сложности алгоритмов: О-большое, Омега-большое и Тета-большое. Чаще всего используются первые два:
1. О-большое - производительность алгоритма в худшем случае.
2. Омега-большое - производительность в лучшем случае.
Запись O(n) означает, что алгоритм потребует количество единичных действий, состоящее из константы О и числа n - например, количества элементов массива.
О(n) - линейная сложность алгоритма. То есть, в худшем случае, алгоритм потребует n действий.
O(n2) - сложность, при которой потребуется n*n операций. Это откровенно низкая производительность.
O(lg n) - сложность, обозначаемая логарифмом от n. Это очень быстро. Например, для массива более чем из миллиона элементов потребуется всего 20 операций.
В оценке О-большого опускаются побочные множители. Самое главное - количество операций над структурой данных.
Есть три шкалы оценки сложности алгоритмов: О-большое, Омега-большое и Тета-большое. Чаще всего используются первые два:
1. О-большое - производительность алгоритма в худшем случае.
2. Омега-большое - производительность в лучшем случае.
Запись O(n) означает, что алгоритм потребует количество единичных действий, состоящее из константы О и числа n - например, количества элементов массива.
О(n) - линейная сложность алгоритма. То есть, в худшем случае, алгоритм потребует n действий.
O(n2) - сложность, при которой потребуется n*n операций. Это откровенно низкая производительность.
O(lg n) - сложность, обозначаемая логарифмом от n. Это очень быстро. Например, для массива более чем из миллиона элементов потребуется всего 20 операций.
В оценке О-большого опускаются побочные множители. Самое главное - количество операций над структурой данных.
Автотесты в JS
По-хорошему, разработка должна начинаться с BDD-документации. Это значит, что перед написанием кода мы пишем спецификацию, в которой опеределяем возможные результаты работы кода, и пишем его, подстраивая под них. Если код проходит тестирование в пограничных условиях - значит, всё замечательно.
Автотесты делают с помощью фреймворков. Самые популярные - Чай и Мока.
Они работают как NPM-модули, но могут и подключаться через браузер.
Простой модуль тестов через Моку состоит из трёх вложенных блоков:
- describe(string, callback())
- it(string, callback()
- assert.test(arg,[args])
Describe - блок для группировки тестов. Аргумент string - название блока, ничего более.
It - обертка конкретного теста. Первый аргумент - строка описания, что код должен делать при условиях, описанных тестом: it('should return false when...').
Assert - обращение к библиотеке функций, которая занимается непосредственно анализом результатов. У assert есть аналоги, и внутри assert есть множество разных методов. Здесь указан абстрактный метод "assert.test", идентичный настоящему "equal". Метод "equal" ожидаемо сравнивает первый аргумент со вторым. Осторожно! Метод использует нестрогое сравнение!
Другие методы assert:
https://nodejs.org/api/assert.html
По-хорошему, разработка должна начинаться с BDD-документации. Это значит, что перед написанием кода мы пишем спецификацию, в которой опеределяем возможные результаты работы кода, и пишем его, подстраивая под них. Если код проходит тестирование в пограничных условиях - значит, всё замечательно.
Автотесты делают с помощью фреймворков. Самые популярные - Чай и Мока.
Они работают как NPM-модули, но могут и подключаться через браузер.
Простой модуль тестов через Моку состоит из трёх вложенных блоков:
- describe(string, callback())
- it(string, callback()
- assert.test(arg,[args])
Describe - блок для группировки тестов. Аргумент string - название блока, ничего более.
It - обертка конкретного теста. Первый аргумент - строка описания, что код должен делать при условиях, описанных тестом: it('should return false when...').
Assert - обращение к библиотеке функций, которая занимается непосредственно анализом результатов. У assert есть аналоги, и внутри assert есть множество разных методов. Здесь указан абстрактный метод "assert.test", идентичный настоящему "equal". Метод "equal" ожидаемо сравнивает первый аргумент со вторым. Осторожно! Метод использует нестрогое сравнение!
Другие методы assert:
https://nodejs.org/api/assert.html
Элементарная математика
- Умножение - это совокупность тактов над каким-либо числом. Например, 6 * 3 = 6 + 6 + 6.
- Операцию выше можно назвать представлением. Представление - это описание числа как результата действий над другими числами. 18 = 6 * 3, 18 = 6 + 6 + 6, и 6 * 3 = 6 + 6 + 6.
- Разряд цифры - это ее позиция в числе. Возьмём 1024. В его разряде единиц - 4, в разряде десятков - 2, сотен - 0, тысяч - 1. Разряд говорит, сколько единиц, десятков, сотен, тысяч... и т.д. входят в число. Значением разряда могут быть только числа от 0 до 9, не считая дробей.
- Группировка чисел - это их представление как количества разрядных элементов. Скажем, 1024 = (100 * 10) + (10 * 2) * 4. Или 1000 + 20 + 4.
() => Разряды можно использовать для быстрого сложения больших чисел:
623 + 2422 = 2 + (6+4) + (2+2) + (2+3) = 3045.
В этом примере на третьей операции с конца произошло "переполнение разряда". Это значит, что при сложении значения разрядов сотен дали число больше 10. "Лишняя" цифра 1 переносится в следующий разряд, а в текущий записывается разряд единиц от этого числа.
Формально, такой способ перебрасывания лишних разрядов применяет для работы с числами представление их как групп разрядов. Применяется всё, что бысло описано выше.
В вычитании просиходит обратное - разряд переполняется в отрицательную сторону. Тогда просто заимствуем один из соседнего старшего разряда, записываем полученное число от в текущий разряд, а следующий считаем на единицу меньше:
756-699 = (6-6)+(14-9)+(16-9)=0+5+7=57.
() => Один из методов быстрого вычитания из чисел с большим количеством нулей на конце - вычитание единицы из таких чисел, затем классическое вычитание, затем прибавление единицы.
() => Если записать алгоритмически (на псевдо-JS) умножение на числа из первого разряда, равного 1, и остальными - равными 0, оно будет выглядеть так:
26 + 100 = 26.concat(00).
То есть мы просто переписываем нули из множителя во множимое. Или наоборот.
- Умножение - это совокупность тактов над каким-либо числом. Например, 6 * 3 = 6 + 6 + 6.
- Операцию выше можно назвать представлением. Представление - это описание числа как результата действий над другими числами. 18 = 6 * 3, 18 = 6 + 6 + 6, и 6 * 3 = 6 + 6 + 6.
- Разряд цифры - это ее позиция в числе. Возьмём 1024. В его разряде единиц - 4, в разряде десятков - 2, сотен - 0, тысяч - 1. Разряд говорит, сколько единиц, десятков, сотен, тысяч... и т.д. входят в число. Значением разряда могут быть только числа от 0 до 9, не считая дробей.
- Группировка чисел - это их представление как количества разрядных элементов. Скажем, 1024 = (100 * 10) + (10 * 2) * 4. Или 1000 + 20 + 4.
() => Разряды можно использовать для быстрого сложения больших чисел:
623 + 2422 = 2 + (6+4) + (2+2) + (2+3) = 3045.
В этом примере на третьей операции с конца произошло "переполнение разряда". Это значит, что при сложении значения разрядов сотен дали число больше 10. "Лишняя" цифра 1 переносится в следующий разряд, а в текущий записывается разряд единиц от этого числа.
Формально, такой способ перебрасывания лишних разрядов применяет для работы с числами представление их как групп разрядов. Применяется всё, что бысло описано выше.
В вычитании просиходит обратное - разряд переполняется в отрицательную сторону. Тогда просто заимствуем один из соседнего старшего разряда, записываем полученное число от в текущий разряд, а следующий считаем на единицу меньше:
756-699 = (6-6)+(14-9)+(16-9)=0+5+7=57.
() => Один из методов быстрого вычитания из чисел с большим количеством нулей на конце - вычитание единицы из таких чисел, затем классическое вычитание, затем прибавление единицы.
() => Если записать алгоритмически (на псевдо-JS) умножение на числа из первого разряда, равного 1, и остальными - равными 0, оно будет выглядеть так:
26 + 100 = 26.concat(00).
То есть мы просто переписываем нули из множителя во множимое. Или наоборот.
() => Алгоритм умножения многоразрядного числа на одноразрядное:
1. Умножаем значение разряда единиц множимого на множитель.
2. Если результат - многозначное число, пишем в результат его единицы, а значение разряда десятка перекидываем на следующий разряд множимого.
3. Умножаем следующий разряд.
4. Прибавляем к результату перекинутое значение в качестве единиц, записываем получившиеся единицы, а "лишний" разряд снова перебрасываем на следующий.
4. Повторяем, пока во множимом не кончатся числа. Последний "лишний" разряд записываем крайним слева.
1. Умножаем значение разряда единиц множимого на множитель.
2. Если результат - многозначное число, пишем в результат его единицы, а значение разряда десятка перекидываем на следующий разряд множимого.
3. Умножаем следующий разряд.
4. Прибавляем к результату перекинутое значение в качестве единиц, записываем получившиеся единицы, а "лишний" разряд снова перебрасываем на следующий.
4. Повторяем, пока во множимом не кончатся числа. Последний "лишний" разряд записываем крайним слева.
Методы элементарной математики - образцы оптимизации алгоритмов. Например, метод округления делимого и делителя в делении многозначных чисел значительно сокращает количество проверок частного. Мы сразу берем наиболее вероятное частное, проверяем, умножая его на делитель и сверяя с делимым, и по результату проверки увеличиваем/уменьшаем на один такт.
Вместо этого мы могли бы брать частное наугад, а машина - считать в цикле с нуля, что, очевидно, неоптимально при большой разнице между делимым и делителем.
Вместо этого мы могли бы брать частное наугад, а машина - считать в цикле с нуля, что, очевидно, неоптимально при большой разнице между делимым и делителем.