Skip to content

Commit

Permalink
auto-archiving for issue #244
Browse files Browse the repository at this point in the history
  • Loading branch information
kaola-blog-bot committed Mar 27, 2018
1 parent 999f869 commit 7edc93e
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions source/_posts/从两个字符看 JavaScript 编码.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
### 引子
1. 前几天在`shopms`中有一个页面报错,看了一下发现是在最外面拿到后端给的同步数据的时候`JSON.parse()`的时候报错的,仔细看了之后发现后端传过来一个`\ u2027`的一个字符,下面这样就直接报错了
```
JSON.parse({"a":"\u2027"})
```

### 编码
文字算不算编码。盲文算不算编码。电报算不算编码。编码是用来交流/传递信息的一种方式/媒介,那么我们和计算机之间的信息传递自然也需要编码。

在计算机内部信息以电路的方式传递,通或不通代表1和0,所以基本上的信息单元(Binary digit)就是二进制的一位叫做比特(Bit)

我们输入的时候怎么让计算机知道我们输入的什么,或者计算机输出的时候我们也要知道他输出的什么,所以需要一种将二进制的信息转换成我们能理解的信息


### ASCII
因为这种需求,在1967年美国公布了一套信息交换标准代码简称 ASCII 码,ASCII 码规定了一共128个字符,用的是7位编码,因为计算机是按8位存储的,所以 ASCII 前面一位一般都是0,所以一个 ASCII 码占8个二进制位,也就是一个字节(byte)。

对于英文做母语的人来讲 ASCII 是够了,但是还有很多还有很多中文,日文这种又怎么办呢

### Unicode
1988年,几个大型的计算机公司一起研究了一字符集码叫做 Unicode,旨在为全世界所有的符号创造一个唯一的编码。

最开始 Unicode 使用的是2个字节也就是16位进行编码,可以表最少示 65 536 中不同的字符,后来发现不够,所以一个 Unicode 码至少需要占两个字节,那么都用 Unicode 码来传递存储的话会很浪费空间。

比如一个英文字符明明只需要1个字节就能存储,用 Unicode 的话就要2个字节,那么又人可能会想,那能用 ASCII 码的字符就用 ASCII,不能再用 Unicode 不就好了,但是计算机去读的时候怎么知道哪个字符是用 ASCII 哪个是用 Unicode。

### UTF
就是因为这个原因所以出现了类似 UTF-8 这种编码方式,其他还有 UTF-16, UTF-32, GB2312 等等,但是其中 UTF-8 是用得最广的。

上面说的这些只是编码方式,和 Unicode 这种字符集的关系是,UTF-8 是 Unicode 的一种实现方式。也可以说是一种存储方式。

UTF-8 是一种变长的编码方式,可以使用1~4个字节表示一个符号,极大的减少空间浪费,他的编码原理简单点说是这样的
- 对于单字节的符号,第一位是0,后面7位是这个字符的 ASCII 码
- 对于多个字节的符号,需要用几个字节那么就以几个1开头,紧跟着一个0,后面每个字节都以10开头

UCS-2编码(16进制)| UTF-8 字节流(二进制)
---|---
0000 - 007F | 0xxxxxxx
0080 - 07FF | 110xxxxx 10xxxxxx
0800 - FFFF | 1110xxxx 10xxxxxx 10xxxxxx

### 乱码
乱码其实就是编码和解码的时候用了不同的字符集,比如我用UTF-8编码之后的字符你用GBK去解码,自然不能拿到我给你的字符,那么计算机是怎么知道他应该用什么编码方式来打开这个文件呢,一般是检测文件头,感觉前几个字符来匹配,猜测,实在不行就提示用户选择


有一个很奇怪的梗就是在记事本中输入`联通`然后保存关闭再打开,发现出现的是两个乱码,然后有人说这就是联通不如移动的原因。。

根本原因是在文本文件中输入中文的时候使用的是默认系统编码,在中文系统中是 GB 系列的编码,所以`联调`两个字在保存的时候会变成
```
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
```
第一二个字节和三四个字节正好符合 UTF-8 的编码格式,那么记事本会误认为是 UTF-8 的文件,所以就产生了乱码


### /引子
`\u2028`的问题


在 Unicode 还没出来的时候,有两个团队不约而同的想搞一个统一的字符集,分别是1988年成立的 Unicode 团队和1989年成立的 UCS 团队,而且 UCS 团队先于 Unicode 团队在1990年公布了第一套编码方式 UCS-2 使用2个字节来表示已经有了的字符,然后在 1991 年两个团队觉得只需要一套就够了所以合并了字符集。比 UCS-2 更高级的编码方式是 1996 年诞生的 UTF-16,而且 UTF-16 是 UCS-2 的超集。

JavaScript 是1995年Brendan Eich用了10天设计的,那个时候只有 UCS-2 这一种编码方式可以用,所以在 JavaScript 中只能用 UCS-2 编码,造成了所有字符在这门语言中都是2个字节,如果是4个字节的字符会被当成2个字节处理

JSON 是一种语法来序列化一些对象,它基于 JavaScript 但不同的是

类型 | 区别
---|---
对象和数组| 属性名称必须是双引号字符串;最后一个属性后不能有逗号。
数值 | 前导零是禁止的(在 JSON.stringify 零将被忽略,但在 JSON.parse 它将抛出 SyntaxError);小数点后必须至少有一位数字。
字符串 | 只有有限的一些字符可能会被转义;禁止某些控制字符;Unicode 行分隔符 (U+2028)和段分隔符 (U+2029)被允许 ; 字符串必须是双引号。请参见以下示例,其中 JSON.parse() 能够正常解析,但把它当作JavaScript解析时会抛出 SyntaxError 错误:let code = '"\u2028\u2029"';JSON.parse(code); // 工作正常eval(code); // 失败


简单的说JSON自身是支持这个特殊的 Unicode 字符的,但是在用 JavaScript 去执行 JSON 方法的时候,会把这些特殊的字符解析成换行,所以就报错了。这可能是JSON设计的时候的一个失误吧,导致他并不严格是 JavaScript 的一个子集。

### 参考
[Unicode与JavaScript详解](http://www.ruanyifeng.com/blog/2014/12/unicode.html)

[字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html)

[程序员趣味读物:谈谈Unicode编码](http://pcedu.pconline.com.cn/empolder/gj/other/0505/616631_all.html#content_page_2/)

[JSON: The JavaScript subset that isn't](http://timelessrepo.com/json-isnt-a-javascript-subset/)

[JSON](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON)

0 comments on commit 7edc93e

Please sign in to comment.