Получить последний символ строки в текущем современном Javascript, что позволяет использовать астральные символы, такие как Emoji, которые используют суррогатные пары (две единицы кода)

Символы Unicode (кодовые точки), не входящие в базовую многоязычную плоскость (BMP), могут состоять из двух символов (единиц кода), называемых суррогатной парой.

«ab» — ​​это две кодовые единицы и две кодовые точки. (Таким образом, два символа и два символа.)

«a????» — это три кодовые единицы и две кодовые точки. (То есть три символа и два символа.)

Мой код не должен работать со старыми версиями JavaScript. ES6 или что-то более современное.

Как я могу получить доступ к последнему персонажу, независимо от того, является ли он астральным персонажем или нет?

Разделение строки на «все, кроме последнего символа» и «последний символ» также допустимо.


person hippietrail    schedule 11.07.2017    source источник
comment
@Andreas: О Боже, это и очень уродливо, и невероятно красиво одновременно.   -  person T.J. Crowder    schedule 11.07.2017
comment
@hippietrail: решение Андреаса работает, потому что итератор String перебирает кодовые точки, а не символы JavaScript (кодовые единицы), и поэтому он распределяет строку по кодовым точкам в массиве, а затем выбирает последнюю из массива через pop.   -  person T.J. Crowder    schedule 11.07.2017
comment
@Andreas: Вы действительно должны опубликовать это как ответ.   -  person T.J. Crowder    schedule 11.07.2017
comment
Потрясающий! Я был уверен, что будет по крайней мере один отличный способ добиться этого с помощью нового материала ES6. Пока я мог думать только о Array.from('a????') или 'a????'.match(/.$/u).   -  person hippietrail    schedule 11.07.2017
comment
@Andreas: ...раскладывать какашки? :D   -  person Amadan    schedule 11.07.2017
comment
@hippietrail: Мне было бы интересно увидеть сравнение производительности между подходом Андреаса к спредам и вашим подходом с регулярными выражениями. На самом деле, я был так заинтересован, что сделал один, и Рекс выиграл с большим отрывом на V8 в Chrome и много на SpiderMonkey в Firefox: jsperf.com/подходы к получению последней кодовой точки в строке Я рекомендую опубликовать его как ответ и (с извинениями перед Андреасом) принять его.   -  person T.J. Crowder    schedule 11.07.2017
comment
Вполне могут быть и другие подходы, о которых никто из нас не подумал. Если мой быстрее, я полагаю, я должен представить его как ответ в конце концов. Мое также легко распространить и на вторую часть моего вопроса.   -  person hippietrail    schedule 11.07.2017
comment
@hippietrail: Да. Фактически, вы можете оставить вопрос без принятого ответа в течение дня или около того... Как вы говорите, это не только быстрее, но, по крайней мере, так же элегантно и гибко.   -  person T.J. Crowder    schedule 11.07.2017
comment
Просто убедитесь, что вы действительно имеете в виду последний символ, а не что-то похожее на последний видимый символ, так как может быть много символов, изменяющих последний видимый символ, например. но не ограничиваясь селекторами вариантов для эмодзи. Также см. этот вопрос за неожиданные результаты по этому поводу.   -  person ASDFGerte    schedule 11.07.2017
comment
@ASDFGerte: Да, ради этого вопроса символ означает кодовую точку. Я также не забочусь о кластерах графем и т. Д. Все они находятся на более высоком уровне, а этот вопрос касается более низких уровней.   -  person hippietrail    schedule 11.07.2017


Ответы (2)


Распространение разделит строку на ее кодовые точки.

[...'a????'].pop()
person Andreas    schedule 11.07.2017

Из ответов на другие вопросы SO я знал, что и Array.from(), и регулярные выражения с флагом /u будут правильно обрабатывать символы Unicode, отличные от BMP, но я не думал, что это будет лучший ответ.

Возможно, я ошибался, поэтому вот два решения:

Array.from()

let c = Array.from('a????')[1];
console.log(c);

u флаг

let c ='a????'.match(/.$/u)[0];
console.log(c);

Этот второй подход можно расширить, чтобы ответить и на вторую часть моего вопроса:

let [,l,r] = 'abcd????'.match(/(.*)(.)/u);
console.log(l);
console.log(r);

(Якорь не нужен, так как .* будет жадным.)

person hippietrail    schedule 11.07.2017
comment
Array.from очень похож на Андреаса, оба используют итератор строк для получения массива кодовых точек, а затем берут последнюю запись из массива. (Ваше позволяет избежать последующего изменения массива, что способствует повышению производительности.) Однако регулярное выражение умнее, потому что оно максимально проникает внутрь движка JavaScript, где его можно оптимизировать. - person T.J. Crowder; 11.07.2017
comment
Я попытался добавить метод Array.from в jsperf, но он просто продолжает говорить мне, что проверьте обязательные поля и сохраните снова. и сайт очень медленный для меня здесь, в Лаосе прямо сейчас. \-: - person hippietrail; 11.07.2017
comment
Не только у вас, не только в Лаосе. jsPerf часто работает очень медленно или даже в автономном режиме. - person T.J. Crowder; 11.07.2017
comment
Regex выигрывает, Array.from — самый медленный. (Что неудивительно, это более сложный метод с дополнительной функцией сопоставления.) - person T.J. Crowder; 11.07.2017