String.prototype.charAt()
Метод charAt()
створює і вертає підрядок, що міститиме лише один символ (кодова одиниця UTF-16), розташований у рядку із зазначеним зсувом.
Синтаксис
str.charAt(index)
Параметри
index
- Індекс символа у рядку, ціле число від
0
доstr.length - 1
. Якщо не зазначено (метод викликано без аргументів), метод повертає перший символ рядка.
Вертає
Підрядок з одного символа (одна кодова одиниця UTF-16) отриманого за вказаним індексом, або порожній рядок, якщо index
вказує за межі рядка (менше 0
чи понад str.length - 1
).
Опис
Кожен символ рядка має індекс, що зростає зліва направо. Лік починається від нуля, тож перший символ має індекс 0
, а останній — str.length - 1
. Якщо зазначено індекс, що за ці межі виходить, метод chartAt()
вертає порожній рядок.
Якщо індекс не зазначено для метода charAt()
, буде задіяно типове значення 0
.
Приклади
Виведення різних символів рядка
Цей приклад дістає та виводить до консолі різні символи рядка «Хай йому грець»
:
var str = 'Хай йому грець';
// Індекс не зазначено, буде неявно задіяно значення 0
console.log(str.charAt()); // виводить "Х"
console.log(str.charAt(0)); // виводить "Х"
console.log(str.charAt(1)); // виводить "а"
console.log(str.charAt(2)); // виводить "й"
console.log(str.charAt(-1)); // виводить ""
console.log(str.charAt(99)); // виводить ""
Отримання цілого символа
Позаяк деякі символи в UTF-16 подаються двома кодовими одиницями, слід зважати на те, що метод charAt()
дістає їх з рядка нарізно, а отже задля отримання цілого символа доведеться їх об'єднати.
Наведений нижче код призначено для послідовної обробки рядків, що можуть містити такі складені символи (не належать до Основної Багатомовної Площини (ОБП) Unicode):
// Символи поза ОБП можна було б вжити безпосередньо
var str = 'A \uD87E\uDC04 Z';
for (var i = 0, chr; i < str.length; i++) {
// Просто додайте цю перевірку на початку кожного циклу з перебору символів
// і завжди матимете складені символи повністю, а не половини складеного
// символа нарізно.
if ((chr = getWholeChar(str, i)) === false) {
continue;
}
console.log(chr);
}
function getWholeChar(str, i) {
var code = str.charCodeAt(i);
// Значення зсуву «i» за межами рядка
if (Number.isNaN(code)) {
return '';
}
if (code < 0xD800 || code > 0xDFFF) {
return str.charAt(i);
}
// Старша половина (можна замінити друге значення на 0xDB7F й тлумачити
// «старші половини приватного вжитку» як окремі символи).
if (0xD800 <= code && code <= 0xDBFF) {
if (str.length <= (i + 1)) {
throw 'High surrogate without following low surrogate';
}
var next = str.charCodeAt(i + 1);
if (0xDC00 > next || next > 0xDFFF) {
throw 'High surrogate without following low surrogate';
}
return str.charAt(i) + str.charAt(i + 1);
}
// Молодша половина (0xDC00 <= code && code <= 0xDFFF)
if (i === 0) {
throw 'Low surrogate without preceding high surrogate';
}
var prev = str.charCodeAt(i - 1);
// Можна замінити друге значення на 0xDB7F й тлумачити
// «старші половини приватного вжитку» як окремі символи.
if (0xD800 > prev || prev > 0xDBFF) {
throw 'Low surrogate without preceding high surrogate';
}
// Молодшу половину було оброблено разом із старшою, тож тепер
// ми її пропускаємо.
return false;
}
У середовищі ECMAScript 2016, що підтримує присвоєння деструктурованням
, можна трохи поліпшити легкочитність коду, повертаючи з функції також оновлене (якщо останній символ був складений) значення зсуву:
// Символи поза ОБП можна було б вжити безпосередньо
var str = 'A\uD87E\uDC04Z';
for (var i = 0, chr; i < str.length; i++) {
[chr, i] = getWholeCharAndI(str, i);
// Просто додайте цей виклик на початку кожного циклу з перебору символів
// і завжди матимете складені символи повністю, а не половини складеного
// символа нарізно.
// Значення «i» буде оновлено, якщо метод натрапить на складений символ.
console.log(chr);
}
function getWholeCharAndI(str, i) {
var code = str.charCodeAt(i);
// Значення зсуву «i» за межами рядка
if (Number.isNaN(code)) {
return ['', i];
}
if (code < 0xD800 || code > 0xDFFF) {
/ / Звичайний символ, просто лишаємо все як є.
return [str.charAt(i), i];
}
// Старша половина (можна замінити друге значення на 0xDB7F й тлумачити
// «старші половини приватного вжитку» як окремі символи).
if (0xD800 <= code && code <= 0xDBFF) {
if (str.length <= (i + 1)) {
throw 'High surrogate without following low surrogate';
}
var next = str.charCodeAt(i + 1);
if (0xDC00 > next || next > 0xDFFF) {
throw 'High surrogate without following low surrogate';
}
// Зібрати складений символ докупи й повернути збільшений зсув
return [str.charAt(i) + str.charAt(i + 1), i + 1];
}
// Low surrogate (0xDC00 <= code && code <= 0xDFFF)
if (i === 0) {
throw 'Low surrogate without preceding high surrogate';
}
var prev = str.charCodeAt(i - 1);
// Можна замінити друге значення на 0xDB7F й тлумачити
// «старші половини приватного вжитку» як окремі символи.
if (0xD800 > prev || prev > 0xDBFF) {
throw 'Low surrogate without preceding high surrogate';
}
// Повернути натомість наступний символ й повернути збільшений зсув
return [str.charAt(i + 1), i + 1];
}
Також можна навести більш витончене рішення, хоча дещо менш гнучке:
// Просто перебираємо символи рядка за допомогою forEachChar()
forEachChar('A\uD87E\uDC04Z', function(c) {
console.log(c);
});
function forEachChar(string, predicate) {
for (var i = 0; i < string.length; i++) {
var code = string.charCodeAt(i);
var value;
// Звичайний символ, просто лишаємо як є.
if (code < 0xD800 || code > 0xDFFF) {
value = string.charAt(i);
} else {
// Старша половина (можна замінити друге значення на 0xDB7F й тлумачити
// «старші половини приватного вжитку» як окремі символи).
if (0xD800 <= code && code <= 0xDBFF) {
if (string.length <= (i + 1)) {
throw 'High surrogate without following low surrogate';
}
var next = string.charCodeAt(i + 1);
if (0xDC00 > next || next > 0xDFFF) {
throw 'High surrogate without following low surrogate';
}
value = string.charAt(i) + string.charAt(i + 1);
i++;
} else {
// Молодша половина (0xDC00 <= code && code <= 0xDFFF)
throw 'Low surrogate without preceding high surrogate';
}
}
// Перебір можна перервати, повернувши з функції-присудка значення false
if (false === predicate.call(string, value)) {
return;
}
}
}
Виправлений charAt()
з урахуванням складених символів
Приклад нижче наводить функцію fixedCharAt()
, яка не лише злучає половинки складених символів, а ще й змінює індексацію символів таким чином, що index
позначає порядковий номер (лік від нуля, як завжди) не кодової одиниці (як для звичайного charAt()
), а саме повного символа.
Втім, слід зважати, що це рішення є вкрай неоптимальним, якщо користувати його для перебору всього рядка:
function fixedCharAt(string, index) {
var isExpectingLowSurrogate = false;
var charIndex = 0;
var i = 0;
// За межами рядка.
if (index < 0 || index >= string.length) {
return '';
}
while (i < string.length && charIndex < index) {
if (isHighSurrogateAt(string, i) && isLowSurrogateAt(string, i + 1)) {
i++;
}
i++;
charIndex++;
}
if (i < string.length) {
if (isHighSurrogateAt(string, i) && isLowSurrogateAt(string, i + 1)) {
return string.charAt(i) + string.charAt(i + 1);
} else {
return string.charAt(i);
}
}
return '';
}
function isHighSurrogateAt(string, index) {
var code = string.charCodeAt(index);
return 0xD800 <= code && code <= 0xDBFF;
}
function isLowSurrogateAt(string, index) {
var code = string.charCodeAt(index);
return 0xDC00 <= code && code <= 0xDFFF;
}
Специфікації
Специфікація | Статус | Коментар |
---|---|---|
ECMAScript 1st Edition (ECMA-262) | Standard | Початкова виознака. |
ECMAScript 5.1 (ECMA-262) The definition of 'String.prototype.charAt' in that specification. |
Standard | |
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'String.prototype.charAt' in that specification. |
Standard | |
ECMAScript (ECMA-262) The definition of 'String.prototype.charAt' in that specification. |
Living Standard |
Підтримка веб-переглядачами
BCD tables only load in the browser