Thứ tư, 12/02/2020 | 00:00 GMT+7

Hiểu bản đồ và thiết lập đối tượng trong JavaScript


Tác giả đã chọn Open Internet / Free Speech Fund để nhận khoản đóng góp như một phần của chương trình Viết cho DO donate .

Trong JavaScript, các nhà phát triển thường dành nhiều thời gian để quyết định cấu trúc dữ liệu chính xác để sử dụng. Điều này là do việc chọn đúng cấu trúc dữ liệu có thể giúp bạn dễ dàng thao tác dữ liệu đó hơn sau này, tiết kiệm thời gian và làm cho mã dễ hiểu hơn. Hai cấu trúc dữ liệu chủ yếu để lưu trữ tập hợp dữ liệu là Đối tượngMảng (một kiểu đối tượng). Các nhà phát triển sử dụng Đối tượng để lưu trữ các cặp khóa / giá trị và Mảng để lưu trữ danh sách được lập index . Tuy nhiên, để cung cấp cho các nhà phát triển sự linh hoạt hơn, đặc tả ECMAScript 2015 đã giới thiệu hai loại đối tượng có thể lặp lại mới: Bản đồ , là tập hợp có thứ tự của các cặp khóa / giá trị và Bộ , là tập hợp các giá trị duy nhất.

Trong bài viết này, bạn sẽ xem qua các đối tượng Bản đồ và Tập hợp, điều gì làm cho chúng giống hoặc khác với Đối tượng và Mảng, các thuộc tính và phương thức có sẵn cho chúng, và ví dụ về một số cách sử dụng thực tế.

Bản đồ

Bản đồ là một tập hợp các cặp khóa / giá trị có thể sử dụng bất kỳ kiểu dữ liệu nào làm khóa và có thể duy trì thứ tự các mục nhập của nó. Bản đồ có các phần tử của cả Đối tượng (tập hợp cặp khóa / giá trị duy nhất) và Mảng (tập hợp có thứ tự), nhưng giống với Đối tượng hơn về mặt khái niệm. Điều này là do, mặc dù kích thước và thứ tự của các mục nhập được giữ nguyên như Mảng, nhưng bản thân các mục nhập là các cặp khóa / giá trị giống như Đối tượng.

Bản đồ có thể được khởi tạo bằng cú pháp new Map() :

const map = new Map()

Điều này cho ta một Bản đồ trống:

Output
Map(0) {}

Thêm giá trị vào bản đồ

Bạn có thể thêm giá trị vào bản đồ bằng phương thức set() . Đối số đầu tiên sẽ là khóa và đối số thứ hai sẽ là giá trị.

Phần sau thêm ba cặp khóa / giá trị vào map :

map.set('firstName', 'Luke')
map.set('lastName', 'Skywalker')
map.set('occupation', 'Jedi Knight')

Ở đây ta bắt đầu xem cách Maps có các phần tử của cả Đối tượng và Mảng. Giống như Mảng, ta có một bộ sưu tập được lập index bằng 0 và ta cũng có thể xem có bao nhiêu mục trong Bản đồ theo mặc định. Bản đồ sử dụng cú pháp => để biểu thị các cặp khóa / giá trị là key => value :

Output
Map(3) 0: {"firstName" => "Luke"} 1: {"lastName" => "Skywalker"} 2: {"occupation" => "Jedi Knight"}

Ví dụ này trông tương tự như một đối tượng thông thường với các khóa dựa trên chuỗi, nhưng ta có thể sử dụng bất kỳ loại dữ liệu nào làm khóa với Maps.

Ngoài việc cài đặt thủ công các giá trị trên Bản đồ, ta cũng có thể khởi tạo Bản đồ với các giá trị đã có. Ta thực hiện việc này bằng cách sử dụng Mảng Mảng chứa hai phần tử là mỗi cặp khóa / giá trị, trông giống như sau:

[ [ 'key1', 'value1'], ['key2', 'value2'] ]

Sử dụng cú pháp sau, ta có thể tạo lại cùng một Bản đồ:

const map = new Map([
  ['firstName', 'Luke'],
  ['lastName', 'Skywalker'],
  ['occupation', 'Jedi Knight'],
])

