Разве чистый JS не крут?
Сейчас, да и в принципе давно уже, мало кто пишет на чистом js. Все хотят писать на современных фреймворках. Я не являюсь исключением и на работе в основном пишу на VueJS(2.0+) и стареньком AngularJS. И этот выбор полностью оправдан. Не буду углубляться в подробности, но в большинстве случаев при грамотном построении приложения на фреймворке вы выиграете в скорости, поддерживаемости и читаемости.
Так к чему же эта статья?
Я хочу повествовать о том, что не стоит забывать о чистом js-е. Ведь все же мы начинали с простого:
console.log('Hello world');
Вы же не будете писать на фреймворке такое? Хотя, возможно, я очень плохо с вами знаком. В этой статье я хочу написать фильтры на чистом js-е. Задача, в принципе, тривиальная и не требует каких-либо задатков гения. Я постараюсь написать как можно более сжатый код. Не гарантирую, что читателю, который не знаком с особенностями js-а будет понятно. Однако я постараюсь описать весь процесс как можно подробнее.
Начнем
Для начала примерно набросаем, что нам надо и как это будет работать.
Нам нужны данные. Пусть будет статический массив. Поехали.
let list = ['JavaScript','Kotlin','Rust','PHP','Ruby','Java','MarkDown','Python','C++','Fortran','Assembler']
(Заранее прошу прощения, если не указал ваш любимый язык)
Отлично, у нас есть данные. На самом деле эти данные должны получаться через запросы к серверу, но у нас, как и у «ООО» — упрощенная модель.
Для вывода я сделал инпут и список. Выглядит это вот так:
<input type="text" id="search">
<ul id="results">
</ul>
В input пользователь вводит запрос, а список будет заполняться в зависимости от запроса. И еще определим, что, если input пустой, то мы будем выводить весь список.
У нас есть данные и пользовательский интерфейс(можете смеяться). Теперь нам нужна функция, которая отрендерит нам список.
function renderList(_list=[],el=document.body){
_list.forEach(i=>{
let new_el = document.createElement('li')
new_el.innerHTML=i
el.appendChild(new_el)
})
}
Получилось вот так. Функция принимает два аргумента — массив с элементами и элемент DOM-дерева, к который мы хотим нарисовать список. Если первый аргумент не задан, то мы говорим функции, что это пустой массив. Аналогично с элементом дерева.
Далее мы перебираем все элементы массива в цикле и для каждого элемента создаем узел DOM-дерева, добавляем внутрь этого узла сам элемент и добавляем его в конец нашего родительского элемента. В принципе ничего сложного.
На данном этапе код выглядит примерно так:
let list = ['JavaScript','Kotlin','Rust','PHP','Ruby','Java','MarkDown','Python','C++','Fortran','Assembler']
const result = document.getElementById('results')
renderList(list,result)
function renderList(_list=[],el=document.body){
_list.forEach(i=>{
let new_el = document.createElement('li')
new_el.innerHTML=i
el.appendChild(new_el)
})
}
А само приложения вот так:
Пора заняться самым интересным. Начинаем фильтровать. Нам нужна функция, которая на вход берет подстроку(запрос пользователя) и массив всех элементов, а на выходе выдает массив отфильтрованных элементов. Приступим. Самый очевидный вариант такой:
function filter(val,list){
let result=[];
list.forEach(i=>{
if(i.indexOf(val)!=-1)
result.push(i)
})
return result;
}
У нас есть черновой вариант. Теперь соединим все вместе, добавим обработчик и поехали. Код теперь такой:
let list = ['JavaScript','Kotlin','Rust','PHP','Ruby','Java','MarkDown','Python','C++','Fortran','Assembler']
const result = document.getElementById('results')
renderList(list,result)
function filter(val,list){
let result=[];
list.forEach(i=>{
if(i.indexOf(val)!=-1)
result.push(i)
})
return result;
}
function renderList(_list=[],el=document.body){
_list.forEach(i=>{
let new_el = document.createElement('li')
new_el.innerHTML=i
el.appendChild(new_el)
})
}
document.getElementById('search').addEventListener('input',e=>{
let new_arr = filter(e.target.value,list)
renderList(new_arr,result)
})
А браузер нам показывает вот это:
Упс. Забыли отчищать родительский элемент перед рендером. Добавим это в функцию рендера:
el.innerHTML='';
Теперь у нас все работает. Примерно также работают фильтры на вашем любимом фреймворке. Но мы же хорошие программисты и мы хотим сделать все красИвее, а может красивЕе. К счастью, я не филолог. Вернемся к нашей функции фильтра и попробуем ее переписать.
Для начала заменим if на тернарный оператор:
(i.indexOf(val)!=-1)?result.push(i):'';
Покопавшись в документации, я нашел, что можно юзать побитовое отрицание. Работает оно по формуле -(N+1). То есть при нашем значении -1 в indexOf мы получаем по формуле -(-1+1)=0. То есть это уже готовый false для нашего условия. Перепишем:
(~i.indexOf(val))?result.push(i):'';
А теперь десерт. forEach уже не модно. Есть же ES6. Там есть метод для перебора массива — filter. Он возвращает значение, если оно соответствует условию. Применим его:
function filter(val,list){
console.time('test')
return list.filter(i=>(~i.indexOf(val)))
};
Красиво, не так ли?
Теперь переделаем обработчик. Запишем все в одну стрелочную функцию. У меня получилось вот так:
document.getElementById('search').addEventListener('input',e=>renderList(filter(e.target.value,list),result))
Я надеюсь, что все понятно. Берем отфильтрованный массив и кидаем его как аргумент для рендера.
Захотел замерить время обработки. Использовал модные функции.
На этом я остановился. Полный и рабочий код получился такой:
let list = ['JavaScript','Kotlin','Rust','PHP','Ruby','Java','MarkDown','Python','C++','Fortran','Assembler']
const result = document.getElementById('results')
renderList(list,result)
function filter(val,list){
console.time('test')
return list.filter(i=>(~i.indexOf(val)))
};
function renderList(_list=[],el=document.body){
el.innerHTML='';
_list.forEach(i=>{
let new_el = document.createElement('li')
new_el.innerHTML=i
el.appendChild(new_el)
})
console.timeEnd('test')
}
document.getElementById('search').addEventListener('input',e=>renderList(filter(e.target.value,list),result))
Такое приложение получилось очень шустрым и нетрудным для понимания. Среднее время для таких данных — 0.3 миллисекунды. А размер после сжатия через babel дает всего 782 байта.
Рабочий пример можете найти по ссылке
Этой статьей я хотел лишь натолкнуть людей не забывать про чистый js. Не так все и плохо там. Пользуйтесь нативными функциями и используйте es6.
Спасибо Houston Теперь пример выглядит вот так и не зависит от регистра.
Спасибо за внимание.
Комментарии (55)
RidgeA
22.11.2017 11:08+1Array.prototype.filter — это ES5, а не ES6
Если говорить об ES6 то почему бы не использовать Array.prototype.includes вместо вот этого
~i.indexOf(val)
в
function filter(val,list){ console.time('test') return list.filter(i=>(~i.indexOf(val))) };
?
И красоты, тут, честно говоря, лично я не наблюдаю.Ankell Автор
22.11.2017 11:26Для начала можете статью прочитать. Насчёт filter вы правы. Действительно es5. Array.prototype.includes не годится. Я ищу подстроку в строке. Тогда уж String.prototype.includes. Как вы понимаете — логи в консоль только для проверки времени. Как говорят:«Обвиняешь — предлагай». Я не вижу от вас более изящного решения. Если оно заключается в замене index of, то я считаю это достаточно мизерным изменением
RidgeA
22.11.2017 11:44Консоль ни при чем, это понятно.
Вот тут, ИМХО, гораздо понятнее и нету сомнительных трюков с битовыми операторами:
function filter(val, list){ console.time('test') return list.filter( i => i.includes(val) ) };
Ankell Автор
22.11.2017 11:54Я считаю, как уже говорил, это минорным изменением и код изменение лучше не сделало. Спасибо
ihost
22.11.2017 13:13Если говорить об ES6 то почему бы не использовать Array.prototype.includes вместо вот этого
Безотносительно ко всей статье, но именно includes в некоторым смысле вредная штука — способ выкинуть поддержку как минимум IE11 и Samsung TV browser на пустом месте
Если синтаксические конструкции ES7+ легко раз-babel-иваются, а для ES6-функционала polyfills во всех современных браузерах не нужны и по умолчанию их давно уже никто не включает в bundle-ы, то includes — фактически единственное исключение и способ нажить неудобства при deployment-еjustboris
22.11.2017 18:10подключаем https://polyfill.io и проблема решена?
ihost
22.11.2017 20:11Или
babel-polyfill
, если зависимости собираютсяwebpack
-ом и поставляются на клиент в виде bundle-а? Окей, но скорее всего нет, поскольку есть один важный аспект — вопросы оптимизации, включая объем трафика для bundle-а и вычислительные ресурсы для выполнения на клиенте.
Применение единого bundle-а со всеми polyfills и transpile-кодом на современных браузерах приводит к существенному оверхэду, поэтому рекоммендуется разворачивать отдельный bundle для современных клиентских агентов (раз, два).
Условно все относительно-современные браузеры поддеривают ES6, и для них может приозводиться доставка непосредственно ES6-кода. Для старых браузеров можно применять polyfills, transpile-ить код, или вообще просить пользователя обновиться для просмотра ресурса — это вопрос отдельный.
Теперь если взять все браузеры с ES6, то часть из них, включая IE11, Smart TV, Tizen, не поддерживают ES7, но отлично понимают ES6. Если вспомнить соответствующую спецификацию комитета TC39, то 6-ая версия включает всего два изменения — оператор возведения в степень и includes. ВСЁ.
Исходя из этого вопрос — имеет ли смысл заморачиваться с отдельным bundle-ом с polyfills и transpile-ингом с уровня ES7 до ES6 (*), по сути ради одной единственной функции, которая чуть менее, чем на 100% — всего лишь синтаксический сахар для
arr.indexOf(value) > 0
? Настраивать развертывание, кеширование отдельного bundle-а, отдельные функциональные тесты на каком-нибудь SauseLabs, ради одной малополезной функции?
(*) При условии, что такое легко реализовать; babel по умолчанию, вроде бы, так не умеет, так что задача приобретает еще один виток сложности
justboris
22.11.2017 20:15Вы хотя бы немного мою ссылку читали? Сервер читает userAgent, и присылает полифиллы нужные этой версии браузера. Если браузер не может только Array.includes, то загрузится только полифилл для него.
ihost
22.11.2017 20:31Сервер читает userAgent
Производить проверку браузера, исходя из строки агента, не рекоммендуется — современным методом является feature discovery — к примеру, вариант для ES6, но это даже не столь существенно
Самое главное — такое решение плохо сочетается с самой идеей bundle-ирование клиентского кода. Конечно, можно загрузить предложенную библиотеку как статику и добавить в externals, но тогда bundle все равно или будет целиком содержать излишний исходный код, или требовать условной сборки, зависимой от клиентского агента.
justboris
22.11.2017 21:22Конечно, можно загрузить предложенную библиотеку как статику и добавить в externals
Вы точно разбираетесь, чем полифилл отличается от библиотеки?
Его не надо импортировать. Подключили скрипт на страницу, он добавил в нативные объекты недостающие методы, ваш код может спокойно к ним обращаться в любом браузере.
ihost
23.11.2017 10:53+1Подключили скрипт на страницу
Какой скрипт, на какую страницу. о чем Вы вообще? Как у Вас организован deployment, bundle-ирование и доставка кода на клиент?
Если Вы делаете transpile-инг на серверной стороне, то и все необходимые polyfills надо интегрировать сразу, чтобы сборщик зависимостей исключил дубликаты. Если не делаете — то код просто сломается на старых клиентских агентах из-за синтаксических ошибок, тогда и polyfills вообще не понадобится уже — и так страница не откроется.
Получается, что Вы предлагаете разрабатывать на синтаксисе ES5, но с функциями из ES7? Смысла мало, но если в Вашем проекте Вам там удобно — кто ж запретит, юзайте на здоровье
Вы точно разбираетесь, чем полифилл отличается от библиотеки?
Ничем не отличаются, что одно package, что другое package. Правда обычно polyfill package не является прямой prod-зависимостью других пакетов, а добавляется явно во входной точке конфигурации того же
webpack
-а или другого bundler-а, если Вам угодно.
Однако в таком случае встречный вопрос —
regenerator-runtime
это в Вашей терминологии polyfill или абстрактный library?
Его надо явно import-ить (Как видимо "библиотека" в Вашей терминологии), но при этом он модифицирует глобальное пространство процесса (Как видимо "полифилл" в вашей терминологии)
Впрочем, позволю резюмировать. Каждый deploy-ит как хочет, выбирает клиентские агенты, какие поддерживать, а какие нет, решает, оптимизировать производительность или сделать попроще, забив на это. Для каждого случая это можно решать отдельно. Однако если вспомнить, что все это ради одной функции, то лучше на нее забить.
justboris
23.11.2017 11:45Если бы вы все-таки сходили по ссылке, и прочитали что из себя представляет сервис polyfill.io, то большинство ваших вопросов отпали бы сами собой.
ihost
23.11.2017 17:25+3сервис polyfill.io
Сторонний сервис, который судя по обзору документации и единственному стороннему устаревшему пакету для bundle-ирования, не стыкуется с концепцией упаковки и доставки кода на клиент, не говоря уже о chunk splitting, feature detecting-based bundles, HMR и тому подобное.
Как это использовать с react-scripts, или уж тем более с resolve-scripts, с изоморфным кодом data layer-а, и чтобы при этом оставалась поддержка требуемых клиентских агентов без избычного кода — совершенно неясно, и вероятно, вообще невозможно.
Если Вы где-то используете и для Ваших задач подходит — на здоровье, никто не против. В серьезных же проектах лучше отказаться от одной малополезной функции, ради упрощения и стабилицаии продукта.
Посмотрите на тот же React (includes vs indexOf) — на клиентской стороне ни одногоincludes
, та парочка что есть — это серверный task runner.
Разумеется, на node.js в НЕ-изоморфном окружении можно использовать что угодно, хоть stage-0 со всякими флагами. На клиенте или изоморфном коде следует подходить аккуратнее.
justboris
24.11.2017 12:34Как это использовать с react-scripts, или уж тем более с resolve-scripts
- Удаляем
import "babel-polyfill"
, если он есть. - В html файле добавляем скрипт перед основным бандлом
<!-- полифиллы --> <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script> <!-- ваш бандл --> <script src="/static/js/bundle.js"></script>
Что здесь сложного?
ihost
24.11.2017 17:16+1react-scripts, или уж тем более с resolve-scripts
Что здесь сложного?
Тем что надо отдельно настраивать конфигурируемую
head
-секцию с внешнием сценарием через React Helmet ?
Тем что из-за синхронной загрузки скриптов увеличивается время загрузки страницы? А если CDN этого внешнего сервиса отвалится, то и web-приложение тоже? А также тем, что начинаются проблемы с offline-режимом, и к примеру, невозможно зарегистрировать service worken для удаленного СDN: https://filipbech.github.io/2017/02/service-worker-and-caching-from-other-origins ?
Тем что это может уже не сработать, если браузер поддержал ES modules-подобную загрузку, которая уже асинхронная, и если предполагался polyfill уровня ES8+, то он мог не успеть загрузиться?
Тем что нельзя установить жесткую CSP-политику для безопасности web-приложения, к примеру, отключающую eval для внешних сценариев или аналогичную?
В общем, можно дискутировать бесконечно. На каждое решение — свой потребитель. Как Вам уже не раз говорилось ранее — нравится — пользуйтесь на здоровье.
- Удаляем
justboris
24.11.2017 12:36В серьезных же проектах лучше отказаться от одной малополезной функции
includes
сам по себе не очень хороший пример, но о динамической генерации полифиллов полезно знать. Promise и fetch не завезли в IE11, и при помощи такого способа можно динамически подгрузить полифиллы, не нагружая пользователей Chrome бесполезным кодом.ihost
24.11.2017 16:52о динамической генерации полифиллов полезно знать
Безусловно. А еще лучше иметь заранее подготовленные bundle-ы для версия браузеров. Более того, есть решения, которые позволяет также включать сразу ES Modules для современных клинетских агентов, не выполняя дифференциации генерируемой HTML-страницы, а делая это исходя из feature discovery: статьи как настроить раз и два.
Promise и fetch не завезли в IE11
Разумеется. Для
fetch
-а в исходном коде вообще скорее всего не будет прямого вызова, а изоморфный импортimport fetch from 'isomorphic-fetch'
.
Это все довольно очевидные вещи. Весь тред собственно о том, если не использовать ES8+, то выбрасывание многострадального и довольно бесполезного
includes
позволяет сэкономить тонну работы по deployment-у.
Если вдруг ваше приложение использует ES8+, то и говорить не о чем. Собственно, как Вы сами частично и признаете:
includes сам по себе не очень хороший пример
А весь тред только об includes и есть.
pifpafka
22.11.2017 11:40Когда увидел заголовок «Глупый JS. Делаем фильтры «по красоте»», думал что сейчас будет что то интересное «по красоте»… Дочитал и чувство что кинули =( Я думаю все же если это фильтр, хоть он и типа «примитивный», но на пользовательский ввод в нижнем регистре он хотя бы должен корректный результат выдавать ИМХО. Удачи :)
Ankell Автор
22.11.2017 11:42Отчасти у каждого свое мнение. По моему скромному мнению учёт регистра — излишество. Делается это в пару строк. Акцент статьи — не на создание самого фильтра. Я призываю людей писать на чистом js-е) Хотя я не реформатор и не революционер. Просто такое мнение у студента)
Houston
22.11.2017 11:43На вкус и цвет, конечно.
const list = ['JavaScript', 'Kotlin', 'Rust', 'PHP', 'Ruby', 'Java', 'MarkDown', 'Python', 'C++', 'Fortran', 'Assembler']; const result = document.getElementById('results'); renderList(list, result); function filter(val, _list) { return _list.filter(it => it.indexOf(val) !== -1); } function renderList(_list = [], el = document.body) { el.innerHTML = _list.map(item => `<li>${item}/li>`); } document.getElementById('search').addEventListener('input', e => { console.time('test'); renderList(filter(e.target.value, list), result); console.timeEnd('test'); });
Ankell Автор
22.11.2017 11:51Спасибо) Почему-то поздним вечером не дошел до другого способа рендера) А мысли были рядом) Спасибо ещё раз) Я просто ещё учусь и не собирался учить других)
Slowz
22.11.2017 12:15Извините, но я кажется не понял посыла сего опуса. Что вы имеете в виду под словами «чистый JS»? Отсутствие фреймворков? Или ручное управление DOM?
Вы продемонстрировали решение очень простой задачи. Любой маломальски знакомый с веб-разработкой решил бы эту задачу примерно так же. Проблемы ведь появляются при масштабировании :) Чем больше проект, чем больше связанность различных элементов и чем больше интерактивности, тем сложнее становится управлять всем этим в ручную. Именно за этим и появились фреймворки)
Разумеется, для простых вещей, например лэндинг с каким-то уровнем интерактивности спокойно обойдется без сторонних библиотек. В таких задачах совершенно излишне подключать что-то мощное.
Вообще, конечно удачи в изучении языка. Быть увлеченным студентом прекрасно.
P.S. У вас что-то ужасное с форматированием кода. Лучше исправить.Ankell Автор
22.11.2017 12:36Все в совокупности, что ты назвали — для меня чистый js. Без бандлеров, без сборщиков и прочей лабуды. С вами согласен, но далеко не каждый «маломальский знакомый с разработкой» сделает это так. Вы можете говорить только за себя
iShatokhin
22.11.2017 15:33Из статьи.
Сейчас, да и в принципе давно уже, мало кто пишет на чистом js. Все хотят писать на современных фреймворках. Я не являюсь исключением и на работе в основном пишу на VueJS(2.0+) и стареньком AngularJS.
Это скорее относится не к JS, а к DOM API. Чистый JS не содержит в себе даже console, не говоря об document. Я, к примеру, не работаю с DOM/HTML, т.ч. как видите, не все хотят писать на модных фреймворках.
lair
22.11.2017 12:29Покопавшись в документации, я нашел, что можно юзать побитовое отрицание
Не надо использовать битовые операторы вместо логических. Просто не надо.
KirEv
22.11.2017 13:22Рабочий пример можете найти по ссылке
в хроме — работает, в ие8 — не работает, не сам код, а сам сервис =)
глупость вот в чем:
1. проверяйте существование элементов перед использованием, особенно когда
document.getElementById
2. если делаете фильтр, он как минимум должен фильтровать не учитывая регистр входных данных, вот хочу найти «Kotlin» или «JavaScript», не вводить же мне «JavaScript», строчу «javascript», «Javascript», «JavaSCript», etc…
3. не нужно «архивировать» логикуjustboris
22.11.2017 18:14Вы специально IE8 держите, чтобы молодых разработчиков троллить?
KirEv
22.11.2017 18:23почему сразу троллить? Вы не смотрите как то или иное выглядит в разных браузерах? ради интереса :)
win7 стоит чуть больше 2х лет, нет привычки накатывать свежие обновления от микрософт, да и по работе бывает нужно отдебажить что-то под ИЕ <9… у некоторых клиентов и того хуже (:…
PS: давным давно было ооочень много html-верстки, пришлось ставить зоопарк браузеров и их версий, потом пришли представители интернет-провайдера инет починить, мастер когда увидел количество установленных браузеров — смотрел на меня как на придурка )Zenitchik
22.11.2017 18:28Как я Вас понимаю. У меня на работе ровно та же картина с зоопарком браузеров.
kahi4
22.11.2017 13:35+1// list дальше по коду не меняется, должен быть const let list = ['JavaScript','Kotlin','Rust','PHP','Ruby','Java','MarkDown','Python','C++','Fortran','Assembler'] // красивиое и понятное название переменной, да const result = document.getElementById('results') // вызывать функцию до объявления конечно можно, но не нужно renderList(list,result) // зачем фильтр два раза объявлен? function filter(val,list){ let result; list.forEach(i=>{ if(i.indexOf(val)!=-1) result.push(i) }) return result; } // вообще сама конструкция array.filter подразумевает что у вас будет не обертка над ней // а где-то в коде list.filter(predicate); // а тут этот самый предикат объявлен function filter(val,list){ console.time('test') return list.filter(i=>(~i.indexOf(val))) }; function renderList(_list=[],el=document.body){ // Удаляйте элементы правильно, раз на то пошло. // https://jsperf.com/innerhtml-vs-removechild // В идеале даже делать дифф и не удалять не удалившиеся el.innerHTML=''; // то есть, то нет точки с запятой, уж определитесь _list.forEach(i=>{ // const опять же let new_el = document.createElement('li') new_el.innerHTML=i // а погуглить правильный путь не пробовали? el.appendChild(new_el) }) console.timeEnd('test') } // Как отписываться от этого события будете? // Бизнес-логика в хандлере. +1, мне нравится document.getElementById('search').addEventListener('input',e=>renderList(filter(e.target.value,list),result))
А еще почему используете стрелочные функции и let, но игнорируете классы?
Смешена логика и отображение, да вообще, "по красоте" как-то некрасиво получилось.
сжатия через babel
Ничего бейбель не жмет, он не для этого предназначен.
iShatokhin
22.11.2017 16:39Ничего бейбель не жмет, он не для этого предназначен.
Babel предназначен в том числе и для минификации кода
Rastishka
22.11.2017 18:32Написать что ли статью «Делаем сложение двух чисел по красоте».
Без фреймворков, наванильномчистом js.
JSmitty
22.11.2017 19:10-1Поучаствую в олимпиаде:
const list = ['JavaScript', 'Kotlin', 'Rust', 'PHP', 'Ruby', 'Java', 'MarkDown', 'Python', 'C++', 'Fortran', 'Assembler']; const result = document.getElementById('results'); const renderResult = list => renderList(list, result); const renderFilteredResult = val => renderResult(list.filter(i => (~i.indexOf(e.target.value)))); const searchHandler = ({target: {value}}) => renderFilteredResult(value); renderResult(list); function renderList(list = [], el = document.body) { el.innerHTML = ''; list.forEach(i => { let new_el = document.createElement('li'); new_el.innerHTML = i; el.appendChild(new_el); }) } document.getElementById('search').addEventListener('input', searchHandler);
i360u
22.11.2017 21:20Молодой человек, почитайте что-нибудь на тему соглашений о оформлении кода, ваши будущие коллеги будут вам благодарны.
Odrin
23.11.2017 10:27+1new_el.innerHTML=i
И если в данных будут теги, то все сломается. Использовать надо textContent.
Такое приложение получилось очень шустрым
Если уж вы так топите за производительность, то делать appendChild в цикле надо в DocumentFragment, а не в «живой» DOM элемент.
dopusteam
23.11.2017 10:52Не буду углубляться в подробности, но в большинстве случаев при грамотном построении приложения на фреймворке вы выиграете в скорости, поддерживаемости и читаемости.
Скорости разработки?
makcums
Эхх, о времена, о нравы… Начинали мы не с
console.log()
, а сdocument.write("Hello world!")
:)Ankell Автор
К сожалению(а может к счастью), у меня ещё нет бороды и я начинал с console.log) А вообще спасибо)
makcums
Да я же абсолютно не со зла. Сам вспомнил те времена, когда не было Реактов, ЕС6,7,8,...,2015 (да-да, несмешная шутка), но был ИЕ6.
П.С. у меня тоже нет бороды :(
Ankell Автор
Так и я не со зла) Просто появилось желание написать что-то на чистом js-е. И дабы пригубить это желание решил написать статью)
Zenitchik
5.5
У меня борода не растёт (зараза, и витамины не помогают).
Ankell Автор
Вот и я помню ie 5.5) На win 95 шла, вроде бы) Только я на 95-ке в mortal combat 4 играл в основном)Не было место кодингу
helgisbox
Мне показалось, что борода — это мода современной молодежи. А так по поводу статьи — очень хорошо, что на хабре есть такие. Иногда ликбез полезен. Старые доки из поисковиков исчезают (да и не только из поисковиков, хостинги закрываются), а новые — вот такие, иногда бывают полезны. Ну, когда нужно сверстать какой-нибудь отчетик, а копать мануалы лень. Хотя, лично меня, частенько за использование JS ругают. Но мне нравится ;)
pehat
Молодёжь… Только
alert()
, только хардкор!makcums
Пфф! Алерт для отладки!