最近原创文章😊:
- 《了不起的 Webpack HMR 学习指南(含源码分析)》
- 《了不起的 Webpack 构建流程学习指南》
- 《你不知道的 WeakMap》番外篇
- 《你不知道的 Blob》番外篇
- 《了不起的 tsconfig.json 指南》
- 《200行JS代码,带你实现代码编译器》
前言
最近与部门老大一起面试了许多前端求职者,其中想换个学习氛围较好的人占多数,但良好的学习氛围也是需要一点点营造出来的🌺。
为此我们组建了我们团队内部的“现代 JavaScript 突击队”,第一期学习内容为《现代 JavaScript 教程》系列,帮助小组成员系统地进行学习巩固,并让大家养成系统性学习和输出学习总结的学习方式。
本文作为我输出的第一部分学习总结,希望作为一份自测清单,帮助大家巩固知识,温故知新。
这里也下面分享我们学习小组的“押金制度”和“押金记录表”🍀
接下来开始分享自测清单的内容。
一、Hello World!
1. 脚本引入方式
JavaScript 脚本引入方式有两种:
<script>
标签插入脚本;<script>
标签src
设置脚本地址。
2. script 标签属性
<script>
标签有以下常用属性:
2.1 src
src
:指定外部脚本的URI, 如果设置了 src
特性,script 标签内容将会被忽略;
<script src="example-url.js"></script>
2.2 type
type
:指定引用脚本的语言,属性值为 MIME 类型,包括text/javascript
, text/ecmascript
, application/javascript
, 和application/ecmascript
。如果没有定义这个属性,脚本会被视作JavaScript。
ES6 新增了属性值 module
,代码会被当做 JavaScript 模块。
<script type="text/javascript"></script>
2.3 async
async
规定一旦脚本可用,则会异步执行。 注意:async 属性仅适用于外部脚本(只有在使用 src 属性时)。 有多种执行外部脚本的方法: 如果 async="async"
:脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行); 如果不使用 async
且 defer="defer"
:脚本将在页面完成解析时执行; 如果既不使用 async
也不使用 defer
:在浏览器继续解析页面之前,立即读取并执行脚本;
<script async="async"></script>
2.4 defer
defer
属性规定是否对脚本执行进行延迟,直到页面加载为止。
如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script>
标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。
<script defer="defer"></script>
详细介绍可以阅读《MDN <script>
章节 》。
二、代码结构
1. 语句
语句是执行行为(action)的语法结构和命令。如: alert('Hello, world!')
这样可以用来显示消息的语句。
2. 分号
存在分行符时,多数情况下可以省略分号。但不全是,比如:
alert(3 + 1 + 2);
建议新人最好不要省略分号。
3. 注释
单行注释以两个正斜杠字符 //
开始。
// 注释文本 console.log("leo");
多行注释以一个正斜杠和星号开始 “/*”
并以一个星号和正斜杆结束 “*/”
。
/* 这是多行注释。 第二行注释。 */ console.log("leo");
三、现代模式,"use strict"
1. 作用
JavaScript 的严格模式是使用受限制的 JavaScript 的一种方式,从而隐式地退出“草率模式”。
"use strict"
指令将浏览器引擎转换为“现代”模式,改变一些内建特性的行为。
2. 使用
通过在脚本文件/函数开头添加 "use strict";
声明,即可启用严格模式。 全局开启严格模式:
// index.js "use strict"; const v = "Hi! I'm a strict mode script!";
函数内开启严格模式:
// index.js function strict() { 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); }
3. 注意点
"use strict"
需要定义在脚本最顶部(函数内除外),否则严格模式可能无法启用。- 一旦进入了严格模式,就无法关闭严格模式。
4. 体验
启用 "use strict"
后,为未定义元素赋值将抛出异常:
"use strict"; leo = 17; // Uncaught ReferenceError: leo is not defined
启用 "use strict"
后,试图删除不可删除的属性时会抛出异常:
"use strict"; delete Object.prototype; // Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
详细介绍可以阅读《MDN 严格模式章节 》。
四、变量
1. 介绍
变量是数据的“命名存储”。
2. 使用
目前定义变量可以使用三种关键字:var / let / const。三者区别可以阅读《let 和 const 命令》 。
let name = "leo"; let name = "leo", age, addr; let name = "leo", age = 27, addr = "fujian";
3. 命名建议
变量命名有 2 个限制:
- 变量名称必须仅包含字母,数字,符号
$
和_
。 - 首字符必须非数字。 变量命名还有一些建议:
- 常量一般用全大写,如
const PI = 3.141592
; - 使用易读的命名,比如
userName
或者shoppingCart
。
4. 注意点
- JavaScript 变量名称区分大小写,如变量
leo
与Leo
是不同的; - JavaScript 变量名称允许非英文字母,但不推荐,如
let 平安 = "leo"
; - 避免使用
a
、b
、c
这种缩写。
五、数据类型
JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据:
var foo = 42; // foo is a Number now foo = "bar"; // foo is a String now foo = true; // foo is a Boolean now
详细介绍可以阅读《MDN JavaScript 数据类型和数据结构 》。
1. 八大数据类型
前七种为基本数据类型,也称为原始类型(值本身无法被改变),而 object
为复杂数据类型。 八大数据类型分别是:
number
用于任何类型的数字:整数或浮点数,在 ±2 范围内的整数。bigint
用于任意长度的整数。string
用于字符串:一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。boolean
用于true
和false
。null
用于未知的值 —— 只有一个null
值的独立类型。undefined
用于未定义的值 —— 只有一个undefined
值的独立类型。symbol
用于唯一的标识符。object
用于更复杂的数据结构。 每个类型后面会详细介绍。
2. 检测数据类型
通过 typeof
运算符检查:
- 两种形式:
typeof x
或者typeof(x)
。 - 以字符串的形式返回类型名称,例如
"string"
。 typeof null
会返回"object"
—— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个object
。
typeof "leo" // "string" typeof undefined // "undefined" typeof 0 // "number" typeof NaN // "number" typeof 10n // "bigint" typeof true // "boolean" typeof Symbol("id") // "symbol" typeof [1,2,3,4] // "object" typeof Math // "object" (1) Math 是一个提供数学运算的内建 object。 typeof null // "object" (2) JavaScript 语言的一个错误,null 不是一个 object。null 有自己的类型,它是一个特殊值。 typeof alert // "function" (3) alert 在 JavaScript 语言中是一个函数。
六、类型转换
JavaScript 变量可以转换为新变量或其他数据类型:
- 通过使用 JavaScript 函数
- 通过 JavaScript 自身自动转换
1. 字符串转换
通过全局方法 String()
将**其他类型数据(任何类型的数字,字母,布尔值,对象)**转换为 String 类型:
String(123); // "123" // Number方法toString()/toExponential()/toFixed()/toPrecision() 也有同样效果。 String(false); // "false" // Boolean方法 toString() 也有同样效果。 String(new Date()); // "Sun Jun 07 2020 21:44:20 GMT+0800 (中国标准时间)" // Date方法 toString() 也有同样效果。 String(leo);
2. 数值转换
通过以下几种方式能将其他类型数据转换为 Number 类型:
- 一元运算符
+
const age = +"22"; // 22
Number
方法
const age = Number("22"); // 22 Number.parseFloat("22"); // 22 Number.parseInt("22"); // 22
- 其他方式转 Number 类型
// 布尔值 Number(false) // 返回 0 Number(true) // 返回 1 // 日期 const date = new Date(); Number(date); // 返回 1591537858154 date.getTime(); // 返回 1591537858154,效果一致。 // 自动转换 5 + null // 返回 5 null 转换为 0 "5" + null // 返回"5null" null 转换为 "null" "5" + 1 // 返回 "51" 1 转换为 "1" "5" - 1 // 返回 4 "5" 转换为 5
3. 布尔值转换
转换规则如下:
- 直观上为“空”的值(如
0
、空字符串、null
、undefined
和NaN
)将变为false
。 - 其他值变成
true
。
Boolean(1); // true Boolean(0); // false Boolean("hello"); // true Boolean(""); // false Boolean("0"); // true Boolean(" "); // 空白, 也是 true (任何非空字符串是 true)
4. 小结
七、运算符
1、运算符概念
常见运算符如加法 +
、减法 -
、乘法 *
和除法 /
,举一个例子,来介绍一些概念:
let sum = 1 + 2; let age = +18;
其中:
- 加法运算
1 + 2
中,1
和2
为 2 个运算元,左运算元1
和右运算元2
,即运算元就是运算符作用的对象。 1 + 2
运算式中包含 2 个运算元,因此也称该运算式中的加号+
为 二元运算符。- 在
+18
中的加号+
对应只有一个运算元,则它是 一元运算符 。
2、+ 号运算符
let msg = "hello " + "leo"; // "hello leo" let total = 10 + 20; // 30 let text1 = "1" + "2"; // "12" let text2 = "1" + 2; // "12" let text3 = 1 + "2"; // "12" let text4 = 1 + 2 + "3"; // "33" let num = +text1; // 12 转换为 Number 类型
3、运算符优先级
运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。 下面的表将所有运算符按照优先级的不同从高(20)到低(1)排列。
优先级 | 运算类型 | 关联性 | 运算符 |
20 | 圆括号 |
n/a(不相关) | ( … ) |
19 | 成员访问 |
从左到右 | … . … |
需计算的成员访问 |
从左到右 | … [ … ] |
|
new (带参数列表) |
n/a | new … ( … ) |
|
函数调用 | 从左到右 | … ( … ) |
|
可选链(Optional chaining) | 从左到右 | ?. |
|
18 | new (无参数列表) | 从右到左 | new … |
17 | 后置递增(运算符在后) | n/a | |
… ++ |
|||
后置递减(运算符在后) | … -- |
||
16 | 逻辑非 | 从右到左 | ! … |
按位非 | ~ … |
||
一元加法 | + … |
||
一元减法 | - … |
||
前置递增 | ++ … |
||
前置递减 | -- … |
||
typeof | typeof … |
||
void | void … |
||
delete | delete … |
||
await | await … |
||
15 | 幂 | 从右到左 | … ** … |
14 | 乘法 | 从左到右 | |
… * … |
|||
除法 | … / … |
||
取模 | … % … |
||
13 | 加法 | 从左到右 | |
… + … |
|||
减法 | … - … |
||
12 | 按位左移 | 从左到右 | … << … |
按位右移 | … >> … |
||
无符号右移 | … >>> … |
||
11 | 小于 | 从左到右 | … < … |
小于等于 | … <= … |
||
大于 | … > … |
||
大于等于 | … >= … |
||
in | … in … |
||
instanceof | … instanceof … |
||
10 | 等号 | 从左到右 | |
… == … |
|||
非等号 | … != … |
||
全等号 | … === … |
||
非全等号 | … !== … |
||
9 | 按位与 | 从左到右 | … & … |
8 | 按位异或 | 从左到右 | … ^ … |
7 | 按位或 | 从左到右 | … | … |
6 | 逻辑与 | 从左到右 | … && … |
5 | 逻辑或 | 从左到右 | … || … |
4 | 条件运算符 | 从右到左 | … ? … : … |
3 | 赋值 | 从右到左 | … = … |
… += … |
|||
… -= … |
|||
… *= … |
|||
… /= … |
|||
… %= … |
|||
… <<= … |
|||
… >>= … |
|||
… >>>= … |
|||
… &= … |
|||
… ^= … |
|||
… |= … |
|||
2 | yield | 从右到左 | yield … |
yield* | yield* … |
||
1 | 展开运算符 | n/a | ... … |
0 | 逗号 | 从左到右 | … , … |
3 > 2 && 2 > 1 // return true 3 > 2 > 1 // 返回 false,因为 3 > 2 是 true,并且 true > 1 is false // 加括号可以更清楚:(3 > 2) > 1
八、值的比较
1. 常见比较
在 JS 中的值的比较与数学很类型:
- 大于/小于/大于等于/小于等于:
a>b
/a<b
/a>=b
/a<=b
; - 判断相等:
// 使用 ==,非严格等于,不关心值类型 // == 运算符会对比较的操作数做隐式类型转换,再比较 '1' == 1; // true // 使用 ===,严格相等,关心值类型 // 将数字值 -0 和 +0 视为相等,并认为 Number.NaN 不等于 NaN。 '1' === 1; // false
(图片来自:《MDN JavaScript 中的相等性判断》)
- 判断不相等: 和判断相等一样,也有两种:
!=
/!==
。
2. 相等性判断(Object.is())
另外 ES6 新增 Object.is 方法判断两个值是否相同,语法如下:
Object.is(value1, value2);
以下任意项成立则两个值相同:
Object.is('foo', 'foo'); // true Object.is(window, window); // true Object.is('foo', 'bar'); // false Object.is([], []); // false var foo = { a: 1 }; var bar = { a: 1 }; Object.is(foo, foo); // true Object.is(foo, bar); // false Object.is(null, null); // true // 特例 Object.is(0, -0); // false Object.is(0, +0); // true Object.is(-0, -0); // true Object.is(NaN, 0/0); // true
兼容性 Polyfill 处理:
if (!Object.is) { Object.is = function(x, y) { // SameValue algorithm if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 return x !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } }; }
3. null 与 undefined 比较
对于相等性判断比较简单:
null == undefined; // true null === undefined; // false
对于其他比较,它们会先转换位数字: null
转换为 0
, undefied
转换为 NaN
。
null > 0; // false 1 null >= 0; // true 2 null == 0; // false 3 null < 1; // true 4
需要注意: null == 0; // false
这里是因为:undefined
和 null
在相等性检查 ==
中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。
undefined > 0; // false 1 undefined > 1; // false 2 undefined == 0; // false 3
第 1、2 行都返回 false
是因为 undefined
在比较中被转换为了 NaN
,而 NaN
是一个特殊的数值型值,它与任何值进行比较都会返回 false
。 第 3 行返回 false
是因为这是一个相等性检查,而 undefined
只与 null
相等,不会与其他值相等。