Kiểu chuỗi

Các chuỗi là các giá trị cơ sở trong JavaScript và không thể thay đổi. Các toán tử tác động lên chuỗi không thay đổi chuỗi mà luôn tạo ra và trả về một chuỗi mới dựa trên chuỗi hiện tại.

Cách viết chuỗi (literal chuỗi)

Các chuỗi được viết giữa hai dấu nháy đơn (single quote) hoặc hai dấu ngoặc kép (double quote):

const str1 = 'abc';
const str2 = "abc";
assert.equal(str1, str2);

Single quote thường được sử dụng nhiều hơn bởi nó phân biệt với HTML, nơi mà double quote được sử dụng.

Chuỗi thoát (Escaping)

Kí tự backslash ('\') giúp ta tạo ra một chuỗi thoát, thường dùng để chèn các kí tự đặc biệt vào chuỗi:

  • Kí tự xuống dòng trong Unix: \n
  • Kí tự xuống dòng trong Windows: \r\n
  • Tab: \t
  • Backslash: \\
  • Single quote: \' (được dùng để chèn single quote vào chuỗi nằm giữa hai kí tự double quote).
  • Double quote: \" (được dùng để chèn double quote vào chuỗi nằm giữa hai kí tự single quote).

Ví dụ:

assert.equal(
  'She said: "Let\'s go!"',
  "She said: \"Let's go!\""
);

Truy cập các kí tự và code point

Truy cập các kí tự

JavaScript không có kiểu kí tự – các kí tự luôn được biểu diễn như chuỗi. Trong JavaScript mỗi kí tự thực ra là một code unit.

const str = 'abc';

// Đọc kí tự ở vị trí cho trước
assert.equal(str[1], 'b');

// Đọc số kí tự trong chuỗi
assert.equal(str.length, 3);

Truy cập code point qua for-of và spreading

Khi bạn duyệt qua chuỗi bằng for-of hoặc sử dụng spreading (...), không phải từng kí tự mà là từng code point được duyệt qua. Mỗi code point được mã hóa bởi 1-2 kí tự JavaScript (mỗi kí tự thực ra là một code unit).

for (const ch of 'x🙂y') {
  console.log(ch);
}
// Output:
// 'x'
// '🙂'
// 'y'

Tương tự khi chuyển một chuỗi thành mảng các code point qua spreading:

assert.deepEqual([...'x🙂y'], ['x', '🙂', 'y']);

Nối chuỗi qua toán tử +

Chỉ cần có một toán hạng chuỗi, toán tử cộng + chuyển toán hạng còn lại (khi nó không phải chuỗi) thành chuỗi sau đó nối hai chuỗi lại để tạo nên chuỗi mới, cuối cùng trả về chuỗi mới:

assert.equal(3 + ' times ' + 4, '3 times 4');

Toán tử gán kết hợp += cũng hữu ích khi bạn tập hợp các chuỗi nhỏ lại với nhau:

let str = ''; // must be `let`!
str += 'Say it';
str += ' one more';
str += ' time';

assert.equal(str, 'Say it one more time');

Chuyển đổi chuỗi

Có ba cách để chuyển đổi giá trị x thành chuỗi:

  • String(x)
  • ''+x
  • x.toString() (không làm việc được với undefinednull)

Các ví dụ:

assert.equal(String(undefined), 'undefined');
assert.equal(String(null), 'null');

assert.equal(String(false), 'false');
assert.equal(String(true), 'true');

assert.equal(String(123.45), '123.45');

Đối với đối tượng thuần, kết quả chuyển đổi mặc định không thực sự hữu ích:

> String({a: 1})
'[object Object]'

Đối với mảng, kết quả trông tốt hơn, nhưng nhiều thông tin vẫn bị ẩn đi:

> String(['a', 'b'])
'a,b'
> String(['a', ['b']])
'a,b'

> String([1, 2])
'1,2'
> String(['1', '2'])
'1,2'

> String([true])
'true'
> String(['true'])
'true'
> String(true)
'true'

Đối với hàm, mã nguồn của nó được trả về:

> String(function f() {return 4})
'function f() {return 4}'

Bạn có thể thay đổi cách chuyển đổi mặc định đối tượng thành chuỗi bằng cách viết lại hàm phương thức toString():

const obj = {
  toString() {
    return 'hello';
  }
};

assert.equal(String(obj), 'hello');

Chuyển đổi giá trị thành chuỗi JSON

Định dạng JSON là một chuỗi biểu diễn các giá trị JavaScript. Ta chuyển một giá trị thành chuỗi JSON bằng JSON.stringify():

> JSON.stringify({a: 1})
'{"a":1}'
> JSON.stringify(['a', ['b']])
'["a",["b"]]'

Tuy nhiên, không phải mọi loại giá trị, JSON chỉ hỗ trợ null, các giá trị lôgic, các số, các chuỗi, các mảng và các đối tượng.

Tham số thứ ba, cho phép chuỗi JSON được viết trên nhiều dòng và chỉ định khoảng thụt lề:

console.log(JSON.stringify({first: 'Jane', last: 'Doe'}, null, 2));
/* Kết quả
{
  "first": "Jane",
  "last": "Doe"
}
*/

So sánh chuỗi

Chuỗi cũng có thể so sánh qua các toán tử sau:

< <= > >=

Nó so sánh dựa trên biểu diễn số của kí tự trong UTF-16:

> 'A' < 'B' // ok
true
> 'a' < 'B' // not ok
false
> 'ä' < 'b' // not ok
false

Làm việc với code unit và code point

Làm việc với code point

Ta có thể viết một kí tự sử dụng code point của nó bằng chuỗi thoát:

> '\u{1F642}'
'🙂'

Một cách khác là sử dụng phương thức String.fromCodePoint() chuyển một code point thành kí tự Unicode tương ứng (tương ứng với 1-2 kí tự JavaScript):

> String.fromCodePoint(0x1F642)
'🙂'

Phương thức .codePointAt() chuyển một hoặc hai kí tự JavaScript (một hoặc hai code unit) tại vị trí cho trước thành một code point:

> '🙂'.codePointAt(0).toString(16)
'1f642'

Khi duyệt chuỗi, mỗi code point (kí tự Unicode) được duyệt qua, chứ không phải mỗi code unit (kí tự JavaScript). Ví dụ sau sử dụng vòng lặp for-of:

const str = '🙂a';
assert.equal(str.length, 3);

for (const codePointChar of str) {
  console.log(codePointChar);
}

// Output:
// '🙂'
// 'a'

Spreading chuỗi thành một mảng cũng dựa trên duyệt từng kí tự Unicode:

> [...'🙂a']
[ '🙂', 'a' ]

Nó cung cấp cách thức tốt để đếm số kí tự Unicode (số code point) trong chuỗi:

> [...'🙂a'].length
2
> '🙂a'.length
3

Làm việc với code unit

JavaScript coi một code unit là một kí tự, để phân biệt với kí tự Unicode, ta gọi nó là kí tự JavaScript.

Để viết một kí tự JavaScript, ta cũng có thể sử dụng chuỗi thoát:

> '\uD83D\uDE42'
'🙂'

Chú ý khác với kí tự Unicode, viết kí tự JavaScript sử dụng chuỗi thoát không đặt mã kí tự trong { }.

Hoặc bạn có thể sử dụng phương thức String.fromCharCode(). Char code là cách mà JavaScript gọi code unit:

> String.fromCharCode(0xD83D) + String.fromCharCode(0xDE42)
'🙂'

Để lấy char code của một kí JavaScript ở vị trí cho trước, sử dụng .charCodeAt():

> '🙂'.charCodeAt(0).toString(16)
'd83d'

Tham khảo nhanh về chuỗi

Chuyển đổi về chuỗi

xString(x)
undefined'undefined'
null'null'
Giá trị lôgicfalse -> 'false', true -> 'true'
Số123 -> '123'
Chuỗikhông thay đổi
Đối tượngCấu hình qua toString()

Các toán tử

Truy cập từng kí tự JavaScript nhờ [ ]

const str = 'abc';
assert.equal(str[1], 'b');

Nối chuỗi nhờ toán tử +

assert.equal('a' + 'b' + 'c', 'abc');
assert.equal('take ' + 3 + ' oranges', 'take 3 oranges');

Các phương thức trong String.prototype

  • .endsWith(searchString: string, endPos=this.length): boolean: trả về true nếu chuỗi kết thúc bằng searchString. Vị trí kết thúc tìm kiếm là endPos. Nếu không trả về false:
> 'foo.txt'.endsWith('.txt')
true
> 'abcde'.endsWith('cd', 4)
true
  • .startsWith(searchString: string, startPos=0): boolean: trả về true nếu chuỗi bắt đầu bằng searchString. Vị trí bắt đầu tìm kiếm là startPos. Nếu không trả về false:
> '.gitignore'.startsWith('.')
true
> 'abcde'.startsWith('bc', 1)
true
  • .includes(searchString: string, startPos=0): boolean: trả về true nếu chuỗi chứa searchStringfalse nếu không. Vị trí bắt đầu tìm là startPos:
> 'abc'.includes('b')
true
> 'abc'.includes('b', 2)
false
  • .indexOf(searchString: string, minIndex=0): number: trả về chỉ số nhỏ nhất nơi searchString xuất hiện bên trong chuỗi, hoặc -1 nếu không. Chỉ số trả về phải lớn hơn hoặc bằng minIndex.
> 'abab'.indexOf('a')
0
> 'abab'.indexOf('a', 1)
2
> 'abab'.indexOf('c')
-1
  • .lastIndexOf(searchString: string, maxIndex=Infinity): number: trả về chỉ số lớn nhất nơi searchString xuất hiện bên trong chuỗi, hoặc -1 nếu không. Chỉ số trả về phải nhỏ hơn hoặc bằng maxIndex.
> 'abab'.lastIndexOf('ab', 2)
2
> 'abab'.lastIndexOf('ab', 1)
0
> 'abab'.lastIndexOf('ab')
2
  • .slice(start=0, end=this.length): string: trả về chuỗi con của chuỗi từ start đến (nhưng không gồm) end. Nếu chỉ số âm, nó được cộng thêm this.length (ví dụ -1 trở thành this.length-1):
> 'abc'.slice(1, 3)
'bc'
> 'abc'.slice(1)
'bc'
> 'abc'.slice(-2)
'bc'
  • .split(separator: string, limit?: number): string: chia chuỗi thành mảng chuỗi con – các chuỗi con này nằm giữa các separator. seperator có thể là chuỗi:
> 'a | b | c'.split('|')
[ 'a ', ' b ', ' c' ]
  • .concat(...strings: string[]): string: nối chuỗi với mảng các chuỗi ...strings:
> 'ab'.concat('cd', 'ef', 'gh')
'abcdefgh'
  • .repeat(count=0): string: lặp lại chuỗi count lần:
> '*'.repeat()
''
> '*'.repeat(3)
'***'
  • .replace(searchValue: string, replaceValue: string): string: thay thế searchValue với replaceValue:
> 'x.x.'.replace('.', '#')
'x#x.'
> 'x.x.'.replace(/./, '#')
'#.x.'
> 'x.x.'.replace(/./g, '#')
'####'
  • .toUpperCase(): chuyển thành chữ hoa
> '-a2b-'.toUpperCase()
'-A2B-'
> 'αβγ'.toUpperCase()
'ΑΒΓ'
  • .toLowerCase(): chuyển thành chữ thường
> '-A2B-'.toLowerCase()
'-a2b-'
> 'ΑΒΓ'.toLowerCase()
'αβγ'
  • .trim(): xóa bỏ khoảng trắng hai đầu
> '\r\n#\t  '.trim()
'#'
> '  abc  '.trim()
'abc'
  • .trimEnd(): xóa bỏ khoảng trắng ở cuối
> '  abc  '.trimEnd()
'  abc'
  • .trimStart(): xóa bỏ khoảng trắng ở đầu
> '  abc  '.trimStart()
'abc  '