Lưu ý: Ví dụ này sử dụng dấu phẩy ở cuối , còn gọi là dấu phẩy treo . Đây là một thực tiễn định dạng JavaScript trong đó mục cuối cùng trong chuỗi khi khai báo một tập hợp dữ liệu có dấu phẩy ở cuối. Mặc dù lựa chọn định dạng này được dùng để tạo ra sự khác biệt rõ ràng hơn và thao tác mã dễ dàng hơn, việc sử dụng nó hay không là một vấn đề tùy chọn. Để biết thêm thông tin về dấu phẩy cuối, hãy xem bài viết Dấu phẩy cuối này từ tài liệu web MDN.

Ngẫu nhiên, cú pháp này giống như kết quả của việc gọi Object.entries() trên một Đối tượng. Điều này cung cấp một cách sẵn sàng để chuyển đổi Đối tượng thành Bản đồ, như trong khối mã sau:

const luke = {
  firstName: 'Luke',
  lastName: 'Skywalker',
  occupation: 'Jedi Knight',
}

const map = new Map(Object.entries(luke))

Ngoài ra, bạn có thể biến Bản đồ trở lại thành Đối tượng hoặc Mảng bằng một dòng mã.

Điều sau đây chuyển đổi một Bản đồ thành một Đối tượng:

const obj = Object.fromEntries(map)

Điều này sẽ dẫn đến giá trị sau đây của obj :

Output
{firstName: "Luke", lastName: "Skywalker", occupation: "Jedi Knight"}

Bây giờ, hãy chuyển đổi một Bản đồ thành một Mảng:

const arr = Array.from(map)

Điều này sẽ dẫn đến Mảng sau cho arr :

Output
[ ['firstName', 'Luke'], ['lastName', 'Skywalker'], ['occupation', 'Jedi Knight'] ]

Phím bản đồ

Bản đồ chấp nhận bất kỳ kiểu dữ liệu nào làm khóa và không cho phép các giá trị khóa trùng lặp. Ta có thể chứng minh điều này bằng cách tạo một bản đồ và sử dụng các giá trị không phải chuỗi làm khóa, cũng như đặt hai giá trị cho cùng một khóa.

Đầu tiên, hãy khởi tạo một bản đồ bằng các khóa không phải chuỗi:

const map = new Map()

map.set('1', 'String one')
map.set(1, 'This will be overwritten')
map.set(1, 'Number one')
map.set(true, 'A Boolean')

Ví dụ này sẽ overrides khóa đầu tiên của 1 với khóa tiếp theo và nó sẽ coi '1' là chuỗi và 1 số là các khóa duy nhất:

Output
0: {"1" => "String one"} 1: {1 => "Number one"} 2: {true => "A Boolean"}

Mặc dù người ta thường tin rằng một Đối tượng JavaScript thông thường đã có thể xử lý Numbers, boolean và các kiểu dữ liệu nguyên thủy khác dưới dạng khóa, nhưng thực tế không phải vậy, vì Đối tượng thay đổi tất cả các khóa thành chuỗi.

Ví dụ: khởi tạo một đối tượng bằng khóa số và so sánh giá trị cho khóa số 1 và khóa "1" xâu chuỗi:

// Initialize an object with a numerical key
const obj = { 1: 'One' }

// The key is actually a string
obj[1] === obj['1']  // true

Đây là lý do tại sao nếu bạn cố gắng sử dụng một Đối tượng làm khóa, nó sẽ in ra object Object chuỗi object Object thay thế.

Ví dụ: tạo một Đối tượng và sau đó sử dụng nó làm khóa của một Đối tượng khác:

// Create an object
const objAsKey = { foo: 'bar' }

// Use this object as the key of another object
const obj = {
  [objAsKey]: 'What will happen?'
} 

Điều này sẽ mang lại những điều sau:

Output
{[object Object]: "What will happen?"}

Đây không phải là trường hợp của Bản đồ. Hãy thử tạo một Đối tượng và đặt nó làm khóa của Bản đồ:

// Create an object
const objAsKey = { foo: 'bar' }

const map = new Map()

// Set this object as the key of a Map
map.set(objAsKey, 'What will happen?')

key của phần tử Bản đồ bây giờ là đối tượng ta đã tạo.

Output
key: {foo: "bar"} value: "What will happen?"

Có một điều quan trọng cần lưu ý về việc sử dụng Đối tượng hoặc Mảng làm khóa: Bản đồ đang sử dụng tham chiếu đến Đối tượng để so sánh bình đẳng, không phải giá trị theo nghĩa đen của Đối tượng. Trong JavaScript, {} === {} trả về false , vì hai Đối tượng không phải là hai Đối tượng giống nhau, mặc dù có cùng giá trị (trống).

Điều đó nghĩa là việc thêm hai Đối tượng duy nhất có cùng giá trị sẽ tạo một Bản đồ có hai mục nhập:

// Add two unique but similar objects as keys to a Map
map.set({}, 'One')
map.set({}, 'Two')

Điều này sẽ mang lại những điều sau:

Output
Map(2) {{…} => "One", {…} => "Two"}

Nhưng việc sử dụng cùng một tham chiếu Đối tượng hai lần sẽ tạo một Bản đồ với một mục nhập.

// Add the same exact object twice as keys to a Map
const obj = {}

map.set(obj, 'One')
map.set(obj, 'Two')

Điều này sẽ dẫn đến những điều sau:

Output
Map(1) {{…} => "Two"}

Tập hợp thứ hai set() đang cập nhật cùng một khóa chính xác với tập hợp đầu tiên, vì vậy ta kết thúc với một Bản đồ chỉ có một giá trị.

Lấy và xóa các mục khỏi bản đồ

Một trong những nhược điểm khi làm việc với Đối tượng là có thể khó liệt kê chúng hoặc làm việc với tất cả các khóa hoặc giá trị. Ngược lại, cấu trúc Bản đồ có rất nhiều thuộc tính tích hợp giúp làm việc với các phần tử của chúng trực tiếp hơn.

Ta có thể khởi tạo một Bản đồ mới để chứng minh các phương thức và thuộc tính sau: delete() , has() , get()size .

// Initialize a new Map
const map = new Map([
  ['animal', 'otter'],
  ['shape', 'triangle'],
  ['city', 'New York'],
  ['country', 'Bulgaria'],
])

Sử dụng phương thức has() để kiểm tra sự tồn tại của một mục trong bản đồ. has() sẽ trả về một Boolean.

// Check if a key exists in a Map
map.has('shark') // false
map.has('country') // true

Sử dụng phương thức get() để truy xuất một giá trị theo khóa.

// Get an item from a Map
map.get('animal') // "otter"

Một lợi ích cụ thể của Bản đồ so với Đối tượng là bạn có thể tìm thấy kích thước của Bản đồ bất kỳ lúc nào, giống như bạn có thể làm với Mảng. Bạn có thể nhận được số lượng các mục trong Bản đồ với thuộc tính size . Điều này bao gồm ít bước hơn so với việc chuyển đổi một Đối tượng thành một Mảng để tìm độ dài.

// Get the count of items in a Map
map.size // 4

Sử dụng phương thức delete() để xóa một mục khỏi Bản đồ theo từng khóa. Phương thức này sẽ trả về Boolean— true nếu một mục tồn tại và đã bị xóa, và false nếu nó không trùng với bất kỳ mục nào.

// Delete an item from a Map by key
map.delete('city') // true

Điều này sẽ dẫn đến Bản đồ sau:

Output
Map(3) {"animal" => "otter", "shape" => "triangle", "country" => "Bulgaria"}

Cuối cùng, một Bản đồ có thể được xóa tất cả các giá trị bằng map.clear() .

// Empty a Map
map.clear()

Điều này sẽ mang lại:

Output
Map(0) {}

Khóa, Giá trị và Mục nhập cho Bản đồ

Các đối tượng có thể truy xuất các khóa, giá trị và mục nhập bằng cách sử dụng các thuộc tính của hàm tạo Object . Mặt khác, Maps có các phương thức nguyên mẫu cho phép ta nhận trực tiếp các khóa, giá trị và mục nhập của đối tượng Bản đồ.

Các phương thức keys() , values()entries() đều trả về một MapIterator , tương tự như một Mảng mà bạn có thể sử dụng for...of để lặp qua các giá trị.

Đây là một ví dụ khác về Bản đồ, ta có thể sử dụng để chứng minh các phương pháp này:

const map = new Map([
  [1970, 'bell bottoms'],
  [1980, 'leg warmers'],
  [1990, 'flannel'],
])

Phương thức keys() trả về các khóa:

map.keys()
Output
MapIterator {1970, 1980, 1990}

Phương thức values() trả về các giá trị:

map.values()
Output
MapIterator {"bell bottoms", "leg warmers", "flannel"}

Phương thức entries() trả về một mảng các cặp khóa / giá trị:

map.entries()
Output
MapIterator {1970 => "bell bottoms", 1980 => "leg warmers", 1990 => "flannel"}

Lặp lại với Bản đồ

Bản đồ có một phương thức forEach được tích hợp sẵn, tương tự như Array, để lặp lại được tích hợp sẵn. Tuy nhiên, có một chút khác biệt trong những gì họ lặp lại. Lệnh gọi lại forEach của Bản đồ lặp lại qua value , keymap , trong khi version Mảng lặp lại qua chính item , indexarray .

// Map 
Map.prototype.forEach((value, key, map) = () => {})

// Array
Array.prototype.forEach((item, index, array) = () => {})

Đây là một lợi thế lớn của Bản đồ so với Đối tượng, vì Đối tượng cần được chuyển đổi bằng keys() , values() hoặc entries() và không có cách nào đơn giản để truy xuất các thuộc tính của Đối tượng mà không cần chuyển đổi nó.

Để chứng minh điều này, hãy lặp lại Bản đồ của ta và ghi các cặp khóa / giá trị vào console :

// Log the keys and values of the Map with forEach
map.forEach((value, key) => {
  console.log(`${key}: ${value}`)
})

Điều này sẽ cho:

Output
1970: bell bottoms 1980: leg warmers 1990: flannel

Vì vòng lặp for...of lặp qua for...of vòng lặp như Bản đồ và Mảng, ta có thể nhận được cùng một kết quả chính xác bằng cách cấu trúc lại mảng các mục Bản đồ:

// Destructure the key and value out of the Map item
for (const [key, value] of map) {
  // Log the keys and values of the Map with for...of
  console.log(`${key}: ${value}`)
}

Thuộc tính và phương pháp bản đồ

Bảng sau đây hiển thị danh sách các thuộc tính và phương pháp của Bản đồ để tham khảo nhanh:

Thuộc tính / Phương thức Sự miêu tả Lợi nhuận
set(key, value) Thêm cặp khóa / giá trị vào Bản đồ Đối tượng Map
delete(key) Xóa cặp khóa / giá trị khỏi Bản đồ theo khóa Boolean
get(key) Trả về giá trị theo khóa giá trị
has(key) Kiểm tra sự hiện diện của một phần tử trong Bản đồ bằng khóa Boolean
clear() Xóa tất cả các mục khỏi Bản đồ N / A
keys() Trả về tất cả các khóa trong Bản đồ Đối tượng MapIterator
values() Trả về tất cả các giá trị trong một Bản đồ Đối tượng MapIterator
entries() Trả về tất cả các khóa và giá trị trong Bản đồ dưới dạng [key, value] Đối tượng MapIterator
forEach() Lặp lại qua Bản đồ theo thứ tự chèn N / A
size Trả về số lượng mục trong Bản đồ Con số

Khi nào sử dụng bản đồ

Tóm lại, Bản đồ tương tự như Đối tượng ở chỗ chúng chứa các cặp khóa / giá trị, nhưng Bản đồ có một số lợi thế hơn đối tượng:

  • Kích thước - Bản đồ có thuộc tính size , trong khi Đối tượng không có cách tích hợp để truy xuất kích thước của chúng.
  • Lặp lại - Bản đồ có thể lặp lại trực tiếp, trong khi Đối tượng thì không.
  • Tính linh hoạt - Bản đồ có thể có bất kỳ kiểu dữ liệu nào (nguyên thủy hoặc Đối tượng) làm key cho một giá trị, trong khi Đối tượng chỉ có thể có chuỗi.
  • Có thứ tự - Bản đồ giữ nguyên thứ tự chèn của chúng, trong khi các đối tượng không có thứ tự đảm bảo.

Do những yếu tố này, Bản đồ là một cấu trúc dữ liệu mạnh mẽ cần xem xét. Tuy nhiên, Objects cũng có một số lợi thế quan trọng:

  • JSON - Các đối tượng hoạt động hoàn hảo với JSON.parse()JSON.stringify() , hai hàm cần thiết để làm việc với JSON , một định dạng dữ liệu phổ biến mà nhiều API REST xử lý.
  • Làm việc với một phần tử duy nhất - Làm việc với một giá trị đã biết trong Đối tượng, bạn có thể truy cập trực tiếp nó bằng khóa mà không cần sử dụng một phương thức, chẳng hạn như Map's get() .

