Những ký tự nào được nhóm với Array.from?


38

Tôi đã chơi xung quanh với JS và không thể hiểu làm thế nào JS quyết định thêm phần tử nào vào mảng đã tạo khi sử dụng Array.from(). Ví dụ: biểu tượng cảm xúc sau có length2, vì nó được tạo bởi hai điểm mã, nhưng, Array.from()coi hai điểm mã này là một, tạo ra một mảng có một phần tử:

const emoji = '👍';
console.log(Array.from(emoji)); // Output: ["👍"]

Tuy nhiên, một số ký tự khác cũng có hai điểm mã như ký tự này षि(cũng có .length2 điểm). Tuy nhiên, Array.fromkhông "nhóm" nhân vật này và thay vào đó tạo ra hai yếu tố:

const str = 'षि';
console.log(Array.from(str)); // Output: ["ष", "ि"]

Câu hỏi của tôi là: Điều gì quyết định liệu nhân vật có bị phá vỡ (như trong ví dụ hai) hay được coi là một yếu tố duy nhất (như trong ví dụ một) khi nhân vật bao gồm hai điểm mã?


5
Hãy xem các cặp thay thế UTF-16 ...
Jonas Wilms


1
Tôi có một mối quan tâm về polyfill của Array.from của MDN, có một hành vi khác: -s
Ele

1
@Ele nó chỉ xem xét các đối tượng với length. Trình lặp hoặc thậm chí Setkhông hoạt động với điều đó
adiga

Câu trả lời:


26

Array.fromđầu tiên cố gắng gọi trình lặp của đối số nếu nó có một và các chuỗi có các trình lặp, vì vậy nó gọi String.prototype[Symbol.iterator], vì vậy hãy tìm hiểu cách thức hoạt động của phương thức nguyên mẫu. Nó được mô tả trong đặc tả ở đây :

  1. Cho O là? RequireObjectCoercible (giá trị này).
  2. Cho S là? ToString (O).
  3. Trả về CreatStringIterator (S).

Nhìn lên CreateStringIteratorcuối cùng sẽ đưa bạn đến 21.1.5.2.1 %StringIteratorPrototype%.next ( ), mà:

  1. Hãy để cp được! CodePointAt (s, vị trí).
  2. Đặt resultString là giá trị Chuỗi chứa cp. [[CodeUnitCount]] các đơn vị mã liên tiếp từ s bắt đầu với đơn vị mã ở vị trí chỉ mục.
  3. Đặt O. [[StringNext Index]] thành vị trí + cp. [[CodeUnitCount]].
  4. Trả về CreatIterResultObject (resultString, false).

