我们前端是最常见的字符,符号,数字,英文,中文,我们通常都是使用直接量来表示,偶尔会在正则表达等场景用到UTF-16码点的格式,问题来了,那你知道JS有几种字符表示方式吗?
答案:至少6种,以字符a为例子:
`a`//'a''a'//'a''\a'//'a''\141'//'a''\x61'//'a''\u0061'//'a''\u{0061}'//'a'前三种都很理解,后面这又是\,\x,\u,\u{},这都是什么玩意?
别急,我们一一道来。
更多前端基础进阶知识,可以
特别申明:测试代码最新版chrome上执行。
完全理解字符表示,还是需要一些简单的编码知识,我们一起来看看吧。
ASCII码一共定义了128个字符,例字母a是97(01100001)。这128个字符只使用了8位二进制数中的后面7位,最前面的一位统一规定为0。
Unicode是字符集,为了兼容ASCII,Unicode规定前0-127个字符是和ASCII是一样的,不一样的是128-255这一部分。
我们一起看看ASCII128-255部分:
其给某个字符规定对应的数值,我们经常称其为码点。我们可以通过字符串的实例方法charCodeAt和codePointAt获取,前者只能准确获取码点值小于0xFFFF(65535)的码点。
''.codePointAt(0)//1311040x20020正确''.charCodeAt(0)//553600xd840错误'a'.charCodeAt(0)//970x0061正确对应的我们可以使用String的静态方法fromCharCode,fromCodePoint用码点获取对应的字符。
String.fromCodePoint(131104)//""正确String.fromCharCode(131104)//""错误String.fromCharCode(97)//"a"正确UTF-8,UTF-16我们平时接触比多的就是UTF-8和UTF-16,均是Unicode字符编写的一种实现。我们JS编码的字符串是UTF-16格式来存储的和表示的。
UTF-16对于码点小于0xFFFF的用2个字节(1个编码单元)表示,大于0xFFFF的编码用四个字节(2个编码单元)表示。具体的体现可以表现在字符的长度上。
"".length//2码点131104(0x20020)>65535(0xFFFF)"a".length//1"人".length//1这里强调0xFFFF是分界线,很重要。
我们可以使用数字实例toString()10进制转为相对应的进制。
97..toString(16)//6197..toString(2)//1100001下面进入正题,一起来看字符的表示:
\是一个特殊的存在,转义字符,大多数情况下,不产生什么作用。只对一些特殊的字符起作用。
从下可以看出\a这个\没有任何作用,对\r就不一样了。
其能表示的码点范围值为0-255。
这里提个问题,这里显示的ASCII的字符,还是Unicode的字符。
'a'.charCodeAt(0)//97console.log('\141')//a我们看一些特殊码点的字符,因为码点为31和127的字符,不能被显示或者表示。
//37=31..toString(8)'\37'//'\x1F'//177=127..toString(8)'\177'//'\x7F'至于为什么\177变为了\x7F,是不是有点疑惑,其实也很简单。当程序检查其值,不在可显示范围的时候,直接反向计算其原值,并转为16进制值,并使用\x两位十六进制格式表示。
//177=127..toString(8)'\177'//'\x7F'127.toString(16)//7f关于表示码点上限(255):
'\377'//""---码点255'\400'//'0'---码点256//大于255可以理解为'\40'+'0'回答开始的提问:我们这里显示的肯定是Unicode字符啊,前面提过了,JS字符编码采用的是UTF-16啊。可以用码点在128-255的一个字符试试,那就码点254的字符吧:
//376=254..toString(8)'\376'//'t'所以大家要明白,我们这各种字符表示方法,表示的都是Unicode字符。
关于\x两位十六进制格式,我们开讲。
我们可以用0x表示16进制的数字,所以\x大家也很好理解,是16进制。
0x61//970x表示16进制格式数字'a'.charCodeAt(0)//获取码点9797..toString(16)//转为16进制61'\x61'//'a'两位十六进制码点,0x00~0xFF(0~255),和\八进制码一样,不可显示的码点字符,直接显示其编码
'\x9F'//'\x9F'//编码输出'\xA1'//""//正常这个结果,在不通浏览器,可能输出还不一样。尽量采用最新版本的chrome去验证。
虽然输出的显示有些区别,但是都表示这玩意不能表示一个字符,请自重。
360浏览器:
firefox:
chrome:
这里固定是4位,少一位都不行。
还是4位,如果多了,截取前4位,后面的直接追加。看个例子,非常好理解。
'\u0061'//'a''\u00610'//'a0'这里也就反应了问题,码点大于0xFFFF,大于4位16进制的字符怎么表示???
ES6考虑了这个问题,于是推出了\u{+十六进制+},看下个章节。
我们之前说过,UTF-16是Unicode的一种实现,Unicode的代理区0xD800-0xDFFF,其不代表任何字符。同理,我们采用\u+四位十六进制方式,如果码点在这个区间,返回或者原字符(浏览器不同,可能返回不同),当然其他的码点也可能还没设定值或者是不可打印的。
'\uD800'//'\uD800''\uDFFF'//'\uD800'实际上UTF-16也就是利用了代理区,把码点大于0xFFFF字符分为高低两部分,索引值0获得的值实际上是高位部分,索引值1获得的是低位部分。
vartext="";text[0]//'\uD840'text[1]//'\uDC20'更多utf-16编码的知识,我们后续跟上。
ES6新增的能力。这个多了一个{}包裹。这个应该是可以一统江湖,可以表示码点低于0xFFFF的字符,也可表示码点大于0xFFFF的字符。
"\u{20020}"//''"\u{0061}"//'a'"\u{061}"//'a'"\u{61}"//'a'"\u{9}"//"\t"而且其还没有强制四位的限制,简直爽的没法没边。
ES6的模板字符串,可以说是爽歪歪,我们就也算其一种新的字符表示方式吧。
我们也是可以使用\u,\u{},\x格式的
//61="a".charCodeAt(0).toString(16)`我\u{61}`//我a`我\x61`//我a`我\u0061`//我a`我\a`//我a到这里,你们没觉得少了了点什么吗?没错\8进制,是不被允许的
如果,你非得用,那就${''}包裹一下:
`我${'\141'}`//'我a'实际的应用匹配中文的正则我们总不能去罗列,多义采取的是[\u4e00-\u9fa5]这种格式去匹配:
varregZH=/[\u4e00-\u9fa5]/g;regZH.test("a");//falseregZH.test("人");//trueregZH.test("");//false尴尬了不这里注意了,只能识别常见中文。,毕竟码点的范围就那么大点。
module.exports='\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002'+'\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';我们还是输出一下吧:
'\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002'+'\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF'//'\t\n\v\f\r'CSScontent属性和CSS字体图标现代浏览器,已经支持中文,但是建议还是使用\16进制编码格式。这里使用的是\16进制,不需要u也不需要{},支持码点大于0xFFFF的字符
div.me::before{content:"\6211";//我padding-right:10px;}div.me::before{content:"\20020:";//padding-right:10px;}CSS颜色色值字体颜色,背景颜色,边框颜色等等,其有一种表示方式就是6位16进制,当然也有简写形式。
constspRegexp=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;functionchCounts(str){returnstr.replace(spRegexp,'_').length;}chCounts("")//1"".length//2ES6中的方式就多一些了:
constisPNG=check([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a]);//PNG图片对应的魔数constrealFileElement=document.querySelector("#realFileType");asyncfunctionhandleChange(event){constfile=event.target.files[0];constbuffers=awaitreadBuffer(file,0,8);constuint8Array=newUint8Array(buffers);realFileElement.innerText=`${file.name}文件的类型是:${isPNG(uint8Array)"image/png":file.type}`;}其他写在最后不忘初衷,有所得,而不为所累,如果你觉得不错,你的一赞一评就是我前行的最大动力。