Danh sách này sẽ giúp bạn quyết định xem Bản đồ hoặc Đối tượng có phải là cấu trúc dữ liệu phù hợp cho trường hợp sử dụng của bạn hay không.

Bộ

Tập hợp là một tập hợp các giá trị duy nhất. Không giống như một Bản đồ, một Tập hợp về mặt khái niệm giống với một Mảng hơn là một Đối tượng, vì nó là một danh sách các giá trị chứ không phải các cặp khóa / giá trị. Tuy nhiên, Set không phải là sự thay thế cho Mảng, mà là một phần bổ sung để cung cấp hỗ trợ bổ sung để làm việc với dữ liệu trùng lặp.

Bạn có thể khởi tạo Bộ bằng cú pháp new Set() .

const set = new Set()

Điều này cho ta một Tập hợp trống:

Output
Set(0) {}

Các mục có thể được thêm vào một Tập hợp bằng phương thức add() . (Điều này không được nhầm lẫn với phương thức set() có sẵn cho Map, mặc dù chúng tương tự nhau.)

// Add items to a Set
set.add('Beethoven')
set.add('Mozart')
set.add('Chopin')

Vì Bộ chỉ có thể chứa các giá trị duy nhất nên mọi nỗ lực thêm giá trị đã tồn tại sẽ bị bỏ qua.

set.add('Chopin') // Set will still contain 3 unique values

Lưu ý : So sánh bình đẳng tương tự áp dụng cho các phím Bản đồ cũng áp dụng cho Đặt mục. Hai đối tượng có cùng giá trị nhưng không cùng tham chiếu sẽ không được coi là bằng nhau.

Bạn cũng có thể khởi tạo Bộ với Mảng giá trị. Nếu có các giá trị trùng lặp trong mảng, chúng sẽ bị xóa khỏi Tập hợp.

// Initialize a Set from an Array
const set = new Set(['Beethoven', 'Mozart', 'Chopin', 'Chopin'])
Output
Set(3) {"Beethoven", "Mozart", "Chopin"}

Ngược lại, một Bộ có thể được chuyển đổi thành Mảng với một dòng mã:

const arr = [...set]
Output
(3) ["Beethoven", "Mozart", "Chopin"]

Set có nhiều phương thức và thuộc tính giống như Map, bao gồm delete() , has() , clear()size .

// Delete an item
set.delete('Beethoven') // true

// Check for the existence of an item
set.has('Beethoven') // false

// Clear a Set
set.clear()

// Check the size of a Set
set.size // 0

Lưu ý Set không có cách nào để truy cập giá trị bằng khóa hoặc index , như Map.get(key) hoặc arr[index] .

Khóa, Giá trị và Mục nhập cho Bộ

Map và Set đều có keys() , values()entries() trả về một lặp lại. Tuy nhiên, trong khi mỗi phương pháp này có một mục đích riêng biệt trong Bản đồ, Bộ không có khóa và do đó khóa là alias cho các giá trị. Điều này nghĩa là keys()values() đều sẽ trả về cùng một Iterator và entries() sẽ trả về giá trị hai lần. Sẽ là hợp lý nhất khi chỉ sử dụng values() với Set, vì hai phương thức còn lại tồn tại đảm bảo tính nhất quán và khả năng tương thích chéo với Map.

const set = new Set([1, 2, 3])
// Get the values of a set
set.values()
Output
SetIterator {1, 2, 3}

Lặp lại với Set

Giống như Map, Set có một phương thức forEach() . Vì Bộ không có khóa, nên tham số đầu tiên và thứ hai của lệnh gọi lại forEach() trả về cùng một giá trị, vì vậy không có trường hợp sử dụng nào cho nó ngoài khả năng tương thích với Bản đồ. Các tham số của forEach()(value, key, set) .

Cả forEach()for...of đều được dùng trên Set. Đầu tiên, hãy xem lần lặp forEach() :

const set = new Set(['hi', 'hello', 'good day'])

// Iterate a Set with forEach
set.forEach((value) => console.log(value))

Sau đó, ta có thể viết version for...of :

