我是三钻,一个在《技术银河》中等你们一起来终生漂泊学习。
点赞是力量,关注是认可,评论是关爱!下期再见 !
这个笔记是基于 Winter 老师的 《重学前端》的内容总结而得。
JavaScript 中最小的结构,同学们已知的有什么呢?我想同学们都应该会想到一些东西,比如一些关键字,数字 123,或者 String 字符等等。这里我们从最小的单位,字面值和运行时类型开始讲起。
这里分为语法(Grammer)和运行时(Runtime)两个部分。
语法(Grammer)
这些都是我们用来组成 JavaScript 语言的最小元素/单位,这是通过我们的字面值,比如一个数字类型的字面值
123
、1.1
、2.2
,然后配合上我们的变量和if
、else
关键字,以及一些符号、空白符、换行符等。它们虽然不会产生一些语言上的作用,但是可以让我们整个语言的格式更好看一些。
运行时(Runtime)
语法中的元素实际上最终反映到运行时(Runtime)中,字面值一共有五六种写法,对应到 JavaScript 的 7 种基本类型中的几种。另外我们的变量实际上对应到运行时的 Execution Context 的一些存储变化。最终这些语法都会造成运行时的改变。
Null
和Undefined
经常被我们的前端工程师被混起来使用, 所以说我们不会把 Undefined 的值用来赋值,我们只会检查一个变量的值是否是 Undefined。但是客观上来说 JavaScript 是允许进行 Undefined 赋值的。建议大家一定要克制,凡是我们进行过赋值的我们尽量都用 Null,而不是用 Undefined。
真正编程中会有5种比较常用的基本类型,Number
、String
、Boolean
、Object
、Null
。
在我们的概念里面 Number 就是一个数字,准确的说 JavaScript 中的 Number 对应到我们的概念里面的有限位数的一个小数。
Number 按照它的定义是 double float
,双精度浮点数类型。很多时候我们对 Number 的理解都在表面,所以我们要理解 IEEE754
定义的 Float 标准,我们才能真正理解 JavaScript 里面的 Number。
Float
表示浮点数,意思是它的小数点是可以来回浮动的。他的基本思想就是把一个数字拆成它的 “指数” 和 “有效位数”。这个数的有效位数决定了浮点数表示的精度,而指数决定浮点数表的的范围。
浮点数还有一个可以表示的符号,它可以是正负,总共占一位。0
为正数,1
为负数。
IEEE754 定义的浮点数中有以下:
Number 的语法 在 2018 年的标准里面有 4 个部分:
0
、0.
、.2
、1e3
0b111
—— 以 0b
开头,可以用 0 或者 10o10
—— 以 0o
开头,可以用 0-70xFF
—— 0x
开头,可以用 0-9,然后 A-F用
十进制
来表示 Number,比如说我们现在有一个浮点数205.75
,那么用十进制来表示呢?
数学中的表示就是: 205.75 = 2 × 100 + 0 × 10 + 5 × 1 + 7 × 0.1 + 5 × 0.01 205.75 = 2\times100 + 0\times10 + 5\times1 + 7\times0.1 + 5\times0.01 205.75=2×100+0×10+5×1+7×0.1+5×0.01
换成计算机的十进制就是: 205.75 = 2 × 1 0 2 + 0 × 1 0 1 + 5 × 1 0 0 + 7 × 1 0 − 1 + 5 × 1 0 − 2 205.75 = 2\times10^2 + 0\times10^1 + 5\times10^0 + 7\times10^{-1} + 5\times10^{-2} 205.75=2×102+0×101+5×100+7×10−1+5×10−2
上面两个公式得出的结果都是:200 + 0 + 5 + 0.7 + 0.05 = 205.75
在上面的
IEEE754
中的每一位数都是二进制的,而一共是有64 位
。那二进制的5.75
又是怎么表示呢?
计算机中的二进制表示: 5.75 = ( 1 × 2 2 + 0 × 2 1 + 1 × 2 0 ) + ( 1 × 2 − 1 + 1 × 2 − 2 ) 5.75 = (1\times2^2 + 0\times2^1 + 1\times2^0) + (1\times2^{-1} + 1\times2^{-2}) 5.75=(1×22+0×21+1×20)+(1×2−1+1×2−2)
这里有一个浮点数中的老生常谈的
0.1
的浮点数的精度丢失问题,到底是怎么回事呢?我们一起来用二进制来表示看一下是怎么回事吧!
首先我们把二进制中一些精度位数的值先列出来,这些会在后面表示我们 0.1
的时候用到。
1 2 = 0.5 1 4 = 0.25 1 8 = 0.125 1 16 = 0.0625 1 32 = 0.03125 1 64 = 0.015625 1 128 = 0.0078125 1 256 = 0.00390625 1 512 = 0.001953125 1 1024 = 0.000976562 \frac12 = 0.5 \\ \frac14 = 0.25 \\ \frac18 = 0.125 \\ \frac1{16} = 0.0625 \\ \frac1{32} = 0.03125 \\ \frac1{64} = 0.015625 \\ \frac1{128} = 0.0078125 \\ \frac1{256} = 0.00390625 \\ \frac1{512} = 0.001953125 \\ \frac1{1024} = 0.000976562 \\ 21=0.541=0.2581=0.125161=0.0625321=0.03125641=0.0156251281=0.00781252561=0.003906255121=0.00195312510241=0.000976562
使用二进制的时候,其实我们是用所有 2 次方的结果值相加得到我们的数字的。因为这里是从,小数点开始所以我们从 2 − 1 2^{-1} 2−1(也就是 1 2 \frac12 21)开始。然后我们用上面的 2 次方表来找到可相加的数值,让相加的数值可以等于,或者最接近
0.1
。
0.1
所以都是 0 × 二 次 方 的 数 值 0\times二次方的数值 0×二次方的数值,直到 1 16 \frac1{16} 161 开始是可以相加的。这里 0.01 − 1 16 = 0.0375 0.01 - \frac1{16} = 0.0375 0.01−161=0.0375,所以加法的结果与 0.1
还差 0.0375。0.1
的。0.09375
0.1 = 0 × 1 2 + 0 × 1 4 + 0 ∗ 1 8 + 1 × 1 16 + 1 × 1 32 = 0.09375 0.1 = 0\times\frac12 + 0\times\frac14 + 0*\frac18 + 1\times\frac1{16} + 1\times\frac1{32} = 0.09375 0.1=0×21+0×41+0∗81+1×161+1×321=0.09375
0.1
,我们就需要继续往下找,首先 0.1 − 0.09375 = 0.00625 0.1 - 0.09375 = 0.00625 0.1−0.09375=0.00625,所以我们需要继续往下找小于或者等于这个数的2的次方。0.1 = 0 × 1 2 + 0 × 1 4 + 0 ∗ 1 8 + 1 × 1 16 + 1 × 1 32 + 0 × 1 64 + 0 × 1 128 + 1 × 1 256 + 1 × 1 512 = 0.999609375 0.1 = 0\times\frac12 + 0\times\frac14 + 0*\frac18 + 1\times\frac1{16} + 1\times\frac1{32} + 0\times\frac1{64} + 0\times\frac1{128} + 1\times\frac1{256} + 1\times\frac1{512} = 0.999609375 0.1=0×21+0×41+0∗81+1×161+1×321+0×641+0×1281+1×2561+1×5121=0.999609375
如果我们把上面的公式换成二进制就是:00110011
如果我们一直往下寻找,并且相加,我们会发现二进制会一直循环
0011
这个规律。但是因为 IEEE754 里面双精度的精度位最多只有52
位。所以就算我们一直放满 52 位,也无法相加得到0.1
这个数值,只能越来越接近。所以最后再二进制中 0.1 是一定会有至少一个epsilon
的精度丢失的。(这里的解说,是通过 “代码会说话” UP 主的视频学习所得。想看视频解说的可以 点这里观看)
String 对大家来说就是一个文本,写字读字大家都会,表示字我们在代码中加上 '
(引号)就是 String了。 但是 String 还是有一些知识需要我们去理解透测的。
首先我们要介绍的是 Character
(字符) 相关的知识。String 在英文里的意思是串成一串的意思,在计算机领域里面这个字符串,就是把字符串在一起,那串着的就当然是字符了。
那字符在英文里面就是 Character
,但是字符在计算机里面是没有办法表示的。比如我们看到的字母 A
、中文的 中
等这些字符都是一个形状,其实这些都是字形,我们认为字符其实是一个抽象的表达。然后结合字体才会变成一个可见的形象。
那计算机里怎么表示 Character
呢?它是用一个叫 Cold Point(码点)
来表示 Character
的。Code Point 其实也不是什么复杂的东西,就是一个数字。比如说我们规定 97
就代表 A
,只要我们结合一定的类型信息,我们只要用 97 和字体里面的信息,就可以把 A
找出来并且画到屏幕上。
那问题来了,计算机怎么存储这个 97
这个数字呢?我们都知道计算机当中存储的基本单位是 字节(Bytes)
。数字和英文只需要一个字节就能存了,但是中文一个字节就不够用了。所以要理解透彻 String 呢,我们就需要理解 字符(Character)
、码点(Code Point)
和 编译(Encoding)
,这三个概念了。
这里我们就讲讲这些字符集的来由和各自的特征。
GB2312
、GBK(GB3000)
、GB18030
GB18030
, 这个就补上了所有的缺失的字符了因为 ASCII 字符集本身最多就占一个字节,所以说它的编码和码点事一模一样的,我们是没有办法做出比一个字节更小的编码单位。所以 ASCII 不存在编码问题,但是 GB
、Unicode
都存在编码问题。因为 Unicode
结合了各个国家的字符,所以它存在一些各种不同的编码方式。
UTF-8 (全称:Unicode Transformation Format 8-bit)是一种针对 Unicode 的可变长字符编码,也是一种续码。里面的
8
是代码 8 个字节。
我们一起来通过理解 String 是怎么编译 UTF-8 的,从而来深入认识 UTF-8 背后的原理。
我们要转换 String 之前,我们要知道 UTF-8 的编码结构长度,它是根据某单个字符的大小
来决定的。
在 JavaScript 中,我们可以使用
charCodeAt
来查看一下字符大小,我们会发现:英文占的是 1 个字符,汉字占的是 2 个字符。
然后单个 Unicode 字符编码之后最大的长度是 6 个字节,以下就是每个字符大小占用多少个字节的一个换算:
这里呢,英文和英文字符的 Unicode 码点是
0 - 127
,所以英文在 Unicode 和 UTF-8 中的长度和字节是一致的。都是只占用一个字节。但是中文汉字的 Unicode 码点范围是0x2e80 - 0x9fff
,所以汉字在 UTF-8 中的最长长度是 3 个字节。
1、获取字符 Unicode 值大小
let string = '中';
let charCode = str.charCodeAt(0);
console.log(charCode); // 返回:20013
所以这里我们获取到,汉字字符 中
的字符大小是 20013
。
2、判断字符 UTF-8 长度
上一步我们获得字符的大小,根据 Unicode 的长度区间换算出这个字符占用多少个字节。根据我们的上面的表格,我们可以看出字符 中
落在 2048 - 0xFFFF
这个区间,那就是占 3 个字节。
3、补码
在转换成 UTF-8 时,我们就需要用补码的规则进行转换。首先我们看看 UTF-8 中的补码规则:
这里面的
x
代码补位的位置。在一个字节的时候是特殊的,直接用0
控制位开头,然后后面补位有 7 个。其他都是 n 个1
+0
开头。这里有一个规律。从 2 个字节开始,头一个字节中的1
的个数就是字节的个数,比如 2 个字节的,就是 2 个1
+0
开头,3 个字节的就是 3 个1
+0
开头。然后后面的字节都是10
开头,接着的都是补位。
现在知道补位的规则,那如果是一个字符 “A” 我们应该怎么填写这些补位,从而获得 UTF-8 编码呢?
200013
2048 - 0xFFFF
的区间,所以 “中” 是占用三个字节的1110xxxx 10xxxxxx 10xxxxxx
01001110 00101101
x
的位置)11100100 10111000 10101101
的 3 个字节的 UTF-8 编码,转换成 十六进制
,得到 0xE4 0xB8 0xAD
为了证明我们转换的结果是正确的,我们可以用 node.js 中的 Buffer 来验证一下。
var buffer = new Buffer('中');
console.log(buffer.length); // => 3
console.log(buffer); // =>
// 最终得到三个字节 0xe4 0xb8 0xad
这部分内容的参考了 “张亚涛” 的 《通过javascript进行UTF-8编码》
早年 JavaScript 支持两种写法:
双引号和单引号字符串其实没有什么区别,它们之间的区别仅仅是在单双引号的使用下,双引号里面可以加单引号作为普通字符,而单引号中可以加双引号作为普通字符。
引号中会有一些特殊字符,比如说 “回车” 就需要用 \n
、“Tab” 符就是 \t
。在双引号当中如果我们想使用双引号这个字符的时候,同样我们可以在前面加上反斜杠: \"
。没有特殊含义的字符,就是在它们前面加上反斜杠。(然后反斜杠自身也是 \\
就可以了)
这些就是字符串里面的 “微语法”。
到了后面比较新的 JavaScript 版本就加了 “反引号” —— `abc`,也就是我们键盘上 1
键左边的按键。目前来说反引号这个符号是不太常用,也正因为这个字符不常用,所以它非常适合做语法的结构。
反引号要比早年的双单引号更加强大,里面可以解析出回车、空格、Tab等字符。特别是可以在里面插入 ${变量名}
,直接就可以在字符串内插入变量拼接。只要我们在里面不用反引号,我们可以随便加什么都行。
那么 JavaScript 引擎是怎么编译反引号和分解里面的变量的呢?
这里我们举个例子 `ab${x}abc${y}abc`
在这个反引号中,JavaScript 引擎会把它拆成 3 份,`ab${ 、`}abc%{ 、}ab`
反引号
后面跟着一个字符串,然后后边一个 $ 符号
和左大括号
,这才是一对的括号关系,它们引入了字符串右打括号
,后面跟着一串字符
,最后是 $ 符号
和左打括号
,这一个整个也是一对括号关系开始
、中间
、结束
,当然还有前后的反引号,但是中间我们是不插变量的这样形式。这里其实就是用 4 种 token 形成了一种 String 模版的语法结构(String Template)。案例 —— 这里我们尝试使用正则表达式,来匹配一个单引号/双引号的字符串:
// 双引号字符正则表达式
"(?:[^"\n\\\r\u2028\u2029]|\\(?:[''\\bfnrtv\n\r\u2029\u2029]|\r\n)|\\x[0-9a-fA-F]{
2}|\\u[0-9a-fA-F]{
4}|\\[^0-9ux'"\\bfnrtv\n\\\r\u2028\u2029])*"
// 单引号字符正则表达式
'(?:[^'\n\\\r\u2028\u2029]|\\(?:[''\\bfnrtv\n\r\u2029\u2029]|\r\n)|\\x[0-9a-fA-F]{
2}|\\u[0-9a-fA-F]{
4}|\\[^0-9ux'"\\bfnrtv\n\\\r\u2028\u2029])*'
回车
、斜杠
、\n\r
2028
和 2029
就是对应的分段
和分页
\x
和 \u
两种转义方法bfnrtv
这几种特殊的字符,还有上面考虑到的因素即可。其实就是 true
和 false
, 这个类型真的是非常简单的类型,如果没有和计算联合起来用,就真的是一个很简单的类型。
这两个类型都是大家日常会接触的,其实都表示空值。不同的是:
Null
表示有值,但是是空Undefined
语义上就表示根本没有人去设置过这个值,所以就是没有定义我们要注意 Null
其实是关键字 ,但是 Undefined
其实并不是关键字。
Undefined 是一个全局变量,在早期的 JavaScript 版本里全局的变量我们还可以给他重新赋值的。比如我们把 Undefined
赋值成 true,最后造成了一大堆地方出问题了。但是大家一般都没有那么顽皮,这么顽皮的人一般都被公司开掉了哈。
虽然说新版本的 JavaScript 无法改变全局的
Undefined
的值,但是在局部函数领域中,我们还是可以改变Undefined
的值的。例如一下例子:
function foo() {
var undefined = 1;
console.log(undefined);
}
那么 null
是一个关键字,所以它就没有这一类的问题,如果我们给 Null 赋值它就会报错了。
function foo() {
var null = 0;
console.log(null);
}
这里就说一下,我们怎么去表示
Undefined
是最安全的呢?在开发的过程中我们一般不用全局变量,我们会用void 0
来产生Undefined
,因为void
运算符是一个关键字,void
后面不管跟着什么,他都会把后面的表达式的值变成Undefined
这个值。那么void 0
、void 1
、void
的一切都是可以的,一般我们都会写void 0
,因为大家都这么写,大家说一样的话比较能够接受。
我们还有一个 Symbol 和 Object 还没有讲到,这个就结合在一起在一篇文章中一起讲了。敬请期待!
小伙伴们可以查看或者订阅相关的专栏,从而集中阅读相关知识的文章哦。
《数据结构与算法》 — 到了如今,如果想成为一个高级开发工程师或者进入大厂,不论岗位是前端、后端还是AI,算法都是重中之重。也无论我们需要进入的公司的岗位是否最后是做算法工程师,前提面试就需要考算法。
《FCC前端集训营》 — 根据FreeCodeCamp的学习课程,一起深入浅出学习前端。稳固前端知识,一起在FreeCodeCamp获得证书
《前端星球》 — 以实战为线索,深入浅出前端多维度的知识点。内含有多方面的前端知识文章,带领不懂前端的童鞋一起学习前端,在前端开发路上童鞋一起燃起心中那团火