Comments 64
Эхх, о времена, о нравы… Начинали мы не с console.log()
, а с document.write("Hello world!")
:)
П.С. у меня тоже нет бороды :(
ИЕ6.
5.5
У меня борода не растёт (зараза, и витамины не помогают).
Если говорить об ES6 то почему бы не использовать Array.prototype.includes вместо вот этого
~i.indexOf(val)
в
function filter(val,list){
console.time('test')
return list.filter(i=>(~i.indexOf(val)))
};
?
И красоты, тут, честно говоря, лично я не наблюдаю.
Вот тут, ИМХО, гораздо понятнее и нету сомнительных трюков с битовыми операторами:
function filter(val, list){
console.time('test')
return list.filter( i => i.includes(val) )
};
Если говорить об ES6 то почему бы не использовать Array.prototype.includes вместо вот этого
Безотносительно ко всей статье, но именно includes в некоторым смысле вредная штука — способ выкинуть поддержку как минимум IE11 и Samsung TV browser на пустом месте
Если синтаксические конструкции ES7+ легко раз-babel-иваются, а для ES6-функционала polyfills во всех современных браузерах не нужны и по умолчанию их давно уже никто не включает в bundle-ы, то includes — фактически единственное исключение и способ нажить неудобства при deployment-е
подключаем https://polyfill.io и проблема решена?
Или 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 по умолчанию, вроде бы, так не умеет, так что задача приобретает еще один виток сложности
Вы хотя бы немного мою ссылку читали? Сервер читает userAgent, и присылает полифиллы нужные этой версии браузера. Если браузер не может только Array.includes, то загрузится только полифилл для него.
Сервер читает userAgent
Производить проверку браузера, исходя из строки агента, не рекоммендуется — современным методом является feature discovery — к примеру, вариант для ES6, но это даже не столь существенно
Самое главное — такое решение плохо сочетается с самой идеей bundle-ирование клиентского кода. Конечно, можно загрузить предложенную библиотеку как статику и добавить в externals, но тогда bundle все равно или будет целиком содержать излишний исходный код, или требовать условной сборки, зависимой от клиентского агента.
Конечно, можно загрузить предложенную библиотеку как статику и добавить в externals
Вы точно разбираетесь, чем полифилл отличается от библиотеки?
Его не надо импортировать. Подключили скрипт на страницу, он добавил в нативные объекты недостающие методы, ваш код может спокойно к ним обращаться в любом браузере.
Подключили скрипт на страницу
Какой скрипт, на какую страницу. о чем Вы вообще? Как у Вас организован deployment, bundle-ирование и доставка кода на клиент?
Если Вы делаете transpile-инг на серверной стороне, то и все необходимые polyfills надо интегрировать сразу, чтобы сборщик зависимостей исключил дубликаты. Если не делаете — то код просто сломается на старых клиентских агентах из-за синтаксических ошибок, тогда и polyfills вообще не понадобится уже — и так страница не откроется.
Получается, что Вы предлагаете разрабатывать на синтаксисе ES5, но с функциями из ES7? Смысла мало, но если в Вашем проекте Вам там удобно — кто ж запретит, юзайте на здоровье
Вы точно разбираетесь, чем полифилл отличается от библиотеки?
Ничем не отличаются, что одно package, что другое package. Правда обычно polyfill package не является прямой prod-зависимостью других пакетов, а добавляется явно во входной точке конфигурации того же webpack
-а или другого bundler-а, если Вам угодно.
Однако в таком случае встречный вопрос — regenerator-runtime
это в Вашей терминологии polyfill или абстрактный library?
Его надо явно import-ить (Как видимо "библиотека" в Вашей терминологии), но при этом он модифицирует глобальное пространство процесса (Как видимо "полифилл" в вашей терминологии)
Впрочем, позволю резюмировать. Каждый deploy-ит как хочет, выбирает клиентские агенты, какие поддерживать, а какие нет, решает, оптимизировать производительность или сделать попроще, забив на это. Для каждого случая это можно решать отдельно. Однако если вспомнить, что все это ради одной функции, то лучше на нее забить.
Если бы вы все-таки сходили по ссылке, и прочитали что из себя представляет сервис polyfill.io, то большинство ваших вопросов отпали бы сами собой.
сервис 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 со всякими флагами. На клиенте или изоморфном коде следует подходить аккуратнее.
Как это использовать с 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>
Что здесь сложного?
react-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 для внешних сценариев или аналогичную?
В общем, можно дискутировать бесконечно. На каждое решение — свой потребитель. Как Вам уже не раз говорилось ранее — нравится — пользуйтесь на здоровье.
В серьезных же проектах лучше отказаться от одной малополезной функции
includes
сам по себе не очень хороший пример, но о динамической генерации полифиллов полезно знать. Promise и fetch не завезли в IE11, и при помощи такого способа можно динамически подгрузить полифиллы, не нагружая пользователей Chrome бесполезным кодом.
о динамической генерации полифиллов полезно знать
Безусловно. А еще лучше иметь заранее подготовленные bundle-ы для версия браузеров. Более того, есть решения, которые позволяет также включать сразу ES Modules для современных клинетских агентов, не выполняя дифференциации генерируемой HTML-страницы, а делая это исходя из feature discovery: статьи как настроить раз и два.
Promise и fetch не завезли в IE11
Разумеется. Для fetch
-а в исходном коде вообще скорее всего не будет прямого вызова, а изоморфный импорт import fetch from 'isomorphic-fetch'
.
Это все довольно очевидные вещи. Весь тред собственно о том, если не использовать ES8+, то выбрасывание многострадального и довольно бесполезного includes
позволяет сэкономить тонну работы по deployment-у.
Если вдруг ваше приложение использует ES8+, то и говорить не о чем. Собственно, как Вы сами частично и признаете:
includes сам по себе не очень хороший пример
А весь тред только об includes и есть.
На вкус и цвет, конечно.
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');
});
Вы продемонстрировали решение очень простой задачи. Любой маломальски знакомый с веб-разработкой решил бы эту задачу примерно так же. Проблемы ведь появляются при масштабировании :) Чем больше проект, чем больше связанность различных элементов и чем больше интерактивности, тем сложнее становится управлять всем этим в ручную. Именно за этим и появились фреймворки)
Разумеется, для простых вещей, например лэндинг с каким-то уровнем интерактивности спокойно обойдется без сторонних библиотек. В таких задачах совершенно излишне подключать что-то мощное.
Вообще, конечно удачи в изучении языка. Быть увлеченным студентом прекрасно.
P.S. У вас что-то ужасное с форматированием кода. Лучше исправить.
Сейчас, да и в принципе давно уже, мало кто пишет на чистом js. Все хотят писать на современных фреймворках. Я не являюсь исключением и на работе в основном пишу на VueJS(2.0+) и стареньком AngularJS.
Это скорее относится не к JS, а к DOM API. Чистый JS не содержит в себе даже console, не говоря об document. Я, к примеру, не работаю с DOM/HTML, т.ч. как видите, не все хотят писать на модных фреймворках.
Покопавшись в документации, я нашел, что можно юзать побитовое отрицание
Не надо использовать битовые операторы вместо логических. Просто не надо.
Рабочий пример можете найти по ссылке
в хроме — работает, в ие8 — не работает, не сам код, а сам сервис =)
глупость вот в чем:
1. проверяйте существование элементов перед использованием, особенно когда
document.getElementById
2. если делаете фильтр, он как минимум должен фильтровать не учитывая регистр входных данных, вот хочу найти «Kotlin» или «JavaScript», не вводить же мне «JavaScript», строчу «javascript», «Javascript», «JavaSCript», etc…
3. не нужно «архивировать» логику
Вы специально IE8 держите, чтобы молодых разработчиков троллить?
win7 стоит чуть больше 2х лет, нет привычки накатывать свежие обновления от микрософт, да и по работе бывает нужно отдебажить что-то под ИЕ <9… у некоторых клиентов и того хуже (:…
PS: давным давно было ооочень много html-верстки, пришлось ставить зоопарк браузеров и их версий, потом пришли представители интернет-провайдера инет починить, мастер когда увидел количество установленных браузеров — смотрел на меня как на придурка )
// 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
Ничего бейбель не жмет, он не для этого предназначен.
Ничего бейбель не жмет, он не для этого предназначен.
Babel предназначен в том числе и для минификации кода
Ребята, астанавитесь…
Без фреймворков, на
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);
Молодой человек, почитайте что-нибудь на тему соглашений о оформлении кода, ваши будущие коллеги будут вам благодарны.
new_el.innerHTML=iИ если в данных будут теги, то все сломается. Использовать надо textContent.
Такое приложение получилось очень шустрымЕсли уж вы так топите за производительность, то делать appendChild в цикле надо в DocumentFragment, а не в «живой» DOM элемент.
Не буду углубляться в подробности, но в большинстве случаев при грамотном построении приложения на фреймворке вы выиграете в скорости, поддерживаемости и читаемости.
Скорости разработки?
Я просто уточнить хотел, какую скорость Вы имеете в виду — скорость разработки или скорость работы приложения(производительность)
Мне кажется, что вопрос спорный.
Я имею в виду, что фреймворк (неважно какой, vju, angular, react) — это почти всегда замедление скорости работы, по сравнению с чистым js
В идеальном случае в ваккууме — ванильный JS будет быстрее, это да.
В реальном — если код получается большой, там будет легко забыть добавить где-нибудь проверку и вызвать лишний рендер, или подписаться на событие два раза вместо одного. И такая ошибка убьет всю пользу от микро-оптимизаций ванильного JS.
Во фреймворках такие проверки обычно встроены и страхуют вас от очевидных ошибок.
Глупый JS. Делаем фильтры «по красоте»