Pull to refresh

Считываем hash: пуленепробиваемый способ

Reading time 3 min
Views 27K
Original author: Lea Verou
Это, вероятно, одна из тех задач, о которой все думают, что знают решение, но многие решают её в итоге некорректно. Наткнувшись на ещё один сверхслабый кусок кода, написанный для этой цели, я задумала разъясняющую блогозапись.

Суть проблемы


Вы хотите убрать символ решётки (#) из значения location.hash. Например, когда hash равен "#foo", Вы хотите получить строку, содержащую "foo". Это же просто, правда?

Сложные случаи


Вот что большинство разработчиков, кажется, упускает из виду: в современных, мощно наджаваскриптованных, приложениях переменная hash может содержать любые юникодовые символы. Она не обязательно должна соответствовать значению реального атрибута id с той же страницы. А когда она и соответствует, атрибуты id теперь могут содержать почти любые юникодовые символы. Да ещё часто забывают, что на странице может и не быть никакого хэша. Даже если URL оканчивается символом «#», строка location.hash равняется на самом деле "" (пустой строке), а не "#".

Наивные подходы


Вот наиболее недавний — я нашла его в книге, на которую составляла техническую рецензию:

var hash = location.hash.match(/#(\w+)/)[1];

У него сразу несколько проблем:

  • Даёт неверный результат, когда hash содержит не латинский или не алфавитно-цифровой символ. Например, от хэша «#foo@o#bar$%huh hello» будет получено просто "foo".
     
  • Выбросит TypeError, если строка location.hash пуста — потому что .match() вернёт null.

Я видела другие варианты этого подхода, в том числе с употреблением явно заданных символьных классов вместо \w, с прибавлением начала строки (^) перед символом «#» (превосходная мысль, способствует производительности), и проверявшие, возвращает ли метод .match() что-нибудь, перед употреблением его результата. Однако же все они обычно были жертвою, по меньшей мере, одной из двух вышеупомянутых проблем.

Другой подход, который один мой друг использовал однажды, был вот каков:

var hash = location.hash.split('#')[1];

У этого подхода также есть свои проблемы — забавно, но их меньше, чем у предыдущего, хотя этот выглядит гораздо наивнее.

  • На том же тестовом примере будет получен хотя бы кусок "foo@o", что означает ошибку подхода только когда hash содержит символ «#».
     
  • Когда хэша нет, не выбрасывает ошибку, хотя всё же выдаёт undefined вместо пустой строки.

Получаем правильное значение


Приём, которым я обычно пользуюсь, куда проще каждого из двух вышеупомянутых и, возможно, выглядит слишком нестрогим:

var hash = location.hash.substring(1);

Однако же поглядите попристальнее:

  • От нашего заковыристого тестового хэша он выдаёт правильный результат: "foo@o#bar$%huh hello"
     
  • Когда хэша нет, он правильно возвращает пустую строку.

«Но ведь он предполагает, что в начале строки решётка!» — я прям слышу, как воскликнут некоторые из вас. Ну, это стало бы настоящей заботою, кабы мы обрабатывали произвольную строку. Тогда пришлось бы проверить, символ «#» ли в её начале, да и существует ли эта строка вообще. Однако в случае с location.hash это не так только тогда, когда хэша нет. А этот вариант здесь учтён. ;)

Дополнение: как указывают в комментариях, также можно использовать location.hash.slice(1) вместо substring. Мне это даже больше нравится, потому что на 4 байта короче.

Если, однако же, вы одержимы регэксами и желаете решить задачу с их помощью во что бы то ни стало, то вот столь же пуленепробиваемый и почти столь же краткий способ:

var hash = location.hash.replace(/^#/, '');

Если по какой-то причине (ОКР?) вам хочется решить задачу при помощи .match() во что бы то ни стало, вы можете сделать вот что:

var match = location.hash.match(/^#?(.*)$/)[1];

В этом случае, так как символ «#» необязателен, .match() никогда не возвратит null. И нет, символ «#» никогда ошибочно не окажется частью возвращаемого хэша: так работают движки регэксов.

«Это слишком просто, чего я тут время зря трачу!»


Ну извините. Я знаю: для некоторых из вас это элементарно. Но тот парень, который написал книгу — весьма знающий (и книга взаправду хороша, за исключением вышепроцитированного кода), так что я подумала: это означает, что многие неплохие разработчики неправильно подходят к этой задаче, вот почему понадобилось написать эту блогозапись. Если же вы не один из них — порадуйтесь этому.

«Эй, но и тут не всё в порядке!»


В этом случае хотелось бы знать, что я пропустила — так что, пожалуйста, оставьте комментарий! :)
Tags:
Hubs:
+12
Comments 26
Comments Comments 26

Articles