Как стать автором
Обновить

Комментарии 30

А как быть, если мы используем browserify, webpack? Без полифилов для нодовых модулей, но с module.exports?
var isNode = typeof process != 'undefined' && {}.toString.call(process) == '[object process]';
На самом деле это неправильно) Нас не интересует нода это или нет, нас интересует, можем ли мы разобрать ссылку при помощи DOM.

Как-то так:
if (typeof document == 'object' && document.createElement) {
  url = document.createElement('a');
  url.href = str;
  return url;
} else {
  return url.parse(str);
}
На самом деле, нет никакой разницы, Node.js это или нет, так как browserify предоставляет браузерные версии стандартных модулей, в том числе и url, так что и смысла извращаться нет. Я привёл этот код только потому, как в статье предлагается 2 неверных способа определить, нода это или нет. Ваш вариант не более правильный, потому как в окружении без dom также может отсутствовать и модуль url.
Мой вариант может неправильный практически, но идеологически именно такой вариант и должен использоваться — хотите использовать сомнительную фичу, то проверьте её существование, а не платформу.
На ноде модуль url есть из коробки и определение платформы достаточный фичадетект для его использования без нежелательных последствий. В браузере же его подключение добавит в сборку лишний здоровый модуль (привет варианту из статьи). А результат и моего, и вашего вариантов один.
В статье показан workaround, как можно использовать функционал парсинга урлов, но этот функционал ни разу не является надежным. Т.к. на серверной стороне зависит от специфичного для Node.js окружения, а на клиентской стороне от специфичной фичи браузера. Такой функционал нельзя распространять в виде npm-модуля, в виде кода своего приложения сколько угодно.
Эмм… А какое отношение данный тред имеет к какому-бы ни было распространению данного велосипеда? :)
url = document.createElement('a');
url.href = str;
return url;
Это не очень хороший способ вытащить данные о URL. document.createElement — довольно медленная операция, и если вам надо распарсить 10000 ссылок в цикле, то это может ощутимо подвесить браузер.
Я понимаю, что это только пример, но кто-то может захотеть использовать его в реальной жизни. Нехорошо писать такие примеры.
Не так уж и медленно — на 100 тысяч фаерфокс (на amd fx 6100 3.3GHz) тратит меньше секунды (800 мс)
jsfiddle.net/fh21gu0a/

p.s. По мне так отличнейший способ не изобретать своего велосипеда. Мало того, если вдруг будут внесены какие то изменения в стандарт url, то браузер поддержку обновит, а вот ваш сайт лет через 5-10 никто обновлять не будет!
Можно еще попробовать создать элемент вне цикла.
Отличная идея, это ускорило код на треть, при этом примерно 80% времени в моем примере тратится на получение .hash.
В общем нет никаких тормозов.
А зачем вообще делать document.createElement в цикле?
Отвечу на это примером из жизни. Пару лет назад, когда я был моложе и зеленее, для работы с большими таблицами на клиенте я воспользовался одной библиотекой, название которой уже не помню. Но что мне запомнилось, так это то, что она начинала нещадно тормозить при выводе текста, которому надо было убирать тэги. Данных было много, порядка 5000 строк по 10 ячеек в строке, и это был единственный юзкейс при котором тормоза становились неприемлемыми.
Расчехлив профилировщик я обнаружил, что очистка от тэгов производилась как-то так
var span = document.createElement("span");
span.innerHTML = str;
var filtered_str = span.innerText;
Это и было самым ресурсоёмким местом. С тех пор я помню, что создание элементов DOM — не самая быстрая операция, можно и побыстрее.
Вы спрашиваете, зачем это делать в цикле. Незачем, если это пример. Но в реальной жизни кто-нибудь может засунуть это в функцию
Ну так вопрос как бы и намекал на то, что создавать элемент в цикле — плохая идея.
1) если вам нужно распарсить 100500 URL-адресов в браузере, то вам свинья не товарищ
2) никто для такой узкой задачи не запрещает сделать document.createElement лишь раз
Примерно раз в год, кто-то изобретает этот велосипед, и спешит поделиться с хабром.
Прошу прощения за, возможно, глупый вопрос, а что такое "#1:2"?
Как меня поправили ниже, '#' не возможен. Заменим его на другую строку:

ftp://user!&1:$&*;=3@[ff:fe::1]:3128/foo/bar?q#hash


Штука до '@' — это authority.
> user#1
В данном случае все что после # будет фрагментом, по rfc3986 символ # невозможен нигде кроме как во фрагменте.
authority = [ userinfo "@" ] host [ ":" port ]
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

Да, вы правы. Меняю задачу:

ftp://user!&1:$&*;=3@[ff:fe::1]:3128/foo/bar?q#hash
 ❯ node -p 'require("url").parse("ftp://user!&1:$&*;=3@[ff:fe::1]:3128/foo/bar?q#hash")'
{ protocol: 'ftp:',
  slashes: true,
  auth: 'user!&1:$&*;=3',
  host: '[ff:fe::1]:3128',
  port: '3128',
  hostname: 'ff:fe::1',
  hash: '#hash',
  search: '?q',
  query: 'q',
  pathname: '/foo/bar',
  path: '/foo/bar?q',
  href: 'ftp://user!%261:%24%26*%3B%3D3@[ff:fe::1]:3128/foo/bar?q#hash' }
Круто. Хотя точно должно быть 'ftp:', а не 'ftp'?
Встреченные мною парсеры урлов консистентно отдавали протокол с двоеточием в конце, и в Node.js, и в браузере.
github.com/petkaantonov/urlparser — вот кстати еще более быстрый парсер урлов, без регулярки. Сейчас это вроде висит как PR в io.js с тегом «на повестке дня».
К сожалению, изменять href у ссылки — не самый кроссбраузерный способ. В IE отсутствие 80 дефолтного для http порта трактуется иначе, чем в других браузерах. Вот тут чуть подробнее.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории