带你读红宝书-第三章-语言基础
稍安勿躁
经过前边两个氛围轻松的章节,接下来就是直接接触JavaScript的本身了;想想还有点小刺激呢我会尽量以轻松的氛围把我读到的讲出来,避免你的枯燥.当然,也可以选择走马观花的过一遍啦
语法
对于经常写js的同学来说,可能觉得自己对js的语法早就了如指掌了,那么我们还是来看看有什么新的地方值得讨论的吧:
区分大小写
js世界里的一切都需要区分大小写,无论是变量,函数,属性还是操作符都是需要评估大小写的,废话不多说,上代码更能激起共鸣
const test='1'const Test='2'console.log(test) //1这里的test和Test是不同的变量~
标识符规则
标识符是指变量,函数,属性,函数参数的名称,它遵循下边的规则:
- 第一个字符必须是字母,下划线,或者
$ - 剩下的字符可以是字母,下划线,或者
$
标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是Unicode的字母字符,如 À 和 Æ(但不推荐使用)。
上面这句话是红宝书第四版的原文.基于此,我还测试了使用我们的中文来定义变量,发现也是可以正常运行的.
const 哈哈 = 123;console.log(哈哈)//123哈哈,如果这样,我们能把保留的关键字都替换为中文,那…这,就是易语言!🤪
在我们的日常工作中,标识符的最佳实践为驼峰命名,形如firstSecond,doSomethingImportant;虽然你可能觉得这是共识,自然而然的,不过这并不是强制性的.
注释
这个就不用多说了吧,相信大家都已经熟练掌握了,上个代码就完事
// 单行注释// console.log('hi')// 多行注释/* const run = () => { console.log('run run run~');} */严格模式
严格模式是es5新增的一种运行模式,它可以将之前es3一些不规范的地方进行警告或者抛出错误,现在很多情况下,我们的工程都会默认开启这个模式来规避一些隐患
这个模式可以使用两种方式启用,局部和全局,下面展示一下用法
我们只需要在脚本顶部书写"use strict";,即可全局开启严格模式
// 全局"use strict";如果我们希望严格模式只在某个局部作用域内发挥作用,那么我们可以在函数体内的顶部书写"use strict";
// 局部function run(){ "use strict";}至于严格模式开启之后的一些限制,后续会介绍到的
语句
js中的语句通常是以’;‘结束的,在js的世界里其实是可以没有分号的,比如Vue作者尤雨溪,他就是习惯不加分号的,这在社区中一直是一个讨论的话题~
红宝书里是推荐加上分号的,原文:
即使语句末尾的分号不是必需的,也应该加上。记着加分号有助于防止省略造成的问题,比如可以避免输入内容不完整。此外,加分号也便于开发者通过删除空行来压缩代码(如果没有结尾的分号,只删除空行,则会导致语法错误)。加分号也有助于在某些情况下提升性能,因为解析器会尝试在合适的位置补上分号以纠正语法错误。
注意,这里的语句不等于表达式哈~
关键字与保留字
关键字是指具有特殊用途的字符,比如流程控制的if,while等;ECMA-262的第六版关键字如下:
break do in typeofcase else instanceof varcatch export new voidclass extends return whileconst finally super withcontinue for switch yielddebugger function thisdefault if throwdelete import try哪什么是保留字呢?保留字是未来关键字的预备军,和关键字一样不能作为标识符,这些保留字未来有可能会成为关键字,ECMA-262的第六版关键字如下:
始终保留:enum严格模式下保留:implements package publicinterface protected staticlet private模块代码中保留:await这些词汇虽然不能用作标识符,但是却可以作为对象的属性名,但最好不要这样干,以确保你的代码能够兼容过去和未来的es版本.
变量
js是动态语言,这意味着你声明的变量可以用来保存任何类型的数据.红宝书里准确的把变量比作值的占位符.js发展到至今,我们有三种方式来声明我们的变量:var,const,let;而现在的js运行时几乎全部都支持了let|const,再加上前端工程化的推动,也可以很轻松实现向下兼容,综合来看var的方式可以成为过去式了,我们喜欢且应该使用let,const这两种方式来声明我们的变量.
var
虽然这种方式已经成为过去式,但是了解它更有助于我们了解js这门语言本身.
通常我们可以这样声明一个变量:
var a;这样一个变量a就声明好了,如果声明时没有赋值,那么你它的值是undefined
由于动态语言的特性,我们可以这样用
var a=1a="hello"这样是完全合乎js语言本身的,但是不推荐这样做,变量的最佳实践之一就是其保存的数据类型从始至终不发生改变.
作用域
使用var时的最大诟病来了,来慢慢看吧
function f() { var a = 1}console.log(a); //a is not definedvar的作用域为函数作用域,函数执行完成退出时就会被销毁,很合理是吧,但是它存在一个变量提升的问题
function f() { console.log(a); //undefined var a = 1}f()当你执行这段代码时,会发现一切正常,这就是因为js的变量提升,它会将使用var声明的变量提升到函数顶部,它可以被转换为下面等价代码:
function f() { var a; console.log(a); //undefined a = 1;}f()var还存在一个可重复声明的bug?:
var a=1;var a="a";var a=!!0;console.log(a) //falsewow~一切正常.正是由于var存在这一系列的问题,包括变量提升,可重复声明等一系列问题,所以我们急需要两位得力成员:let,const
let
let除了解决了上面var的问题,而且作用域变为了块级作用域!我们用一个典型的例子来说明它为我们带来的便利
for (var i = 0; i < 4; i++) { setTimeout(() => { console.log(i) // 3,3,3,3 }, 1000)}在以前我们如果在一个for循环里延迟打印索引,你会发现索引打印出来永远时最大索引,这是因为我们异步打印的原因,setTimeout创建的宏任务会在我们主线代码执行完成之后再执行,等主线任务全部执行完延迟任务再去读取的时候,每次都是读取到的最大索引.那咋办?我们可以使用一个闭包来解决这个问题,我们把每次循环的索引保存进一个函数内,然后延迟函数引用函数内保存的变量,这样就可以解决这个问题了.
for (var i = 0; i < 4; i++) { ((i) => { setTimeout(() => { console.log(i) // 0,1,2,3 }, 1000) })(i)}但是如果你使用let来解决这个问题,那这个巴适了,改动成本几乎为0
for (let i = 0; i < 4; i++) { setTimeout(() => { console.log(i) // 0,1,2,3 }, 1000)}对了,还没说明什么是块级,可以这么理解:{}花括号内的作用域就是块级作用域
wow~这也太爽了!这是因为啥呢?因为let是块级作用域,每次循环执行时都会重新创建一个块.等价代码贴出来你可能会明白一切
for (let i = 0; i < 4; i++) { // let i;这是被隐藏在for()的作用域 setTimeout(() => { console.log(i) // 0,1,2,3 }, 1000)}// {let i = 0}// {let i = 1}// {let i = 2}// {let i = 3}暂时性死区问题
另外关于变量提升这个问题,其实let也会存在变量提升,js引擎在解析代码时也会留意let,但是我们为什么无法提前访问它呢?那是因为在声明之前,会有一个暂时性死区.在死区存在区间(也就是被声明之前),我们无法使用任何方式来访问它.
还有一个全局变量的问题
我们在全局作用域使用var声明变量时,变量会自动挂载到全局对象下作为一个属性,比如浏览器环境:
var a = 1console.log(window.a); // 1换let,它并不会自动成为全局对象的一个属性
let a = 1console.log(window.a); // undefined条件声明和无法重新声明
条件声明是什么意思呢?指在符合某一条件时才声明这个变量,通常适用于全局环境中声明变量,由于目前模块化的盛行,这个东西可以抛弃,而且红宝书里也提了这是一种反模式,原文:
注意 不能使用 let 进行条件式声明是件好事,因为条件声明是一种反模式,它让程序变得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式。
let是无法重新声明变量的,以下两种方式都是不被允许的
var a = 1let a = 1
let b = 1var b = 1const
由于大部分特性和let相同,我们这里只说一下区别吧.const声明的变量时必须赋予初值且后续无法重新赋值.看起来貌似const似乎声明后就无法改变,事实也是如此,但是如果你赋予的是一个引用类型,那么其內部的属性等是可以被改变的,而且也并没有范围const的原则.
那么如何区分let,const的使用场景呢?我的建议是能用const就用const,不行再用let.
数据类型
js有六种简单的数据类型,他们分别是:
- Undefined
- Null
- Boolean
- Number(包含BigInt)
- String
- Symbol
除此之外还有一种复杂类型,叫做object,它是一种无序的名值对的集合.严格意义上来讲的话,js里除了六种简单原始类型外,其余都可以归为object对象类型.
typeof 检测数据类型的方式之一
由于js的数据类型是一个动态的,那就需要有一种可以判断当前数据类型的手段,typeof 操作符就是其中之一,使用方式如下:
typeof variable该操作符将返回以下类型:
- undefined 未定义
- boolean 布尔值
- string 字符串
- number 数值
- object 对象
- function 函数
- symbol 符号
特别说明<你可能也发现了>你可能也发现了>,返回值里为什么没有null呢?这是来自于js的一个远古bug.红宝书里也有解释到(但这其实是一个委婉的说法😀)<这是因为特殊值>这是因为特殊值> null 被认为是一个对空对象的引用。
Undefined
这个类型是一个特殊意义的值,只有一个值;如果你声明了一个变量,但是并没有赋值给它,那么它的值就是undefined.
书中还说到一种情况,就是如果你希望你的比那辆显式的声明一个undefined变量,使用形如下边的方式是不推荐的
let a=undefined;其实我们一边编码中如果希望显式的声明,可以使用下边的方式,至少看上去不是那么别扭,而且更安全
let a=void 0;Null
同样是一个特殊类型的值,也只有一个值.null可以用来表示一个空指针对象,所以如果你对它使用typeof操作符,得到的结果会是一个object类型.(其实是一个bug 0.0)
如果你确信将来这个变量会是一个对象类型的值时,你就可以将你的变量初始化为一个null值.
let a=null;a={}Boolean
这个没啥好说的吧,大家使用都很频繁.有true和false两个值.
Number
ECMAScript 中最有意思的数据类型或许就是 Number 了。
红宝书开篇就说了这句话,换句话说就是number类型可能是js里最古怪行为的类型了,哈哈哈
严格意义来说js中是不分浮点和整数类型的
浮点
要定义浮点类型,只需要数字中包含小数点就行
let a=1.1let b=0.1let c=.1 // 不推荐,应该添0 > 0.1如果你只需要定义一个整数,那么就不要使用浮点类型,因为浮点类型占用的内存空间是是整数类型的两倍,js引擎一般来说也会尽量帮你把浮点类型抓换为整数类型,形如1.0这种会被直接转换为1的整数类型.对于非常大或非常小的数值,浮点数可以使用科学计数法来表示(貌似在我的职业生涯中还没有相关的实践 =.=).
浮点值得精确度最高可达17位小数,由于占用双倍内存,这里可以倒推整数类型的范围,应该同样是是17位.
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














