Array.from ile hangi karakterler gruplandırılır?


38

Ben JS ile oynuyorum ve nasıl JS kullanırken oluşturulan dizi eklemek için hangi öğeleri karar karar veremiyorum Array.from(). Örneğin, aşağıdaki emoji 👍, lengthiki kod noktasından yapıldığı için 2'ye sahiptir , ancak Array.from()bu iki kod noktasına bir öğe olarak davranır ve bir öğeye sahip bir dizi verir:

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

Bununla birlikte, diğer bazı karakterlerin de bu karakter gibi iki kod noktası षिvardır (ayrıca .length2'ye sahiptir). Ancak, Array.frombu karakteri "gruplandırmaz" ve bunun yerine iki öğe üretir:

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

Sorum şu: Karakter iki kod noktasından oluştuğunda karakterin parçalanıp bölünmediğini (örneğin iki örnekte olduğu gibi) veya tek bir öğe (örnekte olduğu gibi) olarak ele alındığını belirleyen nedir?


5
UTF-16 vekil çiftlerine bir göz atın ...
Jonas Wilms


1
Farklı bir davranışı olan MDN'nin Array.from çoklu dolgusu hakkında bir endişem var: -s
Ele

1
@Ele sadece nesneleri dikkate alır length. Yineleyiciler ve hatta Setbununla çalışmıyor
adiga

Yanıtlar:


26

Array.fromilk önce, varsa argümanın yinelemesini çağırmaya çalışır ve dizelerde yineleyiciler bulunur, bu nedenle çağırır String.prototype[Symbol.iterator], bu yüzden prototip yönteminin nasıl çalıştığına bakalım. Buradaki spesifikasyonda açıklanmıştır :

  1. Bırakalım O mu? RequireObjectCoercible (bu değer).
  2. Hadi olalım ? .ToString (O) olmasıdır.
  3. CreateStringIterator (S) öğesini döndürün.

CreateStringIteratorSonunda bakmak sizi götürür 21.1.5.2.1 %StringIteratorPrototype%.next ( ), bu da şunları yapar:

  1. Bırak cp olsun! CodePointAt (s, konum).
  2. ResultsString öğesinin, kod birimi dizin konumundayken başlayarak cp. [[CodeUnitCount]] ardışık kod birimlerini içeren String değeri olsun.
  3. O. [[StringNextIndex]] konumunu + cp. [[CodeUnitCount]] olarak ayarlayın.
  4. CreateIterResultObject öğesini döndürür (sonuçString, yanlış).

CodeUnitCountEğer ilgilendiğiniz şeydir bu sayı gelir. CodePointAt :

  1. Önce dize içindeki dizin konumundaki kod birimi olsun.
  2. Sayısal değeri ilk olan kod noktası cp olsun.
  3. İlk önce önde gelen vekil veya takip eden vekil değilse,

    a. Kaydı Döndür { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.

  4. İlk önce bir vekil veya konum + 1 = boyutsa, o zaman

    Kaydı Döndür { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  5. İkincisi, dize içindeki dizin konumunda + 1 olan kod birimi olsun.

  6. İkincisi sondaki bir vekil değilse,

    a. Kaydı Döndür { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  7. Cp olarak ayarlayın! UTF16DecodeSurrogatePair (birinci, ikinci).

  8. Kaydı Döndür { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.

Bu nedenle, bir dize üzerinden yineleme Array.fromyaparken, yalnızca söz konusu karakter bir vekil çiftin başlangıcı olduğunda CodeUnitCount 2 değerini döndürür. Yedek çiftler olarak yorumlanan karakterler burada açıklanmıştır :

Bu tür işlemler, 0xD800 - 0xDBFF dahilinde sayısal bir değere sahip her kod birimine (Unicode Standard tarafından öncü vekil olarak veya daha resmi olarak yüksek vekil kod birimi olarak tanımlanır) ve sayısal değere sahip her kod birimine özel işlem uygular. Aşağıdaki kuralları kullanarak 0xDC00 ila 0xDFFF (izleyen vekil olarak tanımlanır veya daha resmi olarak düşük vekil kod birimi olarak tanımlanır).

षि bir vekil çift değildir:

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

Ama 👍karakterleri:

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

İlk karakter kodu, '👍'altıgen olarak, 0xD800 to 0xDBFFönde gelen taşıyıcıların aralığı içinde olan D83D'dir . Buna karşılık, ilk karakter kodu 'षि'çok daha düşüktür ve değildir. Yani 'षि'bölünür, ama '👍'olmaz.

षिİki ayrı karakterden oluşur: , Devanagari Harf Ssa ve ि, Devanagari Sesli I yapınız . Bu sırayla yan yana olduklarında, iki ayrı karakterden oluşmasına rağmen görsel olarak tek bir karakterde grafik olarak birleştirilirler.

Buna karşılık, karakter kodları 👍 sadece tek bir glif olarak birlikte olduğunda anlamlıdır. Herhangi bir kod noktası diğeri olmadan bir dize kullanmaya çalışırsanız, saçma bir sembol alırsınız:

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


10
Çoğunlukla doğru, yararlı ve dikkatle sağlanan alıntılarla birlikte, bu cevabın iki durum arasındaki temel farkı açıkça açıklayamadığını düşünüyorum: Unicode bakış açısından, षिaslında tek bir kod oluşturmak için farklı kod noktalarına sahip iki karakter glif ( insanlar tarafından anlaşıldığı gibi bir soyut karakter). Bu, 👍kod noktası bir vekil çift olarak bölünmesi için yeterince yüksek olmasına rağmen, kendi içinde tam bir karakter olan emojinin aksine . Ben bunun (aksi takdirde değerli) çok cevap yardımcı olabilir açıklığa inanıyorum.
gergedan

Özellikle, ünsüz ष (ṣ) ve ünlü ि (i) grafiksel olarak hece षि (ṣi)
Amadan

@CertainPerformance "👍" de yalnızca bir kod noktası vardır. Bu, bu yanıttaki terminolojinin yanlış olabileceğini düşündürmektedir.
Ben Aston

13

UTF-16 (js'de dizeler için kullanılan kodlama) 16 bitlik birimler kullanır. Dolayısıyla, 15 bit kullanılarak temsil edilebilecek her unicode bir kod noktası, diğer her şey ikişer çift olarak bilinir . Dizeleri yineleyici kod noktaları üzerinde dolaşır.

Wikipedia'da UTF-16


8

Her şey karakterlerin arkasındaki kodla ilgili. Bazıları iki bayt (UTF-16) olarak kodlanır ve Array.fromiki karakter olarak yorumlanır . Karakterlerin listesini kontrol etmeliyim:

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

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

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)));


Onaltılık kodu görüntüleyen işlev için:

Javascript: Unicode string to hex

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.