Đây CodeUnitCountlà những gì bạn quan tâm. Số này đến từ CodePointAt :

  1. Trước tiên hãy là đơn vị mã ở vị trí chỉ mục trong chuỗi.
  2. Đặt cp là điểm mã có giá trị số là giá trị đầu tiên.
  3. Nếu trước tiên không phải là người thay thế hàng đầu hoặc người thay thế

    a. Trả lại hồ sơ { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.

  4. Nếu đầu tiên là một đại diện thay thế hoặc vị trí + 1 = kích thước, thì

    a.Quay lại bản ghi { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  5. Đặt thứ hai là đơn vị mã ở vị trí chỉ mục + 1 trong chuỗi.

  6. Nếu thứ hai không phải là một đại diện thay thế, thì

    a. Trả lại hồ sơ { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  7. Đặt cp thành! UTF16DecodeSurrogatePair (thứ nhất, thứ hai).

  8. Trả lại hồ sơ { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.

Vì vậy, khi lặp qua một chuỗi với Array.from, nó chỉ trả về CodeUnitCount bằng 2 khi ký tự trong câu hỏi là bắt đầu của một cặp thay thế. Các ký tự được hiểu là cặp thay thế được mô tả ở đây :

Các hoạt động này áp dụng xử lý đặc biệt cho mọi đơn vị mã có giá trị số trong phạm vi bao gồm 0xD800 đến 0xDBFF (được xác định bởi Tiêu chuẩn Unicode là đại diện thay thế hàng đầu , hoặc chính thức hơn là đơn vị mã thay thế cao) và mọi đơn vị mã có giá trị số trong phạm vi bao gồm 0xDC00 đến 0xDFFF (được định nghĩa là đại diện thay thế hoặc chính thức hơn là một đơn vị mã thay thế thấp) sử dụng các quy tắc sau ..:

षि không phải là một cặp thay thế:

console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F

Nhưng 👍các nhân vật là:

console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D

Mã ký tự đầu tiên '👍'là, trong hex, D83D, nằm trong phạm vi của 0xD800 to 0xDBFFcác đại diện thay thế hàng đầu. Ngược lại, mã ký tự đầu tiên của 'षि'thấp hơn nhiều, và không. Vì vậy, 'षि'được tách ra, nhưng'👍' không.

षिbao gồm hai nhân vật riêng biệt: , Devanagari Thư SSA , và ि, Devanagari nguyên âm Đăng tôi . Khi nằm cạnh nhau theo thứ tự này, chúng được kết hợp đồ họa thành một ký tự trực quan, mặc dù được tạo thành từ hai ký tự riêng biệt.

Ngược lại, mã ký tự 👍 chỉ có ý nghĩa khi cùng nhau dưới dạng một glyph. Nếu bạn cố gắng sử dụng một chuỗi có điểm mã mà không có mã khác, bạn sẽ nhận được một biểu tượng vô nghĩa:

console.log('👍'[0]);
console.log('👍'[1]);


10
Tôi nghĩ rằng, mặc dù hầu hết là chính xác, hữu ích và với các trích dẫn được cung cấp cẩn thận, câu trả lời này không giải thích rõ ràng sự khác biệt chính giữa hai trường hợp: theo quan điểm Unicode, षिthực ra là hai ký tự với các điểm mã riêng biệt được kết hợp để tạo thành một glyph (một nhân vật trừu tượng , theo cách hiểu của con người). Điều này trái ngược với 👍biểu tượng cảm xúc, vốn là một nhân vật hoàn chỉnh, mặc dù điểm mã của nó đủ cao để nó phải được chia thành một cặp thay thế. Tôi tin rằng làm rõ rằng có thể giúp điều này (nếu không có giá trị) trả lời rất nhiều.
tê giác

Cụ thể, phụ âm ष (S) và nguyên âm ि (i) đồ họa kết hợp vào âm tiết षि (Si)
Amadan

@CertainPerformance Chỉ có một điểm mã trong "". Điều này cho thấy thuật ngữ trong câu trả lời này có thể không chính xác.
Ben Aston

13

UTF-16 (mã hóa được sử dụng cho chuỗi trong js) sử dụng đơn vị 16 bit. Vì vậy, mọi unicode có thể được biểu diễn bằng 15 bit được biểu diễn dưới dạng một điểm mã, mọi thứ khác là hai, được gọi là cặp thay thế . Trình lặp của chuỗi lặp trên các điểm mã.

UTF-16 trên Wikipedia


8

Đó là tất cả về mã đằng sau các nhân vật. Một số được mã hóa bằng hai byte (UTF-16) và được hiểu Array.fromlà hai ký tự. Phải kiểm tra danh sách các nhân vật:

http://www.fileformat.info/info/charset/UTF-8/list.htmlm

http://www.fileformat.info/info/charset/UTF-16/list.htmlm

function displayHexUnicode(s) {
  console.log(s.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),""));
}

displayHexUnicode('षि');

console.log(Array.from('षि').forEach(x => displayHexUnicode(x)));


function displayHexUnicode(s) {
  console.log(s.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),""));
}

displayHexUnicode('👍');

console.log(Array.from('👍').forEach(x => displayHexUnicode(x)));


Đối với chức năng hiển thị mã hex:

Javascript: Chuỗi Unicode thành hex

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.