Это, вероятно, одна из тех задач, о которой все думают, что знают решение, но многие решают её в итоге некорректно. Наткнувшись на ещё один сверхслабый кусок кода, написанный для этой цели, я задумала разъясняющую блогозапись.
Вы хотите убрать символрешётки (#) из значения location.hash. Например, когда hash равен "#foo", Вы хотите получить строку, содержащую "foo". Это же просто, правда?
Вот что большинство разработчиков, кажется, упускает из виду: в современных, мощно наджаваскриптованных, приложениях переменная hash может содержать любые юникодовые символы. Она не обязательно должна соответствовать значению реальногоатрибута id с той же страницы. А когда она и соответствует, атрибуты id теперь могут содержать почти любые юникодовые символы. Да ещё часто забывают, что на странице может и не быть никакого хэша. Даже если URL оканчивается символом «#», строка location.hash равняется на самом деле "" (пустой строке), а не "#".
Вот наиболее недавний — я нашла его в книге, на которую составляла техническую рецензию:
У него сразу несколько проблем:
Я видела другие варианты этого подхода, в том числе с употреблением явно заданных символьных классоввместо \w, с прибавлением начала строки (^) перед символом «#» (превосходная мысль, способствует производительности), и проверявшие, возвращает ли метод .match() что-нибудь, перед употреблением его результата. Однако же все они обычно были жертвою, по меньшей мере, одной из двух вышеупомянутых проблем.
Другой подход, который один мой друг использовал однажды, был вот каков:
У этого подхода также есть свои проблемы — забавно, но их меньше, чем у предыдущего, хотя этот выглядит гораздо наивнее.
Приём, которым я обычно пользуюсь, куда проще каждого из двух вышеупомянутыхи, возможно, выглядит слишком нестрогим:
Однако же поглядите попристальнее:
«Но ведь он предполагает, что в начале строкирешётка!» — я прям слышу, как воскликнут некоторые из вас. Ну, это стало бы настоящей заботою, кабы мы обрабатывали произвольную строку. Тогда пришлось бы проверить, символ «#» ли в её начале, да и существует ли эта строка вообще. Однако в случае с location.hash это не так только тогда, когда хэша нет. А этот вариант здесь учтён.
Дополнение: как указывают в комментариях, также можно использовать location.hash.slice(1) вместоsubstring. Мне это даже больше нравится, потому что на 4 байта короче.
Если, однако же, вы одержимы регэксами и желаете решить задачу с их помощью во что бы то ни стало, то вот столь же пуленепробиваемый и почти столь же краткий способ:
Если по какой-то причине (ОКР?) вам хочется решить задачу при помощи .match() во что бы то ни стало, вы можете сделать вот что:
В этом случае, так каксимвол «#» необязателен, .match() никогда не возвратит null. И нет, символ «#» никогда ошибочно не окажется частью возвращаемого хэша: так работают движки регэксов.
Ну извините. Я знаю: для некоторых из вас это элементарно. Но тот парень, который написал книгу — весьма знающий (и книга взаправду хороша, за исключением вышепроцитированного кода), так что я подумала: это означает, что многие неплохие разработчики неправильно подходят к этой задаче, вот почему понадобилось написать эту блогозапись. Если же вы не один из них — порадуйтесь этому.
В этом случае хотелось бы знать, что я пропустила — так что, пожалуйста, оставьтекомментарий!
Суть проблемы
Вы хотите убрать символ
Сложные случаи
Вот что большинство разработчиков, кажется, упускает из виду: в современных, мощно наджаваскриптованных, приложениях переменная hash может содержать любые юникодовые символы. Она не обязательно должна соответствовать значению реального
Наивные подходы
Вот наиболее недавний — я нашла его в книге, на которую составляла техническую рецензию:
var hash = location.hash.match(/#(\w+)/)[1];
У него сразу несколько проблем:
- Даёт неверный результат, когда hash содержит не латинский или не алфавитно-цифровой символ. Например, от хэша
«#foo@o#bar$%huh hello» будет полученопросто "foo".
- Выбросит TypeError, если строка location.hash пуста — потому что .match() вернёт null.
Я видела другие варианты этого подхода, в том числе с употреблением явно заданных символьных классов
Другой подход, который один мой друг использовал однажды, был вот каков:
var hash = location.hash.split('#')[1];
У этого подхода также есть свои проблемы — забавно, но их меньше, чем у предыдущего, хотя этот выглядит гораздо наивнее.
- На том же тестовом примере будет получен хотя бы кусок
что означает ошибку подхода только когда hash содержит"foo@o"
,символ «#».
- Когда хэша нет, не выбрасывает ошибку, хотя всё же выдаёт undefined вместо пустой строки.
Получаем правильное значение
Приём, которым я обычно пользуюсь, куда проще каждого из двух вышеупомянутых
var hash = location.hash.substring(1);
Однако же поглядите попристальнее:
- От нашего заковыристого тестового хэша он выдаёт правильный результат: "foo@o#bar$%huh hello"
- Когда хэша нет, он правильно возвращает пустую строку.
«Но ведь он предполагает, что в начале строки
Дополнение: как указывают в комментариях, также можно использовать location.hash.slice(1) вместо
Если, однако же, вы одержимы регэксами и желаете решить задачу с их помощью во что бы то ни стало, то вот столь же пуленепробиваемый и почти столь же краткий способ:
var hash = location.hash.replace(/^#/, '');
Если по какой-то причине (ОКР?) вам хочется решить задачу при помощи .match() во что бы то ни стало, вы можете сделать вот что:
var match = location.hash.match(/^#?(.*)$/)[1];
В этом случае, так как
«Это слишком просто, чего я тут время зря трачу!»
Ну извините. Я знаю: для некоторых из вас это элементарно. Но тот парень, который написал книгу — весьма знающий (и книга взаправду хороша, за исключением вышепроцитированного кода), так что я подумала: это означает, что многие неплохие разработчики неправильно подходят к этой задаче, вот почему понадобилось написать эту блогозапись. Если же вы не один из них — порадуйтесь этому.
«Эй, но и тут не всё в порядке!»
В этом случае хотелось бы знать, что я пропустила — так что, пожалуйста, оставьте