--- title: String.prototype.charAt() slug: Web/JavaScript/Reference/Global_Objects/String/charAt tags: - JavaScript - Method - Prototype - String - Довідка translation_of: Web/JavaScript/Reference/Global_Objects/String/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, що підтримує присвоєння {{jsxref("Operators/Деструктуризація", "деструктурованням")}}, можна трохи поліпшити легкочитність коду, повертаючи з функції також оновлене (якщо останній символ був складений) значення зсуву:
// Символи поза ОБП можна було б вжити безпосередньо 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; }
Специфікація | Статус | Коментар |
---|---|---|
{{SpecName('ES1')}} | {{Spec2('ES1')}} | Початкова виознака. |
{{SpecName('ES5.1', '#sec-15.5.4.4', 'String.prototype.charAt')}} | {{Spec2('ES5.1')}} | |
{{SpecName('ES6', '#sec-string.prototype.charat', 'String.prototype.charAt')}} | {{Spec2('ES6')}} | |
{{SpecName('ESDraft', '#sec-string.prototype.charat', 'String.prototype.charAt')}} | {{Spec2('ESDraft')}} |
{{Compat("javascript.builtins.String.charAt")}}