Array.from
首先尝试调用参数的迭代器(如果有的话),而字符串确实具有迭代器,因此它会调用String.prototype[Symbol.iterator]
,因此让我们看一下原型方法的工作方式。在规范中对此进行了描述:
- 让O成为?RequireObjectCoercible(此值)。
- 让S成为?ToString(O)。
- 返回CreateStringIterator(S)。
CreateStringIterator
最终查找会带您到21.1.5.2.1 %StringIteratorPrototype%.next ( )
,它执行以下操作:
- 让cp为!CodePointAt(s,位置)。
- 令resultString为包含cp。[[CodeUnitCount]]从s开始的连续代码单元的String值,从位于索引位置的代码单元开始。
- 将O。[[StringNextIndex]]设置为+ cp。[[CodeUnitCount]]。
- 返回CreateIterResultObject(resultString,false)。
该CodeUnitCount
是你感兴趣的是这个数字从何而来。提供codePointAt:
- 首先让它成为字符串中索引位置的代码单元。
- 令cp为其数字值为first的代码点。
如果首先不是领先的代理人或尾随的代理人,则
一个。返回记录{ [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
。
如果第一个是尾随代理或位置+ 1 =大小,则
返回记录{ [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
。
令second为字符串中索引位置+ 1处的代码单元。
如果秒不是尾随代理,则
一个。返回记录{ [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
。
将cp设置为!UTF16DecodeSurrogatePair(第一,第二)。
返回记录{ [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }
。
因此,当使用迭代字符串时Array.from
,仅当所讨论的字符是代理对的开头时,它才返回CodeUnitCount为2。这里描述了被解释为代理对的字符:
此类操作对数字值在0xD800到0xDBFF(由Unicode标准定义为领先代理,或更正式地作为高代理代码单元)范围内的每个代码单元和每个具有数字值的代码单元都进行特殊处理。使用以下规则在0xDC00到0xDFFF(包括尾随代理,或更正式地定义为低代理代码单元)的范围内。
षि
不是代理对:
console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F
但是👍
的字符是:
console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D
的第一个字符代码为'👍'
D83D(十六进制),位于0xD800 to 0xDBFF
前导替代字符的范围内。相反,的第一个字符代码'षि'
要低得多,而没有。因此,'षि'
将其分开,但'👍'
没有。
षि
是由两个独立的字符:ष
,梵文字母SSA,并且ि
,梵文元音登录我。当按此顺序彼此相邻时,尽管它们由两个单独的字符组成,但它们在视觉上以图形方式组合为单个字符。
相反,字符代码👍
只有在一起作为单个字形时才有意义。如果您尝试将一个代码点与另一个代码点一起使用而没有另一个,则将得到一个无意义的符号:
console.log('👍'[0]);
console.log('👍'[1]);