// Iterate a Set with for...of
for (const value of set) {  
    console.log(value);
}

Cả hai chiến lược này sẽ mang lại những kết quả sau:

Output
hi hello good day

Đặt thuộc tính và phương thức

Bảng sau đây hiển thị danh sách các thuộc tính và phương thức Đặt để tham khảo nhanh:

Thuộc tính / Phương thức Sự miêu tả Lợi nhuận
add(value) Thêm một mục mới vào một Bộ Set đối tượng
delete(value) Xóa mục đã chỉ định khỏi Bộ Boolean
has() Kiểm tra sự hiện diện của một mục trong một Bộ Boolean
clear() Xóa tất cả các mục khỏi một Tập hợp N / A
keys() Trả về tất cả các giá trị trong một Tập hợp (giống như values() ) Đối tượng SetIterator
values() Trả về tất cả các giá trị trong một Tập hợp (giống như keys() ) Đối tượng SetIterator
entries() Trả về tất cả các giá trị trong một Tập hợp là [value, value] Đối tượng SetIterator
forEach() Lặp lại thông qua Đặt theo thứ tự chèn N / A
size Trả về số lượng mục trong một Tập hợp Con số

Khi nào thì sử dụng bộ

Set là một bổ sung hữu ích cho bộ công cụ JavaScript của bạn, đặc biệt để làm việc với các giá trị trùng lặp trong dữ liệu.

Trong một dòng, ta có thể tạo một Mảng mới mà không có các giá trị trùng lặp từ Mảng có các giá trị trùng lặp.

const uniqueArray = [ ...new Set([1, 1, 2, 2, 2, 3])] // (3) [1, 2, 3]

Điều này sẽ cho:

Output
(3) [1, 2, 3]

Tập hợp được dùng để tìm kết hợp, giao điểm và sự khác biệt giữa hai tập dữ liệu. Tuy nhiên, Mảng có lợi thế đáng kể so với Bộ về thao tác bổ sung dữ liệu do các phương sort() , map() , filter()reduce() cũng như khả năng tương thích trực tiếp với các phương thức JSON .

Kết luận

Trong bài viết này, bạn đã biết rằng Bản đồ là tập hợp các cặp khóa / giá trị có thứ tự và Tập hợp là tập hợp các giá trị duy nhất. Cả hai cấu trúc dữ liệu này đều bổ sung các khả năng bổ sung cho JavaScript và đơn giản hóa các việc phổ biến như tìm độ dài của bộ sưu tập cặp khóa / giá trị và xóa các mục trùng lặp khỏi tập dữ liệu, tương ứng. Mặt khác, Đối tượng và Mảng đã được sử dụng truyền thống để lưu trữ và thao tác dữ liệu trong JavaScript và có khả năng tương thích trực tiếp với JSON, điều này tiếp tục biến chúng trở thành cấu trúc dữ liệu quan trọng nhất, đặc biệt là để làm việc với REST API. Bản đồ và Bộ chủ yếu hữu ích khi hỗ trợ cấu trúc dữ liệu cho Đối tượng và Mảng.

Nếu bạn muốn tìm hiểu thêm về JavaScript, hãy xem trang chủ của loạt bài Cách viết mã trong JavaScript của ta hoặc duyệt qua loạt bài Cách viết mã trong Node.js của ta để biết các bài viết về phát triển back-end.


Tags:

Các tin liên quan

Hiểu Hợp nhất Sắp xếp Thông qua JavaScript
2020-02-08
Tạo biểu mẫu tùy chỉnh bằng API dữ liệu biểu mẫu JavaScript
2020-02-06
Thuật toán sắp xếp cho người mới bắt đầu trong JavaScript: Bubble, Selection & Insertion Sort
2020-02-03
Tìm kiếm nhị phân tuyến tính Vs qua JavaScript
2020-01-29
Hiểu Đệ quy & Ghi nhớ qua JavaScript
2020-01-26
Đọc và xử lý tệp với API JavaScript FileReader
2020-01-23
Tìm hiểu ký hiệu Big O qua JavaScript
2020-01-20
Cách sử dụng API BroadcastChannel trong JavaScript
2020-01-13
Đa năng hóa chuỗi trong JavaScript bằng Simplur
2020-01-10
Hướng dẫn nhanh về phương pháp đối sánh chuỗi trong JavaScript
2020-01-07