一、JavaScript 简介
1. JavaScript 的历史与发展
- 1995 年由 Netscape 推出,最初名称为 LiveScript
- 主要用于网页中的交互行为
- 1997 年成为 ECMAScript 标准
- 发展阶段:
- ES3 —— 基础语法形成
- ES5 —— 正式成熟广泛应用
- ES6+ —— 模块化、类、Promise 等现代特性
👉 核心定位:
- 浏览器脚本语言
- 网页三大基石之一:HTML + CSS + JavaScript
2. JS 的执行环境
- 浏览器环境 —— 操作 DOM、事件处理、页面交互
- Node.js 环境 —— 服务器端开发、命令行工具
3. 如何运行 JavaScript
⭐ 方式一:浏览器控制台
步骤:
- 打开网页
- F12 打开开发者工具
- 选择 Console
- 输入 JS 代码执行
⭐ 方式二:HTML 中运行
在 HTML 中使用 <script> 标签:
1 | <script> |
⭐ 方式三:Node.js 运行
安装 Node.js 后执行:
1 | node app.js |
二、变量与作用域
1. 变量声明方式
| 关键字 | 特点 |
|---|---|
| var | 函数作用域、存在变量提升 |
| let | 块级作用域、不能重复声明 |
| const | 常量、必须赋初值、不可修改引用 |
👉 推荐:优先 let / const,少用 var
2. 命名规则与最佳实践
必须遵守:
- 不能以数字开头
- 区分大小写
- 不能使用关键字
推荐:
- 小驼峰命名:
userName - 语义化命名:
totalPrice
3. 变量提升(Hoisting)
- var 声明提升
- let/const 不会提升(会产生暂时性死区)
- 严格意义来说var, let, const都会进行变量提升,但是导致大家对let, const理解为不会变量提升的原因在于var提升至前面后会被初始化为undefined, 因此在var声明的变量未被赋值时进行调用,不会产生报错,但是let, const声明的变量提升后不会进行初始化,在变量作用域内赋值之前使用都属于暂时性死区,调用会导致程序报错。
- 暂时性死区解释:let和const声明的变量从进入作用域开始,直至变量声明之前都无法使用,因为不存在变量提升,因此在此之前一旦使用就会出现报错,而不是得到undefined
4. 作用域类型
全局作用域:在任何函数,作用域之外声明的变量,在程序内所有范围均可以被访问到
运行环境 全局对象 浏览器 window Node.js global 通用写法 globalThis(推荐) 全局作用域会导致全局污染问题(命名冲突),解决办法有模块化(ES Module、CommonJS),IIFE(立即执行函数),命名空间对象,后面会讲到 函数作用域:在函数内部声明的变量,只能在函数内部进行访问,函数外部无法访问函数内部的变量,这部分需要很好的理解函数作用域与作用域链,后面会讲
块级作用域:被花括号包裹,并且使用 let 或 const 声明的变量,只在这个块内部有效,var 只有 函数作用域
三、数据类型
1. 原始(基本)类型
- string:字符串,表示文本数据,用单双引号或反引号括起来。可以用 `”” ‘’ ```,反引号支持 模板字符串,可表示 Unicode(含中文、emoji)
- number:JavaScript 统一用 双精度浮点数(64 位),没有 int / float / double 区分,NaN 也是 number 类型
- boolean:布尔值,只有true或者false
- null: 表示有值,但是故意为空, 需要注意的是这里存在一个历史遗留问题,typeof null // “object”
- undefined:未定义,1、声明了变量,但没有赋值 2、对象不存在的属性 3、函数没有返回值
- symbol:独一无二的值,创建唯一标识(常用于对象 key),使用symbol创建的变量永远不会相等,适合做私有属性,比如: symbol(“a”) === symbol(“a”) 为false
- bigint: 超大整数(ES2020), 用于表示 超过 Number 安全范围 的整数
| 类型 | 含义 | 例子 |
|---|---|---|
| string | 字符串 | “abc” |
| number | 数值 | 3.14 |
| boolean | 布尔 | true |
| null | 空值 | null |
| undefined | 未定义 | undefined |
| symbol | 唯一值 | Symbol() |
| bigint | 大整数 | 10n |
2. 引用类型
引用数据类型的特点是:值存在堆内存中,栈内存中的变量存储的是堆内存中的引用地址,赋值/传参是 浅拷贝
Object: 对象
1
2
3
4let person = {
name: "Tom",
age: 20
};Array: 数组,有序列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76let arr = [1, 2, 3];
let arr2 = new Array(3);
arr.length // 3
------------------------------------------------------
arr.push(4) // 返回 4, 方法将指定的元素添加到数组的末尾,并返回新的数组长度。
- 语法:
- push()
- push(element0)
- push(element0, element1)
- push(element0, element1, /* … ,*/ elementN)
const total = arr.push(5, 6);
console.log(sports); // [1, 2, 3, 4, 5, 6]
console.log(total); // 6
------------------------------------------------------
arr.pop() // 方法从数组中删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。
// 返回被删除的那个元素
------------------------------------------------------
arr.unshift(0) // 将指定元素添加到数组的开头,并返回数组的新长度。
- 语法:
- unshift()
- unshift(element1)
- unshift(element1, element2)
- unshift(element1, element2, /* …, */ elementN)
------------------------------------------------------
arr.shift() // 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
------------------------------------------------------
arr.map() // 创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
- 语法:
map(callbackFn)
map(callbackFn, thisArg): callbackFn(必填)对数组每个元素执行的函数,会为数组的每一个元素调用一次, 接收以下参数:element 当前处理的元素, index 当前元素的索引(从 0 开始),array 被遍历的原数组。 thisArg(可选)执行 callbackFn 时用作 this 的值(在箭头函数中无效,因为箭头函数不绑定 this)
map() 方法是一个复制方法。它不会改变 this。然而,作为 callbackFn 提供的函数可以更改数组。请注意,在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
当开始调用 map() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。
常见的考点:
1、是否改变原来数组
const numbers = [1, 4, 9];
const doubles = numbers.map((num) => num * 2);
console.log(doubles); // [2, 8, 18]
console.log(numbers); // [1, 4, 9] 原来的数组数据并不会被破坏掉
2、在非数组对象上调用 map()
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2)); // [ 4, 9, 16 ]
等价于:
console.log(Array.from(arrayLike).map(n => n ** 2)); // [ 4, 9, 16 ]
------------------------------------------------------
arr.filter()
------------------------------------------------------
arr.reduce()Function: 函数是一等公民, 函数本质上也是对象, 函数可以:作为参数, 作为返回值, 赋值给变量, 存入数组/对象
Date: 日期对象,用于处理时间与日期
1
2
3
4
5
6
7
8
9let d = new Date();
d.getFullYear()
d.getMonth() + 1
d.getDate()
d.getHours()
d.getMinutes()
d.getSeconds()
d.getTime() // 时间戳Map: 键值对(增强版对象), Object 的问题:key 只能是 string 或 symbol, 无法直接获取大小, 遍历不方便
1
2
3
4
5
6
7
8
9let map = new Map();
map.set("name", "Tom");
map.set(123, "number key");
map.set({a:1}, "object key");
map.get("name"); // Tom
map.size; // 3Set: 去重集合, 元素不重复, 无索引, 自动去重
1
2
3
4
5
6
7
8
9
10let set = new Set([1,2,2,3]);
console.log(set); // {1,2,3}
set.add(4)
set.has(2)
set.delete(1)
set.size
const array = [...new Set([1, 2, 3, 2, 5])];
3. typeof 操作符
1 | typeof 123 // 'number' |
四、运算符
1. 算术运算符
+ - * / % **
2. 赋值运算符
= += -= *= /=
3. 比较运算符
==允许类型转换===严格相等(推荐)
4. 逻辑运算符
- && 与
- || 或
- ! 非
5. 三元运算符
1 | const type = score >= 60 ? '及格' : '不及格'; |
五、控制语句
1. 条件语句
if 语句
1 | if (age >= 18) { |
switch 语句
1 | switch (day) { |
2. 循环语句
for 循环
1 | for (let i = 0; i < 5; i++) { |
while 循环
1 | while (count > 0) { |
do…while
1 | do { |
for…of(遍历数组)
1 | for (const v of arr) { |
for…in(遍历对象)
1 | for (const k in obj) { |
3. 跳出控制
- break —— 立即结束循环
- continue —— 跳过当前循环
- label —— 不建议使用