JavaScript基础详解

1. 概述

JavaScript是一种解释型基于对象动态类型的脚本语言,主要用于增强网页的交互性。它与Java没有关系,只是在诞生初期为了借助Java的热度而取了类似的名字。

  • 解释型:不需要编译,由浏览器直接解释执行
  • 基于对象:支持面向对象编程,但没有类的概念(ES6之前)
  • 动态类型:变量类型在运行时确定,无需声明类型

JavaScript主要运行在客户端浏览器中,但随着Node.js的发展,现在也可以在服务器端运行。

为什么需要JavaScript?

  1. 交互性:创建动态网页效果(如表单验证、动画效果)
  2. 异步通信:通过AJAX实现与服务器的异步通信
  3. 操作DOM:动态修改网页内容和结构
  4. 操作BOM:控制浏览器行为(如窗口、历史记录等)

示例

1
2
3
4
5
6
7
8
9
10
11
12
// 简单的JavaScript代码示例
console.log("Hello, JavaScript!");

// 表单验证示例
function validateForm() {
const username = document.getElementById('username').value;
if (username.length < 5) {
alert("用户名至少需要5个字符");
return false;
}
return true;
}

2. 嵌入方法

2.1 内嵌式

详细解释:

  • 将JavaScript代码直接写在HTML文档的<script>标签中
  • 通常放在<head><body>底部
  • 优点:无需额外文件请求,代码与页面紧密结合
  • 缺点:代码难以复用,HTML与JS混合导致维护困难

示例:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>内嵌式示例</title>
<!-- 推荐写法:将脚本放在head中 -->
<script>
// 使用type属性明确指定类型(现代浏览器可省略)
function greet() {
alert("欢迎来到内嵌式JavaScript示例");
}

// 页面加载完成后执行
window.onload = function() {
console.log("页面已加载");
};
</script>
</head>
<body>
<button onclick="greet()">点击我</button>

<!-- 也可以放在body底部 -->
<script>
// 在body底部执行,确保DOM已加载
document.write("这是在body底部添加的内容");
</script>
</body>
</html>

2.2 外链式

详细解释:

  • 将JavaScript代码保存在单独的.js文件中
  • 通过<script src="路径">标签引入
  • 优点:代码复用性高,HTML与JS分离,便于维护
  • 缺点:增加HTTP请求,可能影响页面加载速度

示例:

  1. 创建script.js文件:
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
// script.js
function sayHello(name) {
console.log(`你好,${name}!`);
}

// 模块化示例
const Calculator = {
add: function(a, b) {
return a + b;
},
subtract: function(a, b) {
return a - b;
}
};

// 使用立即执行函数表达式(IIFE)避免全局污染
(function() {
const privateVar = "我是私有变量";

function privateFunction() {
console.log("这是私有函数");
}

// 暴露公共接口
window.PublicAPI = {
publicMethod: function() {
console.log("调用公共方法");
privateFunction();
}
};
})();
  1. 在HTML中引入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>外链式示例</title>
<!-- 引入外部JS文件 -->
<script src="script.js"></script>
<!-- 可以引入多个JS文件 -->
<script src="another-script.js"></script>
</head>
<body>
<button onclick="sayHello('张三')">打招呼</button>
<button onclick="PublicAPI.publicMethod()">调用公共方法</button>

<script>
// 也可以在引入外部文件后继续写内嵌代码
document.addEventListener('DOMContentLoaded', function() {
console.log("DOM已加载");
});
</script>
</body>
</html>

2.3 行内式

详细解释:

  • 直接将JavaScript代码写在HTML元素的事件属性中
  • 通常用于简单的事件处理
  • 优点:简单直接
  • 缺点:代码难以维护,HTML与JS高度耦合

示例:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>行内式示例</title>
</head>
<body>
<!-- 简单的行内事件 -->
<button onclick="alert('你好,世界!')">点击我</button>

<!-- 复杂一点的行内事件 -->
<input type="text" id="username" placeholder="请输入用户名">
<button onclick="
var name = document.getElementById('username').value;
if (name) {
alert('欢迎,' + name + '!');
} else {
alert('请输入用户名');
}
">提交</button>

<!-- 使用事件属性的其他示例 -->
<div onmouseover="this.style.backgroundColor='yellow'"
onmouseout="this.style.backgroundColor='white'"
style="padding: 20px; border: 1px solid #ccc;">
将鼠标移入此区域
</div>

<!-- 不推荐:复杂逻辑不应放在行内 -->
<button onclick="
function calculateSum() {
var a = parseInt(prompt('请输入第一个数字'));
var b = parseInt(prompt('请输入第二个数字'));
if (!isNaN(a) && !isNaN(b)) {
alert('和为: ' + (a + b));
} else {
alert('请输入有效数字');
}
}
calculateSum();
">计算和</button>
</body>
</html>

3. 语句

详细解释

  • JavaScript语句:由值、运算符、表达式、关键词和注释组成的编程指令
  • 执行顺序:从上到下,从左到右
  • 分号:用于分隔JavaScript语句,虽然在大多数情况下可以省略(自动分号插入机制),但强烈建议显式添加

示例

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
// 基本语句示例
var x = 5; // 声明变量并赋值
var y = 10; // 声明变量并赋值
var z = x + y; // 表达式语句

// 多条语句可以写在同一行(不推荐)
var a = 1, b = 2, c = 3;

// 复合语句(代码块)
{
var message = "Hello";
console.log(message);
}

// 条件语句
if (z > 10) {
console.log("z大于10");
} else {
console.log("z不大于10");
}

// 循环语句
for (var i = 0; i < 5; i++) {
console.log("循环次数: " + i);
}

// 函数声明
function greet(name) {
return "你好," + name + "!";
}

// 函数调用
var greeting = greet("李四");
console.log(greeting);

// 表达式语句
3 + 4; // 有效,但没有实际效果
"Hello"; // 有效,但没有实际效果

// 空语句(分号单独一行)
;

4. 注释

详细解释

  • 单行注释:以//开头,直到行末
  • 多行注释:以/*开头,以*/结尾,可以跨多行
  • 作用:解释代码、临时禁用代码、文档生成
  • 最佳实践:保持注释简洁、准确,与代码同步更新

示例

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
// 单行注释示例
// 计算两个数的和
function add(a, b) {
return a + b; // 返回a和b的和
}

// 多行注释示例
/*
* 函数:calculateAverage
* 用途:计算数组中所有元素的平均值
* 参数:
* numbers - 数字数组
* 返回值:
* 平均值(如果数组为空,返回0)
*/
function calculateAverage(numbers) {
if (numbers.length === 0) {
return 0;
}

var sum = 0;
for (var i = 0; i < numbers.length; i++) {
sum += numbers[i];
}

return sum / numbers.length;
}

// 临时禁用代码
function exampleFunction() {
console.log("这段代码会执行");

/*
console.log("这段代码被注释了,不会执行");
var temp = "临时变量";
console.log(temp);
*/

// 也可以用单行注释禁用单行代码
// console.log("这行也被禁用了");
}

// 文档注释(JSDoc风格)
/**
* 创建一个用户对象
* @param {string} name - 用户名
* @param {number} age - 用户年龄
* @returns {object} 包含用户信息的对象
*/
function createUser(name, age) {
return {
name: name,
age: age,
greet: function() {
console.log("你好," + this.name);
}
};
}

// 注意:多行注释不能嵌套
/*
这是外层注释
/*
这是内层注释 - 会导致语法错误
*/
*/

5. 变量

详细解释

  • 变量:用于存储信息的”容器”
  • 命名规则
    • 只能包含字母、数字、下划线和美元符号
    • 不能以数字开头
    • 区分大小写
    • 不能使用JavaScript保留关键字
  • 声明方式
    • var:ES5及之前,函数作用域
    • let:ES6,块级作用域,可重新赋值
    • const:ES6,块级作用域,不可重新赋值

示例

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
// var 声明(函数作用域)
var globalVar = "全局变量";
function testVar() {
var functionVar = "函数内变量";
if (true) {
var blockVar = "块级变量";
}
console.log(blockVar); // 可以访问,因为var没有块级作用域
}
testVar();
console.log(globalVar); // 可以访问
// console.log(functionVar); // 错误:functionVar is not defined

// let 声明(块级作用域)
function testLet() {
let functionLet = "函数内变量";
if (true) {
let blockLet = "块级变量";
console.log(blockLet); // 可以访问
}
// console.log(blockLet); // 错误:blockLet is not defined
}
testLet();

// const 声明(常量)
const PI = 3.14159;
const USER = {
name: "张三",
age: 25
};

// 基本数据类型不可变
// PI = 3.14; // 错误:Assignment to constant variable

// 对象属性可以修改
USER.age = 26;
console.log(USER); // { name: "张三", age: 26 }

// 声明变量并赋值
var a = 10;
let b = "Hello";
const c = true;

// 多变量声明
var x = 1, y = 2, z = 3;
let m = 4, n = 5;
const p = 6, q = 7;

// 未初始化的变量
var uninitialized;
console.log(uninitialized); // undefined

// 变量提升示例
console.log(hoistedVar); // undefined(变量提升,但未初始化)
var hoistedVar = "我被提升了";

// console.log(notHoistedLet); // 错误:Cannot access 'notHoistedLet' before initialization
let notHoistedLet = "我不会被提升";

6. JavaScript保留关键字

详细解释

  • 保留关键字:JavaScript中保留的特殊单词,不能用作变量名、函数名或标签名
  • 原因:这些关键字可能在当前或未来版本中具有特殊含义
  • 重要性:使用保留关键字会导致语法错误

常见保留关键字

1
2
3
4
5
6
abstract, arguments, boolean, break, byte, case, catch, char, class, const, continue, 
debugger, default, delete, do, double, else, enum, eval, export, extends, false, final,
finally, float, for, function, goto, if, implements, import, in, instanceof, int, interface,
let, long, native, new, null, package, private, protected, public, return, short, static,
super, switch, synchronized, this, throw, throws, transient, true, try, typeof, var, void,
volatile, while, with, yield

示例

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
// 错误示例:使用保留关键字作为变量名
// var function = "test"; // 语法错误
// var class = "math"; // 语法错误
// var const = 10; // 语法错误

// 正确示例:避免使用保留关键字
var funcName = "test";
var className = "math";
var constantValue = 10;

// 使用保留关键字作为对象属性(ES6+允许)
var obj = {
function: "test",
class: "math",
const: 10
};
console.log(obj.function); // "test"

// 保留关键字在特定上下文中可用
function example() {
var arguments = "local"; // 不推荐,会覆盖函数的arguments对象
console.log(arguments); // "local" 而不是参数对象
}
example("test");

// 未来保留关键字(可能在将来版本中使用)
// var let = 5; // 在ES6+中是语法错误
// var yield = 10; // 在ES6+中是语法错误

7. JavaScript作用域

7.1 局部变量

详细解释:

  • 定义:在函数内部声明的变量
  • 作用域:仅在声明它的函数内部可访问
  • 生命周期:函数调用开始时创建,函数执行结束时销毁
  • 特点:每次函数调用都会创建新的局部变量

示例:

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
function calculate() {
// 局部变量
var result = 0;
let sum = 0;
const PI = 3.14;

for (let i = 0; i < 10; i++) {
// i也是局部变量(块级作用域)
sum += i;
}

result = sum * PI;

console.log("i:", i); // 错误:i is not defined(i只在for循环块内有效)
console.log("sum:", sum); // 45
console.log("result:", result); // 141.3

return result;
}

calculate();
// console.log(result); // 错误:result is not defined
// console.log(sum); // 错误:sum is not defined
// console.log(PI); // 错误:PI is not defined

// 局部变量与全局变量同名
var message = "全局消息";

function showMessage() {
var message = "局部消息";
console.log(message); // "局部消息"
}

showMessage();
console.log(message); // "全局消息"

// 函数参数也是局部变量
function greet(name) {
console.log("你好," + name + "!");
name = "修改后的" + name;
console.log("修改后:" + name);
}

greet("张三");
// console.log(name); // 错误:name is not defined

7.2 全局变量

详细解释:

  • 定义:在函数外部声明的变量,或未使用var/let/const声明的变量
  • 作用域:在整个程序中可访问
  • 生命周期:从声明开始直到页面关闭
  • 特点:容易造成命名冲突,应谨慎使用

示例:

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
// 全局变量声明
var globalVar = "全局变量";
let globalLet = "全局let变量";
const GLOBAL_CONST = "全局常量";

function testScope() {
console.log(globalVar); // "全局变量"
console.log(globalLet); // "全局let变量"
console.log(GLOBAL_CONST); // "全局常量"

// 修改全局变量
globalVar = "修改后的全局变量";
globalLet = "修改后的全局let变量";
// GLOBAL_CONST = "修改"; // 错误:Assignment to constant variable
}

testScope();
console.log(globalVar); // "修改后的全局变量"
console.log(globalLet); // "修改后的全局let变量"

// 未声明的变量会成为全局变量(不推荐)
function createGlobal() {
undeclaredVar = "我是全局变量";
}

createGlobal();
console.log(undeclaredVar); // "我是全局变量"

// 全局对象属性
window.myGlobal = "通过window对象创建的全局变量";
console.log(myGlobal); // "通过window对象创建的全局变量"

// 全局变量与局部变量同名
var conflict = "全局";

function conflictExample() {
var conflict = "局部";
console.log(conflict); // "局部"
}

conflictExample();
console.log(conflict); // "全局"

// 避免全局变量污染的IIFE模式
(function() {
var privateVar = "私有变量";

function privateFunc() {
console.log("私有函数");
}

// 暴露公共接口
window.PublicAPI = {
publicMethod: function() {
console.log("公共方法");
privateFunc();
}
};
})();

// console.log(privateVar); // 错误:privateVar is not defined
PublicAPI.publicMethod(); // "公共方法" + "私有函数"

8. 数据类型

8.1 基本数据类型

8.1.1 数值型(Number)

详细解释:

  • 所有数字都是number类型,不分整数和浮点数
  • 支持十进制、八进制、十六进制表示法
  • 特殊值:NaN(非数字)、Infinity(无穷大)
  • 最大安全整数:Number.MAX_SAFE_INTEGER(2^53 - 1)
  • 最小安全整数:Number.MIN_SAFE_INTEGER(-(2^53 - 1))

示例:

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
// 十进制
var decimal = 42;
console.log(typeof decimal); // "number"

// 浮点数
var float = 3.14159;
console.log(typeof float); // "number"

// 科学计数法
var scientific = 1.23e5; // 123000
console.log(scientific); // 123000

// 八进制(以0开头,ES5严格模式下无效)
var octal = 0755; // 493
console.log(octal); // 493

// 十六进制(以0x开头)
var hex = 0xFF; // 255
console.log(hex); // 255

// 二进制(ES6+,以0b开头)
var binary = 0b1010; // 10
console.log(binary); // 10

// 特殊值
console.log(0 / 0); // NaN
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity

// 检测NaN
console.log(isNaN(NaN)); // true
console.log(isNaN("abc")); // true
console.log(isNaN(123)); // false

// 检测有限数
console.log(isFinite(123)); // true
console.log(isFinite(Infinity)); // false

// 转换为数字
console.log(parseInt("123")); // 123
console.log(parseInt("123.45")); // 123
console.log(parseInt("abc123")); // NaN
console.log(parseInt("123abc")); // 123

console.log(parseFloat("123.45")); // 123.45
console.log(parseFloat("123")); // 123

// 数字方法
var num = 123.456;
console.log(num.toFixed(2)); // "123.46"(保留2位小数)
console.log(num.toPrecision(4)); // "123.5"(保留4位有效数字)
console.log(num.toString()); // "123.456"
console.log(num.toString(16)); // "7b.74bc6a7ef9db2"(十六进制表示)

8.1.2 字符串型(String)

详细解释:

  • 用单引号或双引号包裹的文本
  • 字符串是不可变的(修改会创建新字符串)
  • 支持转义字符(如\n\t
  • 模板字符串(ES6+):使用反引号(`)创建,支持多行和变量插值

示例:

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
// 单引号和双引号
var single = '单引号字符串';
var double = "双引号字符串";
console.log(typeof single); // "string"
console.log(typeof double); // "string"

// 转义字符
var escaped = "第一行\n第二行\t制表符";
console.log(escaped);

// 模板字符串(ES6+)
var name = "张三";
var age = 25;
var template = `你好,${name}!你今年${age}岁。`;
console.log(template); // "你好,张三!你今年25岁。"

// 多行字符串
var multiLine = `这是第一行
这是第二行
这是第三行`;
console.log(multiLine);

// 字符串方法
var str = "Hello, JavaScript!";

console.log(str.length); // 17
console.log(str.charAt(0)); // "H"
console.log(str.charCodeAt(0)); // 72(Unicode编码)
console.log(str.concat(", World!")); // "Hello, JavaScript!, World!"

console.log(str.indexOf("Java")); // 7
console.log(str.lastIndexOf("a")); // 14

console.log(str.substring(7, 13)); // "JavaSc"
console.log(str.slice(7, 13)); // "JavaSc"
console.log(str.slice(-6)); // "ript!"

console.log(str.toLowerCase()); // "hello, javascript!"
console.log(str.toUpperCase()); // "HELLO, JAVASCRIPT!"

console.log(str.trim()); // 移除两端空白
console.log(" Hello ".trimLeft()); // "Hello "
console.log(" Hello ".trimRight()); // " Hello"

console.log(str.split(" ")); // ["Hello,", "JavaScript!"]

// 正则表达式相关方法
console.log(str.search(/Java/)); // 7
console.log(str.match(/[a-z]+/g)); // ["ello", "ava", "cript"]
console.log(str.replace(/JavaScript/, "World")); // "Hello, World!"

// 包含检查(ES6+)
console.log(str.includes("Java")); // true
console.log(str.startsWith("Hello")); // true
console.log(str.endsWith("!")); // true

// 重复字符串(ES6+)
console.log("x".repeat(3)); // "xxx"

8.1.3 布尔型(Boolean)

详细解释:

  • 只有两个值:truefalse
  • 用于条件判断和逻辑运算
  • 在条件语句中,其他类型会隐式转换为布尔值

示例:

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
// 布尔值
var isTrue = true;
var isFalse = false;
console.log(typeof isTrue); // "boolean"

// 比较运算
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 == 5); // true
console.log(5 != 3); // true

// 严格相等(值和类型都相等)
console.log(5 === 5); // true
console.log(5 === "5"); // false
console.log(5 !== "5"); // true

// 逻辑运算
console.log(true && true); // true
console.log(true && false); // false
console.log(true || false); // true
console.log(false || false); // false
console.log(!true); // false

// 短路求值
function logMessage() {
console.log("函数被调用");
return true;
}

// 第二个表达式不会执行
true || logMessage(); // 只输出"函数被调用"
false && logMessage(); // 只输出"函数被调用"

// 隐式类型转换
console.log(Boolean(0)); // false
console.log(Boolean(1)); // true
console.log(Boolean("")); // false
console.log(Boolean("Hello")); // true
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean({})); // true
console.log(Boolean([])); // true

// 在条件语句中的应用
var value = 0;
if (value) {
console.log("value为真");
} else {
console.log("value为假"); // 输出
}

// 三元运算符
var result = (5 > 3) ? "大于" : "小于";
console.log(result); // "大于"

8.1.4 null(空值)

详细解释:

  • 表示”无”或”空”的特殊值
  • 通常用于表示对象引用为空
  • typeof null返回"object"(历史遗留问题)

示例:

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
// null的使用
var empty = null;
console.log(empty); // null
console.log(typeof empty); // "object"(历史遗留问题)

// 表示对象引用为空
var user = {
name: "张三",
address: null // 表示地址信息为空
};

// 检查null
console.log(empty === null); // true
console.log(empty == null); // true

// null与undefined的区别
console.log(null == undefined); // true(宽松相等)
console.log(null === undefined); // false(严格相等)

// 使用场景
function findUser(id) {
// 模拟查找用户
if (id === 1) {
return { id: 1, name: "张三" };
} else {
return null; // 表示未找到用户
}
}

var user1 = findUser(1);
var user2 = findUser(2);

console.log(user1); // { id: 1, name: "张三" }
console.log(user2); // null

if (user2 === null) {
console.log("用户未找到");
}

8.1.5 undefined(未定义)

详细解释:

  • 表示变量已声明但未赋值
  • 函数没有返回值时的默认返回值
  • 未定义的属性或方法
  • typeof undefined返回"undefined"

示例:

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
// 未初始化的变量
var uninitialized;
console.log(uninitialized); // undefined
console.log(typeof uninitialized); // "undefined"

// 函数没有返回值
function noReturn() {
// 没有return语句
}
var result = noReturn();
console.log(result); // undefined

// 未定义的属性
var obj = {};
console.log(obj.property); // undefined

// 检查undefined
console.log(typeof uninitialized === "undefined"); // true
console.log(uninitialized === undefined); // true
console.log(uninitialized == undefined); // true

// 与null的区别
console.log(null == undefined); // true
console.log(null === undefined); // false

// 使用场景
function getUser(id) {
if (id === 1) {
return { id: 1, name: "张三" };
}
// 没有return,返回undefined
}

var user1 = getUser(1);
var user2 = getUser(2);

console.log(user1); // { id: 1, name: "张三" }
console.log(user2); // undefined

if (user2 === undefined) {
console.log("用户未找到");
}

// 作为函数参数的默认值
function greet(name = "访客") {
console.log("你好," + name + "!");
}

greet(); // "你好,访客!"
greet("张三"); // "你好,张三!"

8.2 复合数据类型

8.2.1 对象(Object)

详细解释:

  • 用于存储键值对的集合
  • 无序的数据结构
  • 通过属性和方法组织数据
  • 所有非基本类型都是对象(包括数组、函数等)

示例:

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
// 创建对象的多种方式
var obj1 = {}; // 对象字面量

var obj2 = {
name: "张三",
age: 25,
sayHello: function() {
console.log("你好," + this.name);
}
};

var obj3 = new Object();
obj3.name = "李四";
obj3.age = 30;
obj3.sayHello = function() {
console.log("你好," + this.name);
};

// 访问属性
console.log(obj2.name); // "张三"
console.log(obj2["age"]); // 25

// 修改属性
obj2.age = 26;
obj2["name"] = "王五";

// 添加新属性
obj2.gender = "男";
obj2["city"] = "北京";

// 删除属性
delete obj2.city;

// 对象方法
obj2.sayHello(); // "你好,王五!"

// 检查属性是否存在
console.log("name" in obj2); // true
console.log(obj2.hasOwnProperty("age")); // true

// 遍历对象属性
for (var key in obj2) {
if (obj2.hasOwnProperty(key)) {
console.log(key + ": " + obj2[key]);
}
}

// 对象方法
var person = {
firstName: "张",
lastName: "三",
getFullName: function() {
return this.firstName + this.lastName;
},
setFullName: function(fullName) {
var names = fullName.split("");
this.firstName = names[0];
this.lastName = names.slice(1).join("");
}
};

console.log(person.getFullName()); // "张三"
person.setFullName("李四");
console.log(person.getFullName()); // "李四"

// 对象拷贝
var original = { a: 1, b: 2 };
var shallowCopy = Object.assign({}, original);
var deepCopy = JSON.parse(JSON.stringify(original));

8.2.2 数组(Array)

详细解释:

  • 用于存储有序的元素集合
  • 元素可以是任意数据类型
  • 通过索引访问元素(从0开始)
  • 动态大小,可以随时添加或删除元素

示例:

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
76
77
78
79
80
81
// 创建数组的多种方式
var arr1 = []; // 空数组
var arr2 = [1, "Hello", true, { name: "张三" }, [1, 2, 3]];
var arr3 = new Array(); // 空数组
var arr4 = new Array(1, 2, 3); // [1, 2, 3]
var arr5 = new Array(5); // 长度为5的空数组

// 访问元素
console.log(arr2[0]); // 1
console.log(arr2[1]); // "Hello"
console.log(arr2[arr2.length - 1]); // [1, 2, 3]

// 修改元素
arr2[0] = 10;
arr2[4][0] = 100;

// 添加元素
arr2.push("新元素"); // 末尾添加
arr2.unshift("开头元素"); // 开头添加

// 删除元素
var last = arr2.pop(); // 删除末尾元素
var first = arr2.shift(); // 删除开头元素

// 查找元素
console.log(arr2.indexOf("Hello")); // 1
console.log(arr2.includes("Hello")); // true

// 遍历数组
for (var i = 0; i < arr2.length; i++) {
console.log(arr2[i]);
}

arr2.forEach(function(item, index) {
console.log(index + ": " + item);
});

// 数组方法
var numbers = [1, 2, 3, 4, 5];

console.log(numbers.length); // 5

// 添加/删除元素
numbers.push(6); // [1,2,3,4,5,6]
numbers.pop(); // [1,2,3,4,5]
numbers.unshift(0); // [0,1,2,3,4,5]
numbers.shift(); // [1,2,3,4,5]

// 插入/替换/删除元素
numbers.splice(2, 0, 2.5); // 在索引2处插入2.5
numbers.splice(2, 1, 2.6); // 替换索引2处的元素
numbers.splice(2, 1); // 删除索引2处的元素

// 切片
var slice = numbers.slice(1, 3); // [2,3]

// 排序
var unsorted = [3, 1, 4, 2];
unsorted.sort(function(a, b) {
return a - b;
});
console.log(unsorted); // [1,2,3,4]

// 连接数组
var combined = numbers.concat([6, 7, 8]);

// 转换为字符串
console.log(numbers.join(", ")); // "1, 2, 3, 4, 5"

// 高阶函数
var doubled = numbers.map(function(num) {
return num * 2;
});

var even = numbers.filter(function(num) {
return num % 2 === 0;
});

var sum = numbers.reduce(function(acc, num) {
return acc + num;
}, 0);

9. 函数

9.1 函数定义与调用

详细解释:

  • 函数声明:使用function关键字定义
  • 函数表达式:将函数赋值给变量
  • 调用:使用函数名加括号,可传递参数
  • 返回值:使用return语句,未返回则默认返回undefined

示例:

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
// 函数声明
function add(a, b) {
return a + b;
}

// 函数表达式
var subtract = function(a, b) {
return a - b;
};

// 调用函数
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2

// 带默认参数的函数(ES6+)
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); // 5
console.log(multiply(5, 2)); // 10

// 剩余参数(ES6+)
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

// 展开运算符(ES6+)
var nums = [1, 2, 3, 4];
console.log(sum(...nums)); // 10

// 函数作为参数
function operate(a, b, operation) {
return operation(a, b);
}
console.log(operate(5, 3, add)); // 8
console.log(operate(5, 3, subtract)); // 2

// 自调用函数表达式(IIFE)
(function() {
console.log("这个函数立即执行");
})();

// 带参数的IIFE
(function(name) {
console.log("你好," + name + "!");
})("张三");

9.2 匿名函数

详细解释:

  • 没有函数名的函数
  • 通常作为回调函数使用
  • 用于创建立即执行函数表达式(IIFE)

示例:

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
// 作为回调函数
document.getElementById("btn").addEventListener("click", function() {
alert("按钮被点击");
});

// 作为定时器回调
setTimeout(function() {
console.log("2秒后执行");
}, 2000);

// 作为数组方法的参数
var numbers = [1, 2, 3, 4];
var doubled = numbers.map(function(num) {
return num * 2;
});

// 匿名函数赋值给变量
var greet = function(name) {
console.log("你好," + name + "!");
};
greet("李四");

// IIFE(立即执行函数表达式)
(function() {
var privateVar = "私有变量";
console.log("IIFE执行");
})();

// 带参数的IIFE
(function(name) {
console.log("你好," + name);
})("王五");

// 作为返回值
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
var double = createMultiplier(2);
var triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15

10. 字典(对象作为字典使用)

详细解释:

  • JavaScript中没有专门的”字典”类型
  • 通常使用对象来模拟字典(键值对存储)
  • 键必须是字符串或Symbol类型
  • 通过属性访问或Object.keys()等方法遍历

示例:

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
// 创建字典
var dictionary = {
"apple": "苹果",
"banana": "香蕉",
"orange": "橙子"
};

// 访问值
console.log(dictionary.apple); // "苹果"
console.log(dictionary["banana"]); // "香蕉"

// 添加新项
dictionary.grape = "葡萄";
dictionary["mango"] = "芒果";

// 修改值
dictionary.apple = "红苹果";

// 删除项
delete dictionary.orange;
// 或
delete dictionary["banana"];

// 检查键是否存在
console.log("apple" in dictionary); // true
console.log(dictionary.hasOwnProperty("banana")); // false

// 遍历字典
for (var key in dictionary) {
if (dictionary.hasOwnProperty(key)) {
console.log(key + ": " + dictionary[key]);
}
}

// 获取所有键
var keys = Object.keys(dictionary);
console.log(keys); // ["grape", "mango", "apple"]

// 获取所有值
var values = Object.values(dictionary);
console.log(values); // ["葡萄", "芒果", "红苹果"]

// 获取键值对数组
var entries = Object.entries(dictionary);
console.log(entries); // [["grape", "葡萄"], ["mango", "芒果"], ["apple", "红苹果"]]

// 使用Map(ES6+,更强大的字典结构)
var map = new Map();
map.set("apple", "苹果");
map.set("banana", "香蕉");

console.log(map.get("apple")); // "苹果"
console.log(map.has("banana")); // true
map.delete("banana");
console.log(map.size); // 1

// 遍历Map
map.forEach(function(value, key) {
console.log(key + ": " + value);
});

for (var [key, value] of map) {
console.log(key + ": " + value);
}

11. JavaScript序列化与反序列化

详细解释:

  • 序列化:将JavaScript对象转换为字符串
  • 反序列化:将字符串转换回JavaScript对象
  • 主要使用JSON对象的stringifyparse方法
  • JSON(JavaScript Object Notation)是一种轻量级数据交换格式

示例:

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
76
77
78
79
80
81
82
83
84
85
86
87
// 创建对象
var user = {
name: "张三",
age: 25,
address: {
city: "北京",
street: "中关村"
},
hobbies: ["编程", "阅读", "运动"]
};

// 序列化为JSON字符串
var jsonString = JSON.stringify(user);
console.log(jsonString);
// {"name":"张三","age":25,"address":{"city":"北京","street":"中关村"},"hobbies":["编程","阅读","运动"]}

// 序列化时格式化输出
var formattedJson = JSON.stringify(user, null, 2);
console.log(formattedJson);
/*
{
"name": "张三",
"age": 25,
"address": {
"city": "北京",
"street": "中关村"
},
"hobbies": [
"编程",
"阅读",
"运动"
]
}
*/

// 序列化时过滤属性
var filteredJson = JSON.stringify(user, ["name", "age"]);
console.log(filteredJson); // {"name":"张三","age":25}

// 自定义序列化
var customJson = JSON.stringify(user, function(key, value) {
if (key === "age") {
return value + "岁";
}
return value;
});
console.log(customJson); // {"name":"张三","age":"25岁",...}

// 反序列化
var parsedUser = JSON.parse(jsonString);
console.log(parsedUser.name); // "张三"
console.log(parsedUser.address.city); // "北京"

// 反序列化时转换值
var transformedUser = JSON.parse(jsonString, function(key, value) {
if (key === "age") {
return value + "岁";
}
return value;
});
console.log(transformedUser.age); // "25岁"

// 序列化函数
var objWithFunction = {
name: "测试",
func: function() { return "Hello"; }
};

console.log(JSON.stringify(objWithFunction));
// {"name":"测试"} - 函数被忽略

// 序列化日期对象
var objWithDate = {
date: new Date()
};

console.log(JSON.stringify(objWithDate));
// {"date":"2023-10-15T08:30:00.000Z"}

// 自定义日期序列化
var customDateJson = JSON.stringify(objWithDate, function(key, value) {
if (value instanceof Date) {
return value.toISOString().split('T')[0];
}
return value;
});
console.log(customDateJson); // {"date":"2023-10-15"}

12. 转义与编码

详细解释:

  • 转义:将特殊字符转换为安全表示形式
  • URL编码:将URL中的特殊字符转换为%xx格式
  • 常用方法:
    • encodeURI():编码整个URI
    • encodeURIComponent():编码URI的组件
    • decodeURI():解码URI
    • decodeURIComponent():解码URI组件

示例:

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
76
// URI编码
var uri = "https://example.com/search?q=JavaScript 基础";
console.log(encodeURI(uri));
// "https://example.com/search?q=JavaScript%20%E5%9F%BA%E7%A1%80"

var component = "JavaScript 基础";
console.log(encodeURIComponent(component));
// "JavaScript%20%E5%9F%BA%E7%A1%80"

// URI解码
var encodedUri = "https://example.com/search?q=JavaScript%20%E5%9F%BA%E7%A1%80";
console.log(decodeURI(encodedUri));
// "https://example.com/search?q=JavaScript 基础"

var encodedComponent = "JavaScript%20%E5%9F%BA%E7%A1%80";
console.log(decodeURIComponent(encodedComponent));
// "JavaScript 基础"

// 转义HTML特殊字符
function escapeHtml(str) {
return str.replace(/[&<>"']/g, function(match) {
var escapeMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};
return escapeMap[match];
});
}

console.log(escapeHtml('<div class="test">Hello & World</div>'));
// "&lt;div class=&quot;test&quot;&gt;Hello &amp; World&lt;/div&gt;"

// 反转义HTML特殊字符
function unescapeHtml(str) {
var htmlEntities = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'"
};
return str.replace(/&(#?)(\w+);/g, function(match, hash, name) {
return htmlEntities[match] || match;
});
}

console.log(unescapeHtml("&lt;div class=&quot;test&quot;&gt;Hello &amp; World&lt;/div&gt;"));
// "<div class="test">Hello & World</div>"

// Base64编码与解码
var str = "Hello, JavaScript!";
var base64 = btoa(str);
console.log(base64); // "SGVsbG8sIEphdmFTY3JpcHQh"
var decoded = atob(base64);
console.log(decoded); // "Hello, JavaScript!"

// URL安全的Base64编码
function urlSafeBase64(str) {
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}

function urlSafeBase64Decode(str) {
str = str.replace(/-/g, '+').replace(/_/g, '/');
while (str.length % 4) str += '=';
return atob(str);
}

var safeBase64 = urlSafeBase64("Hello, JavaScript!");
console.log(safeBase64); // "SGVsbG8sIEphdmFTY3JpcHQh"
console.log(urlSafeBase64Decode(safeBase64)); // "Hello, JavaScript!"

13. eval

详细解释:

  • eval():执行传入的字符串作为JavaScript代码
  • 风险:可能执行恶意代码,造成安全漏洞
  • 替代方案:尽可能使用更安全的方法(如JSON.parse)

示例:

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
// 基本用法
eval("var x = 10;");
console.log(x); // 10

var result = eval("2 + 3 * 4");
console.log(result); // 14

// 执行函数
eval("function greet(name) { return 'Hello, ' + name; }");
console.log(greet("张三")); // "Hello, 张三"

// 执行JSON字符串(不安全,应使用JSON.parse)
var jsonStr = '{"name": "张三", "age": 25}';
var obj = eval("(" + jsonStr + ")");
console.log(obj.name); // "张三"

// 作用域示例
function testEval() {
eval("var y = 20;");
console.log(y); // 20
}
testEval();
// console.log(y); // 错误:y is not defined

// 安全风险示例(不要在实际代码中使用)
function unsafeFunction(input) {
// 如果input是"alert('XSS攻击')",将执行恶意代码
eval(input);
}

// 安全替代方案
function safeFunction(jsonStr) {
try {
return JSON.parse(jsonStr);
} catch (e) {
console.error("无效的JSON");
return null;
}
}

// 动态创建函数(比eval更安全)
var func = new Function("a", "b", "return a + b;");
console.log(func(5, 3)); // 8

// 使用Function构造函数的注意事项
var userInput = "alert('XSS')";
var safeFunc = new Function("input", "console.log(input);");
safeFunc(userInput); // 安全地输出字符串,不会执行代码

14. 日期与时间

详细解释:

  • Date对象:用于处理日期和时间
  • 支持多种创建方式:当前时间、指定时间、时间戳
  • 提供丰富的方法获取和设置日期时间组件
  • 时区处理:默认使用本地时区

示例:

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
// 创建Date对象
var now = new Date(); // 当前日期时间
console.log(now); // 例如:Sun Oct 15 2023 14:30:00 GMT+0800 (中国标准时间)

var specificDate = new Date(2023, 9, 15, 14, 30, 0); // 2023-10-15 14:30:00
// 注意:月份从0开始(0=1月,9=10月)

var timestamp = new Date(1697323800000); // 从时间戳创建
console.log(timestamp);

var dateString = new Date("2023-10-15T14:30:00"); // 从ISO 8601字符串创建
console.log(dateString);

// 获取日期时间组件
console.log(now.getFullYear()); // 年份(4位)
console.log(now.getMonth() + 1); // 月份(0-11,需+1)
console.log(now.getDate()); // 日期(1-31)
console.log(now.getDay()); // 星期(0-6,0=星期日)
console.log(now.getHours()); // 小时(0-23)
console.log(now.getMinutes()); // 分钟(0-59)
console.log(now.getSeconds()); // 秒(0-59)
console.log(now.getMilliseconds()); // 毫秒(0-999)
console.log(now.getTime()); // 时间戳(从1970-01-01 00:00:00 UTC)

// 设置日期时间组件
var date = new Date();
date.setFullYear(2024);
date.setMonth(0); // 1月
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
console.log(date); // 2024-01-01 00:00:00

// 日期计算
var today = new Date();
var tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
console.log("明天:", tomorrow);

var nextWeek = new Date(today);
nextWeek.setDate(nextWeek.getDate() + 7);
console.log("下周:", nextWeek);

// 日期格式化
function formatDate(date) {
var year = date.getFullYear();
var month = (date.getMonth() + 1).toString().padStart(2, '0');
var day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}

console.log(formatDate(new Date())); // 例如:2023-10-15

// 时间差计算
var start = new Date();
// 模拟一些操作
for (var i = 0; i < 1000000; i++) {
// 空操作
}
var end = new Date();
var duration = end - start;
console.log(`操作耗时: ${duration} 毫秒`);

// 时区处理
console.log(now.toISOString()); // ISO 8601格式(UTC时间)
console.log(now.toLocaleString()); // 本地化格式
console.log(now.toLocaleDateString()); // 本地化日期
console.log(now.toLocaleTimeString()); // 本地化时间

15. 面向对象编程

15.1 构造函数模式

详细解释:

  • 使用函数作为构造函数
  • 通过new关键字创建实例
  • this指向新创建的对象
  • 通过原型添加共享方法

示例:

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
// 构造函数
function Person(name, age) {
// 属性
this.name = name;
this.age = age;

// 方法(不推荐,每个实例都有独立副本)
this.greet = function() {
console.log(`你好,${this.name}!`);
};
}

// 创建实例
var person1 = new Person("张三", 25);
var person2 = new Person("李四", 30);

person1.greet(); // 你好,张三!
person2.greet(); // 你好,李四!

// 问题:greet方法在每个实例中都有独立副本
console.log(person1.greet === person2.greet); // false

// 通过原型添加共享方法
Person.prototype.sayAge = function() {
console.log(`${this.name}的年龄是${this.age}岁`);
};

person1.sayAge(); // 张三的年龄是25岁
person2.sayAge(); // 李四的年龄是30岁

// 检查方法是否共享
console.log(person1.sayAge === person2.sayAge); // true

// 继承示例
function Student(name, age, studentId) {
// 调用父构造函数
Person.call(this, name, age);
this.studentId = studentId;
}

// 设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

// 添加学生特有方法
Student.prototype.study = function() {
console.log(`${this.name}正在学习`);
};

var student1 = new Student("王五", 20, "S12345");
student1.greet(); // 你好,王五!
student1.sayAge(); // 王五的年龄是20岁
student1.study(); // 王五正在学习

// 检查继承关系
console.log(student1 instanceof Student); // true
console.log(student1 instanceof Person); // true
console.log(student1 instanceof Object); // true

15.2 ES6类语法

详细解释:

  • 使用class关键字定义类
  • 更简洁的语法,但底层仍是原型继承
  • 支持构造函数、方法、静态方法、继承
  • 语法糖,与构造函数模式本质相同

示例:

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
76
77
78
79
80
81
82
83
84
85
// 类定义
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}

// 实例方法
greet() {
console.log(`你好,${this.name}!`);
}

// 获取器
get ageInMonths() {
return this.age * 12;
}

// 设置器
set nickname(value) {
this._nickname = value;
}

get nickname() {
return this._nickname || this.name;
}

// 静态方法
static createAnonymous() {
return new Person("匿名用户", 0);
}
}

// 创建实例
var person1 = new Person("张三", 25);
person1.greet(); // 你好,张三!
console.log(person1.ageInMonths); // 300
person1.nickname = "小张";
console.log(person1.nickname); // "小张"

// 静态方法调用
var anonymous = Person.createAnonymous();
anonymous.greet(); // 你好,匿名用户!

// 继承
class Student extends Person {
constructor(name, age, studentId) {
super(name, age);
this.studentId = studentId;
}

study() {
console.log(`${this.name}正在学习,学号:${this.studentId}`);
}

// 重写父类方法
greet() {
super.greet();
console.log(`我是学生,学号:${this.studentId}`);
}
}

var student1 = new Student("李四", 20, "S12345");
student1.greet();
// 你好,李四!
// 我是学生,学号:S12345
student1.study(); // 李四正在学习,学号:S12345

// 检查继承关系
console.log(student1 instanceof Student); // true
console.log(student1 instanceof Person); // true

// 类表达式
var Animal = class {
constructor(name) {
this.name = name;
}

speak() {
console.log(`${this.name}发出声音`);
}
};

var dog = new Animal("狗");
dog.speak(); // 狗发出声音

16. 原型

16.1 原型的基本概念

详细解释:

  • 原型:JavaScript中实现继承的机制
  • 每个函数都有prototype属性,指向原型对象
  • 每个对象都有__proto__属性,指向其构造函数的原型
  • 当访问对象属性时,如果对象本身没有,会沿着原型链查找
  • 原型链最终指向Object.prototype,再往上是null

示例:

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
// 原型基本概念
function Person(name) {
this.name = name;
}

// 添加原型方法
Person.prototype.greet = function() {
console.log(`你好,${this.name}!`);
};

// 创建实例
var person1 = new Person("张三");
var person2 = new Person("李四");

// 检查原型
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true

// 使用原型方法
person1.greet(); // 你好,张三!
person2.greet(); // 你好,李四!

// 检查方法是否共享
console.log(person1.greet === person2.greet); // true

16.2 原型与类的联系

16.2.1 类与原型的基本关系

详细解释:

  • ES6的class只是原型继承的语法糖,它让开发者可以用更熟悉、更面向对象的方式编写代码,但底层仍然基于原型链实现。
  • 类中定义的方法会被添加到构造函数的原型对象上,所有实例共享这些方法。
  • 类的prototype属性指向原型对象,与构造函数的prototype相同。

示例:

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
// ES6 class写法
class Person {
constructor(name) {
this.name = name;
}

greet() {
console.log(`Hello, ${this.name}!`);
}

static createAnonymous() {
return new Person("匿名用户");
}
}

// 等价于ES5原型写法
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, ${this.name}!`);
};
Person.createAnonymous = function() {
return new Person("匿名用户");
};

// 验证方法在原型上
const p = new Person("张三");
console.log(p.greet === Person.prototype.greet); // true
console.log(p.__proto__ === Person.prototype); // true
console.log(Object.getPrototypeOf(p) === Person.prototype); // true

// 所有实例共享同一方法
const p1 = new Person("张三");
const p2 = new Person("李四");
console.log(p1.greet === p2.greet); // true

16.2.2 类方法在原型上的体现

详细解释:

  • 实例方法:类中定义的普通方法会被添加到原型对象上,所有实例共享。
  • 静态方法:被添加到类本身,而不是原型上。

示例:

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
class Person {
constructor(name) {
this.name = name;
}

greet() {
console.log(`Hello, ${this.name}!`);
}

static staticMethod() {
console.log("这是静态方法");
}
}

// 验证方法在原型上
console.log(Person.prototype.greet); // [Function: greet]
console.log(Person.prototype.constructor === Person); // true

// 所有实例共享同一方法
const p1 = new Person("张三");
const p2 = new Person("李四");
console.log(p1.greet === p2.greet); // true

// 静态方法在构造函数上,不在原型上
console.log(Person.staticMethod); // [Function: staticMethod]
console.log(Person.prototype.staticMethod); // undefined

16.2.3 继承与原型链

详细解释:

  • extends关键字实现的继承,实际上是设置子类的prototype指向父类的prototype,建立原型链。
  • super关键字在方法中指向父类的原型,在构造函数中指向父类构造函数。

示例:

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
class Animal {
constructor(name) {
this.name = name;
}

speak() {
console.log(`${this.name} 发出声音`);
}
}

class Dog extends Animal {
constructor(name) {
super(name);
}

speak() {
super.speak(); // 相当于 Animal.prototype.speak.call(this)
console.log(`${this.name} 汪汪叫`);
}
}

// 验证原型链
const dog = new Dog("小黑");

console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

// 原型链关系
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype); // true

16.2.4 类中访问和操作原型

详细解释:

  • 类本身也有prototype属性,可以直接操作。
  • 可以通过实例的__proto__Object.getPrototypeOf()访问原型。

示例:

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
class Person {
constructor(name) {
this.name = name;
}

greet() {
console.log(`Hello, ${this.name}!`);
}
}

// 动态添加方法到原型
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}!`);
};

const p = new Person("张三");
p.sayHello(); // "Hello, 张三!"

// 修改已有的原型方法
const originalGreet = Person.prototype.greet;
Person.prototype.greet = function() {
console.log("Before greet");
originalGreet.call(this);
console.log("After greet");
};

p.greet();
// Before greet
// Hello, 张三!
// After greet

16.2.5 类与原型的高级应用

详细解释:

  • 可以使用Object.setPrototypeOf()动态改变类的原型链。
  • 类的继承机制底层使用Object.create()

示例:

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
class Animal {
speak() {
console.log("动物发出声音");
}
}

class Dog {
bark() {
console.log("汪汪");
}
}

// 动态设置Dog的原型为Animal
Object.setPrototypeOf(Dog.prototype, Animal.prototype);

const dog = new Dog();
dog.speak(); // "动物发出声音"
dog.bark(); // "汪汪"

console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true

// 手动实现继承
class Animal {
constructor(name) {
this.name = name;
}
}

class Dog {
constructor(name) {
this.name = name;
}
}

// 手动实现继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 等价于 class Dog extends Animal

const dog = new Dog("小黑");
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true

17. 运算符

17.1 算术运算符

详细解释:

  • 用于执行数学计算
  • 包括加、减、乘、除、取模、递增、递减等
  • +运算符也用于字符串拼接

示例:

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
// 基本算术
console.log(5 + 3); // 8
console.log(5 - 3); // 2
console.log(5 * 3); // 15
console.log(5 / 3); // 1.6666666666666667
console.log(5 % 3); // 2(取模)

// 递增递减
var a = 5;
console.log(a++); // 5(后递增)
console.log(a); // 6
console.log(++a); // 7(先递增)

var b = 5;
console.log(b--); // 5
console.log(b); // 4
console.log(--b); // 3

// 字符串拼接
console.log("Hello" + " " + "World"); // "Hello World"
console.log("5" + 3); // "53"(字符串拼接)
console.log(5 + "3"); // "53"(字符串拼接)
console.log("5" - 3); // 2(自动转换为数字)

// 一元加号转换为数字
console.log(+"5"); // 5
console.log(+"5.5"); // 5.5
console.log(+"abc"); // NaN

// 指数运算符(ES2016+)
console.log(2 ** 3); // 8(2的3次方)
console.log(4 ** 0.5); // 2(平方根)

// 位运算符
console.log(5 & 3); // 1(按位与)
console.log(5 | 3); // 7(按位或)
console.log(5 ^ 3); // 6(按位异或)
console.log(~5); // -6(按位取反)
console.log(5 << 1); // 10(左移)
console.log(5 >> 1); // 2(有符号右移)
console.log(5 >>> 1); // 2(无符号右移)

17.2 比较运算符

详细解释:

  • 用于比较两个值
  • 返回布尔值(true或false)
  • 包括相等、不等、大于、小于等
  • 有宽松相等(==)和严格相等(===)之分

示例:

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
// 相等比较
console.log(5 == "5"); // true(宽松相等,类型转换)
console.log(5 === "5"); // false(严格相等,不转换类型)
console.log(5 != "5"); // false
console.log(5 !== "5"); // true

// 大小比较
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 >= 5); // true
console.log(5 <= 3); // false

// 特殊值比较
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

// 使用Object.is比较(ES6+)
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(-0, +0)); // false
console.log(Object.is(-0, -0)); // true

// 比较对象
var obj1 = { value: 5 };
var obj2 = { value: 5 };
console.log(obj1 == obj2); // false(比较引用)
console.log(obj1.value == obj2.value); // true(比较值)

// 比较字符串
console.log("apple" < "banana"); // true(按字典序)
console.log("apple" < "Apple"); // false(大写字母在小写字母前)
console.log("apple".localeCompare("banana")); // -1(-1,0,1)

// 比较日期
var date1 = new Date(2023, 9, 15);
var date2 = new Date(2023, 9, 16);
console.log(date1 < date2); // true

17.3 赋值运算符

详细解释:

  • 用于给变量赋值
  • 包括简单赋值和复合赋值
  • 复合赋值是运算和赋值的简写形式

示例:

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
// 简单赋值
var x = 10;

// 复合赋值
x += 5; // x = x + 5
console.log(x); // 15

x -= 3; // x = x - 3
console.log(x); // 12

x *= 2; // x = x * 2
console.log(x); // 24

x /= 4; // x = x / 4
console.log(x); // 6

x %= 4; // x = x % 4
console.log(x); // 2

// 其他复合赋值
x **= 3; // x = x ** 3
console.log(x); // 8

// 链式赋值
var a, b, c;
a = b = c = 10;
console.log(a, b, c); // 10 10 10

// 注意:赋值表达式返回赋值后的值
var y = (x = 5);
console.log(y); // 5

// 解构赋值(ES6+)
var [first, second] = [1, 2];
console.log(first, second); // 1 2

var { name, age } = { name: "张三", age: 25 };
console.log(name, age); // 张三 25

// 默认值
var { city = "北京" } = {};
console.log(city); // "北京"

17.4 逻辑运算符

详细解释:

  • 用于布尔值的逻辑运算
  • 包括逻辑与(&&)、逻辑或(||)、逻辑非(!)
  • 支持短路求值
  • 可用于条件判断和默认值设置

示例:

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
// 逻辑与(&&)
console.log(true && true); // true
console.log(true && false); // false
console.log(false && true); // false
console.log(false && false); // false

// 短路求值
function logMessage() {
console.log("函数被调用");
return true;
}

console.log(true && logMessage()); // 输出"函数被调用",返回true
console.log(false && logMessage()); // 不调用函数,返回false

// 逻辑或(||)
console.log(true || true); // true
console.log(true || false); // true
console.log(false || true); // true
console.log(false || false); // false

console.log(false || logMessage()); // 输出"函数被调用",返回true
console.log(true || logMessage()); // 不调用函数,返回true

// 逻辑非(!)
console.log(!true); // false
console.log(!false); // true
console.log(!!"Hello"); // true(转换为布尔值)

// 默认值设置
var name = null;
var displayName = name || "匿名用户";
console.log(displayName); // "匿名用户"

var value = 0;
var displayValue = value || "无值";
console.log(displayValue); // "无值"(0被视为假值)

// 空值合并运算符(ES2020+)
var nullable = null;
var result = nullable ?? "默认值";
console.log(result); // "默认值"

var zero = 0;
var result2 = zero ?? "默认值";
console.log(result2); // 0(0不是null或undefined)

// 逻辑赋值运算符(ES2021+)
var a = null;
a ??= "默认值";
console.log(a); // "默认值"

var b = "已存在";
b ??= "默认值";
console.log(b); // "已存在"

17.5 三元运算符

详细解释:

  • 条件表达式的简写形式
  • 语法:条件 ? 表达式1 : 表达式2
  • 如果条件为真,返回表达式1;否则返回表达式2
  • 可嵌套使用,但应避免过度嵌套

示例:

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
// 基本用法
var age = 25;
var status = (age >= 18) ? "成年人" : "未成年人";
console.log(status); // "成年人"

// 嵌套三元运算符
var score = 85;
var grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" : "D";
console.log(grade); // "B"

// 与逻辑运算符结合
var name = "张三";
var greeting = name ? `你好,${name}!` : "你好,访客!";
console.log(greeting); // "你好,张三!"

// 用于默认值
var input = null;
var value = input !== null && input !== undefined ? input : "默认值";
// 等价于
var value2 = input ?? "默认值";

// 用于简单条件赋值
var isDarkMode = true;
document.body.style.backgroundColor = isDarkMode ? "#333" : "#fff";

// 作为函数返回值
function getGreeting(time) {
return (time < 12) ? "上午好" :
(time < 18) ? "下午好" : "晚上好";
}
console.log(getGreeting(10)); // "上午好"
console.log(getGreeting(15)); // "下午好"
console.log(getGreeting(20)); // "晚上好"

// 注意:避免过度嵌套
// 不推荐
var result = condition1 ? value1 :
condition2 ? value2 :
condition3 ? value3 : value4;

// 推荐使用if-else或switch

18. 流程控制语句

18.1 if语句

详细解释:

  • 根据条件执行代码块
  • 语法:if (条件) { 代码块 }
  • 条件为真时执行代码块

示例:

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
var score = 85;

if (score >= 90) {
console.log("优秀");
}

// 多行语句不需要大括号(不推荐)
if (score >= 90)
console.log("优秀");

// 布尔转换
var name = "张三";
if (name) {
console.log("名字存在");
}

var empty = "";
if (!empty) {
console.log("字符串为空");
}

// 复杂条件
var age = 25;
var hasLicense = true;
if (age >= 18 && hasLicense) {
console.log("可以开车");
}

18.2 if-else语句

详细解释:

  • 提供条件为假时的备选代码块
  • 语法:if (条件) { 代码块1 } else { 代码块2 }

示例:

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
var score = 75;

if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}

// 嵌套if-else
var age = 25;
if (age < 18) {
console.log("未成年人");
} else {
if (age < 60) {
console.log("成年人");
} else {
console.log("老年人");
}
}

// 三元运算符替代
var message = (score >= 60) ? "及格" : "不及格";
console.log(message);

// 多条件判断
var num = 10;
if (num > 0) {
console.log("正数");
} else if (num < 0) {
console.log("负数");
} else {
console.log("零");
}

18.3 if…else if…else语句

详细解释:

  • 用于多条件分支判断
  • 语法:if (条件1) { ... } else if (条件2) { ... } else { ... }
  • 按顺序检查条件,执行第一个为真的条件对应的代码块

示例:

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
var score = 85;

if (score >= 90) {
console.log("优秀");
} else if (score >= 80) {
console.log("良好");
} else if (score >= 70) {
console.log("中等");
} else if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}

// 日期判断
var day = new Date().getDay();
if (day === 0) {
console.log("星期日");
} else if (day === 1) {
console.log("星期一");
} else if (day === 2) {
console.log("星期二");
} else if (day === 3) {
console.log("星期三");
} else if (day === 4) {
console.log("星期四");
} else if (day === 5) {
console.log("星期五");
} else {
console.log("星期六");
}

// 优先级示例
var value = 10;
if (value > 5) {
console.log("大于5");
} else if (value > 8) {
console.log("大于8"); // 永远不会执行
} else {
console.log("小于等于5");
}

18.4 switch语句

详细解释:

  • 用于多分支选择
  • 语法:switch (表达式) { case 值: ... break; ... default: ... }
  • 比较表达式与case值的严格相等(===)
  • 使用break防止穿透

示例:

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
76
77
78
79
80
81
82
83
var day = new Date().getDay();
switch (day) {
case 0:
console.log("星期日");
break;
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
case 4:
console.log("星期四");
break;
case 5:
console.log("星期五");
break;
case 6:
console.log("星期六");
break;
default:
console.log("无效的日期");
}

// 不使用break的穿透效果
var fruit = "apple";
switch (fruit) {
case "apple":
case "banana":
case "orange":
console.log("这是水果");
break;
case "carrot":
case "broccoli":
console.log("这是蔬菜");
break;
default:
console.log("未知类型");
}

// 多重case
var num = 2;
switch (num) {
case 1:
case 2:
case 3:
console.log("1-3之间");
break;
default:
console.log("其他");
}

// 使用表达式
var a = 5, b = 10;
switch (true) {
case a > b:
console.log("a大于b");
break;
case a < b:
console.log("a小于b");
break;
default:
console.log("a等于b");
}

// 字符串匹配
var color = "red";
switch (color) {
case "red":
console.log("红色");
break;
case "green":
console.log("绿色");
break;
case "blue":
console.log("蓝色");
break;
default:
console.log("其他颜色");
}

18.5 循环结构

18.5.1 while循环

详细解释:

  • 先判断条件,条件为真时执行循环体
  • 语法:while (条件) { 循环体 }
  • 可能一次都不执行

示例:

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
var i = 0;
while (i < 5) {
console.log("当前值: " + i);
i++;
}

// 计算1到100的和
var sum = 0;
var num = 1;
while (num <= 100) {
sum += num;
num++;
}
console.log("1到100的和: " + sum); // 5050

// 读取用户输入
var input;
while ((input = prompt("请输入'quit'退出")) !== "quit") {
console.log("你输入了: " + input);
}

// 避免无限循环
var counter = 0;
while (counter < 5) {
console.log(counter);
counter++; // 必须有递增/递减
}

// 处理数组
var arr = [1, 2, 3, 4, 5];
var index = 0;
while (index < arr.length) {
console.log(arr[index]);
index++;
}

18.5.2 do…while循环

详细解释:

  • 先执行循环体,再判断条件
  • 语法:do { 循环体 } while (条件);
  • 至少执行一次

示例:

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
var i = 0;
do {
console.log("当前值: " + i);
i++;
} while (i < 5);

// 用户验证
var password;
do {
password = prompt("请输入密码(至少6位)");
} while (password && password.length < 6);

// 处理可能为空的情况
var data = [];
var index = 0;
do {
if (index < data.length) {
console.log(data[index]);
}
index++;
} while (index < data.length);

// 生成随机数直到满足条件
var random;
do {
random = Math.random();
console.log(random);
} while (random < 0.5);

18.5.3 for循环

详细解释:

  • 三部分:初始化、条件、迭代
  • 语法:for (初始化; 条件; 迭代) { 循环体 }
  • 最常用的循环结构

示例:

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
// 基本for循环
for (var i = 0; i < 5; i++) {
console.log("当前值: " + i);
}

// 计算阶乘
var n = 5;
var factorial = 1;
for (var i = 1; i <= n; i++) {
factorial *= i;
}
console.log(n + "! = " + factorial); // 120

// 遍历数组
var arr = [1, 2, 3, 4, 5];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}

// 遍历字符串
var str = "Hello";
for (var i = 0; i < str.length; i++) {
console.log(str[i]);
}

// 嵌套循环
for (var i = 1; i <= 3; i++) {
for (var j = 1; j <= 3; j++) {
console.log(i + "," + j);
}
}

// 多变量初始化
for (var i = 0, j = 10; i < j; i++, j--) {
console.log(i + "," + j);
}

// 无限循环(谨慎使用)
// for (;;) {
// console.log("无限循环");
// }

18.5.4 for…in循环

详细解释:

  • 遍历对象的可枚举属性
  • 语法:for (var key in object) { ... }
  • 包括继承的属性,通常需要hasOwnProperty检查
  • 不保证顺序

示例:

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
// 遍历对象属性
var person = {
name: "张三",
age: 25,
city: "北京"
};

for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key + ": " + person[key]);
}
}

// 遍历数组(不推荐,应使用for...of)
var arr = [1, 2, 3];
for (var index in arr) {
console.log(index + ": " + arr[index]);
}

// 包含继承属性
function Person(name) {
this.name = name;
}
Person.prototype.age = 30;

var p = new Person("李四");
for (var key in p) {
console.log(key + ": " + p[key]);
// name: 李四
// age: 30
}

// 使用hasOwnProperty过滤
for (var key in p) {
if (p.hasOwnProperty(key)) {
console.log("自有属性: " + key);
} else {
console.log("继承属性: " + key);
}
}

18.5.5 for…of循环(ES6+)

详细解释:

  • 遍历可迭代对象(数组、字符串、Map、Set等)
  • 语法:for (var item of iterable) { ... }
  • 不遍历对象属性
  • 保证顺序

示例:

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
// 遍历数组
var arr = [1, 2, 3, 4, 5];
for (var num of arr) {
console.log(num);
}

// 遍历字符串
var str = "Hello";
for (var char of str) {
console.log(char);
}

// 遍历Map
var map = new Map();
map.set("name", "张三");
map.set("age", 25);
for (var [key, value] of map) {
console.log(key + ": " + value);
}

// 遍历Set
var set = new Set([1, 2, 3, 2, 1]);
for (var item of set) {
console.log(item); // 1, 2, 3
}

// 与for...in对比
var obj = { a: 1, b: 2, c: 3 };
// for...in遍历对象属性
for (var key in obj) {
console.log(key); // a, b, c
}
// for...of不能直接遍历对象
// for (var value of obj) { } // 错误

// 使用Object.entries遍历对象
for (var [key, value] of Object.entries(obj)) {
console.log(key + ": " + value);
}

18.6 break与continue

详细解释:

  • break:立即终止循环
  • continue:跳过当前迭代,继续下一次迭代
  • 可用于所有循环结构

示例:

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
// break示例
for (var i = 0; i < 10; i++) {
if (i === 5) {
break;
}
console.log(i); // 0,1,2,3,4
}

// continue示例
for (var i = 0; i < 5; i++) {
if (i === 2) {
continue;
}
console.log(i); // 0,1,3,4
}

// 嵌套循环中的break
outer: for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outer;
}
console.log(i + "," + j);
}
}
// 输出: 0,0 0,1 0,2 1,0

// 嵌套循环中的continue
outer: for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
continue outer;
}
console.log(i + "," + j);
}
}
// 输出: 0,0 0,1 0,2 1,0 2,0 2,1 2,2

19. JS操作DOM

19.1 什么是DOM?

详细解释:

  • DOM(Document Object Model):文档对象模型
  • 将HTML/XML文档表示为树形结构
  • 允许JavaScript动态访问和修改文档内容
  • 由W3C标准化,所有现代浏览器都支持
  • DOM树由节点组成:元素节点、属性节点、文本节点等

示例:

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
// DOM结构示例
/*
<html>
<head>
<title>示例页面</title>
</head>
<body>
<h1>标题</h1>
<p>段落文本</p>
</body>
</html>
*/

// 对应的DOM树
/*
Document
└── html
├── head
│ └── title
│ └── #text "示例页面"
└── body
├── h1
│ └── #text "标题"
└── p
└── #text "段落文本"
*/

19.2 DOM查找元素

19.2.1 直接查找

详细解释:

  • 通过ID、标签名、类名等查找元素
  • 返回单个元素或元素集合

示例:

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
// 通过ID获取元素(返回单个元素)
var header = document.getElementById("header");
console.log(header);

// 通过标签名获取元素(返回HTMLCollection)
var paragraphs = document.getElementsByTagName("p");
console.log(paragraphs);
console.log(paragraphs[0]); // 第一个p元素

// 通过类名获取元素(返回HTMLCollection)
var items = document.getElementsByClassName("item");
console.log(items);

// 通过名称获取元素(返回NodeList,常用于表单元素)
var radios = document.getElementsByName("option");
console.log(radios);

// 通过CSS选择器获取单个元素(返回第一个匹配的元素)
var firstItem = document.querySelector(".item");
console.log(firstItem);

// 通过CSS选择器获取所有元素(返回NodeList)
var allItems = document.querySelectorAll(".item");
console.log(allItems);

// 示例:获取特定元素
var mainContent = document.getElementById("main");
var links = document.querySelectorAll("a.external");
var activeItems = document.querySelectorAll(".item.active");

19.2.2 间接查找

详细解释:

  • 通过已获取的元素查找其子元素、父元素或兄弟元素
  • 常用属性:parentNodechildNodeschildrenfirstChildlastChildnextSiblingpreviousSibling

示例:

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
// 获取父元素
var child = document.getElementById("child");
var parent = child.parentNode;
console.log(parent);

// 获取子元素
var parent = document.getElementById("parent");
var children = parent.childNodes; // 包含文本节点
console.log(children);

var elementChildren = parent.children; // 只包含元素节点
console.log(elementChildren);

// 获取第一个/最后一个子元素
var firstChild = parent.firstElementChild;
var lastChild = parent.lastElementChild;
console.log(firstChild, lastChild);

// 获取兄弟元素
var element = document.getElementById("element");
var nextSibling = element.nextElementSibling;
var prevSibling = element.previousElementSibling;
console.log(nextSibling, prevSibling);

// 示例:遍历所有子元素
var container = document.getElementById("container");
for (var i = 0; i < container.children.length; i++) {
console.log(container.children[i]);
}

// 查找特定类型的子元素
var list = document.getElementById("list");
var items = list.querySelectorAll("li");
console.log(items);

19.3 文件内容操作

19.3.1 内容操作

详细解释:

  • innerText:获取或设置元素的文本内容(不包括HTML标签)
  • innerHTML:获取或设置元素的HTML内容(包括HTML标签)
  • textContent:类似innerText,但更标准,保留所有空白

示例:

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
// 获取内容
var div = document.getElementById("content");
console.log(div.innerText); // 仅文本内容
console.log(div.innerHTML); // 包含HTML的完整内容

// 设置内容
div.innerText = "新的文本内容"; // 会转义HTML
div.innerHTML = "<strong>新的HTML内容</strong>"; // 会解析HTML

// 使用textContent(更安全)
div.textContent = "安全的内容,<script>不会执行</script>";

// 示例:动态更新内容
function updateContent() {
var message = "当前时间: " + new Date().toLocaleTimeString();
document.getElementById("time").innerText = message;
}
setInterval(updateContent, 1000);

// 示例:创建HTML内容
var list = document.getElementById("list");
list.innerHTML = `
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
`;

// 使用innerText和innerHTML的差异
var div = document.createElement("div");
div.innerHTML = "<p>Hello</p><script>alert('XSS');</script>";
console.log(div.innerText); // "Hello"
console.log(div.innerHTML); // "<p>Hello</p><script>alert('XSS');</script>"

19.3.2 表单元素操作

详细解释:

  • value:获取或设置输入框的值
  • selectedIndex:获取或设置下拉菜单的选中索引
  • checked:获取或设置复选框/单选按钮的选中状态

示例:

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
// 输入框
var input = document.getElementById("username");
console.log(input.value); // 获取当前值
input.value = "新值"; // 设置新值

// 文本区域
var textarea = document.getElementById("message");
console.log(textarea.value);

// 下拉菜单
var select = document.getElementById("country");
console.log(select.value); // 获取选中值
console.log(select.selectedIndex); // 获取选中索引
select.selectedIndex = 1; // 设置选中项
select.value = "cn"; // 通过值设置选中项

// 复选框
var checkbox = document.getElementById("subscribe");
console.log(checkbox.checked); // 检查是否选中
checkbox.checked = true; // 设置为选中

// 单选按钮组
var radios = document.getElementsByName("gender");
for (var i = 0; i < radios.length; i++) {
if (radios[i].checked) {
console.log("选中值: " + radios[i].value);
break;
}
}

// 示例:表单验证
function validateForm() {
var username = document.getElementById("username").value;
if (username.length < 5) {
alert("用户名至少需要5个字符");
return false;
}
return true;
}

19.4 样式操作

19.4.1 样式操作

详细解释:

  • className:获取或设置元素的class属性(字符串)
  • classList:元素的类列表(DOMTokenList对象)
  • style:元素的行内样式对象

示例:

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
// className操作
var element = document.getElementById("box");
element.className = "highlight active"; // 设置整个class
console.log(element.className); // "highlight active"

// classList操作
console.log(element.classList); // DOMTokenList
console.log(element.classList.length); // 2
console.log(element.classList[0]); // "highlight"

// 添加类
element.classList.add("new-class");
console.log(element.classList.contains("new-class")); // true

// 移除类
element.classList.remove("highlight");
console.log(element.classList.contains("highlight")); // false

// 切换类
element.classList.toggle("active");
console.log(element.classList.contains("active")); // false

// 替换类
element.classList.replace("new-class", "updated-class");

// style操作
element.style.color = "red";
element.style.backgroundColor = "yellow";
element.style.fontSize = "16px";
element.style.padding = "10px";
element.style.borderRadius = "5px";

// 获取计算样式
var computedStyle = window.getComputedStyle(element);
console.log(computedStyle.color);
console.log(computedStyle.fontSize);

// 示例:动态切换主题
function toggleTheme() {
var body = document.body;
if (body.classList.contains("dark")) {
body.classList.remove("dark");
body.classList.add("light");
} else {
body.classList.remove("light");
body.classList.add("dark");
}
}

// 示例:悬停效果
var buttons = document.querySelectorAll(".button");
buttons.forEach(function(button) {
button.addEventListener("mouseover", function() {
this.style.backgroundColor = "#0066cc";
this.style.color = "white";
});
button.addEventListener("mouseout", function() {
this.style.backgroundColor = "";
this.style.color = "";
});
});

19.5 属性操作

19.5.1 属性操作

详细解释:

  • getAttribute():获取元素属性值
  • setAttribute():设置元素属性值
  • removeAttribute():移除元素属性
  • hasAttribute():检查元素是否有指定属性

示例:

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
var link = document.getElementById("myLink");

// 获取属性
var href = link.getAttribute("href");
var target = link.getAttribute("target");
console.log(href, target);

// 设置属性
link.setAttribute("href", "https://example.com");
link.setAttribute("target", "_blank");
link.setAttribute("title", "示例链接");

// 移除属性
link.removeAttribute("target");

// 检查属性
if (link.hasAttribute("title")) {
console.log("链接有title属性");
}

// 自定义属性(data-*)
var dataValue = link.getAttribute("data-id");
link.setAttribute("data-id", "123");
console.log(dataValue);

// 示例:动态修改图片
var img = document.getElementById("myImage");
img.setAttribute("src", "new-image.jpg");
img.setAttribute("alt", "新图片");

// 示例:添加aria属性提高可访问性
var button = document.getElementById("myButton");
button.setAttribute("aria-label", "关闭窗口");

19.6 创建和添加元素

19.6.1 创建标签并添加到HTML

详细解释:

  • 两种主要方式:字符串方式和对象方式
  • 字符串方式:使用insertAdjacentHTML()
  • 对象方式:使用document.createElement()appendChild()

示例:

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
// 字符串方式
var container = document.getElementById("container");
container.insertAdjacentHTML("beforeEnd", "<p>新段落</p>");

// insertAdjacentHTML的位置参数
// 'beforeBegin': 在元素开始前插入
// 'afterBegin': 在元素开始标记后插入
// 'beforeEnd': 在元素结束标记前插入
// 'afterEnd': 在元素结束标记后插入

// 示例:在不同位置插入
container.insertAdjacentHTML("beforeBegin", "<div>在容器前</div>");
container.insertAdjacentHTML("afterBegin", "<div>在容器内开头</div>");
container.insertAdjacentHTML("beforeEnd", "<div>在容器内结尾</div>");
container.insertAdjacentHTML("afterEnd", "<div>在容器后</div>");

// 对象方式
var newDiv = document.createElement("div");
newDiv.className = "box";
newDiv.style.backgroundColor = "lightblue";
newDiv.textContent = "新创建的div";

var newParagraph = document.createElement("p");
newParagraph.textContent = "这是段落内容";

// 添加子元素
newDiv.appendChild(newParagraph);

// 添加到文档
document.body.appendChild(newDiv);

// 创建更复杂的结构
function createCard(title, content) {
var card = document.createElement("div");
card.className = "card";

var cardTitle = document.createElement("h2");
cardTitle.textContent = title;

var cardContent = document.createElement("p");
cardContent.textContent = content;

card.appendChild(cardTitle);
card.appendChild(cardContent);

return card;
}

var card = createCard("示例卡片", "这是卡片内容");
document.getElementById("cards-container").appendChild(card);

19.7 表单操作

详细解释:

  • 任何表单元素都可以通过DOM提交
  • 使用submit()方法提交表单
  • 可以阻止默认提交行为进行验证

示例:

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
// 通过DOM提交表单
function submitForm() {
document.getElementById("myForm").submit();
}

// 阻止默认提交行为
document.getElementById("myForm").addEventListener("submit", function(event) {
event.preventDefault(); // 阻止默认提交

// 执行验证
var username = this.username.value;
if (username.length < 5) {
alert("用户名至少需要5个字符");
return;
}

// 验证通过后提交
this.submit();
});

// 任何元素都可以触发提交
document.getElementById("customSubmit").addEventListener("click", function() {
document.getElementById("myForm").submit();
});

// 示例:表单重置
function resetForm() {
document.getElementById("myForm").reset();
}

// 示例:动态创建表单
function createForm() {
var form = document.createElement("form");
form.id = "dynamicForm";
form.action = "/submit";
form.method = "post";

var input = document.createElement("input");
input.type = "text";
input.name = "username";

var submit = document.createElement("input");
submit.type = "submit";
submit.value = "提交";

form.appendChild(input);
form.appendChild(submit);

document.body.appendChild(form);
}

19.8 其他DOM操作

19.8.1 控制台输出

详细解释:

  • console.log():普通日志
  • console.error():错误日志
  • console.warn():警告日志
  • console.info():信息日志
  • console.debug():调试日志
  • console.table():表格形式显示数据

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log("普通日志");
console.error("错误信息");
console.warn("警告信息");
console.info("信息提示");
console.debug("调试信息");

var user = { name: "张三", age: 25, city: "北京" };
console.log(user);
console.table(user);

var users = [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 }
];
console.table(users);

19.8.2 弹窗

详细解释:

  • alert():显示消息框
  • confirm():显示确认框,返回布尔值
  • prompt():显示输入框,返回用户输入

示例:

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
// alert
alert("欢迎访问本网站!");

// confirm
var result = confirm("确定要删除吗?");
if (result) {
console.log("用户确认删除");
} else {
console.log("用户取消删除");
}

// prompt
var name = prompt("请输入您的名字", "张三");
if (name) {
console.log("欢迎," + name);
} else {
console.log("用户未输入名字");
}

// 示例:表单验证
function validateForm() {
var username = document.getElementById("username").value;
if (username.length < 5) {
alert("用户名至少需要5个字符");
return false;
}
return true;
}

19.8.3 URL和刷新

详细解释:

  • location.href:获取或设置当前URL
  • location.reload():重新加载页面
  • history.back():返回上一页
  • history.forward():前进到下一页

示例:

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
// 获取当前URL
console.log(location.href);

// 重定向
function redirect() {
location.href = "https://example.com";
}

// 重新加载页面
function refreshPage() {
location.reload();
}

// 通过历史记录导航
function goBack() {
history.back();
}

function goForward() {
history.forward();
}

// 替换当前URL(不创建新历史记录)
function replaceUrl() {
location.replace("https://example.com");
}

// 示例:定时重定向
setTimeout(function() {
location.href = "https://example.com";
}, 5000);

19.8.4 定时器

详细解释:

  • setInterval():重复执行函数
  • clearInterval():清除重复执行的定时器
  • setTimeout():延迟执行函数
  • clearTimeout():清除延迟执行的定时器

示例:

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
// setInterval
var intervalId = setInterval(function() {
var time = new Date().toLocaleTimeString();
document.getElementById("clock").value = time;
}, 1000);

// 停止定时器
function stopClock() {
clearInterval(intervalId);
}

// setTimeout
function showNotification() {
document.getElementById("notification").innerText = "操作成功!";
setTimeout(function() {
document.getElementById("notification").innerText = "";
}, 3000);
}

// 递归setTimeout(比setInterval更精确)
function animate() {
// 动画逻辑
console.log("动画帧");

// 递归调用
setTimeout(animate, 1000/60); // 60fps
}
animate();

// 清除定时器
var timerId;
function startTimer() {
timerId = setTimeout(function() {
console.log("定时器触发");
}, 2000);
}

function cancelTimer() {
clearTimeout(timerId);
}

// 示例:倒计时
var countdown = 10;
var countdownId = setInterval(function() {
document.getElementById("countdown").innerText = countdown;
countdown--;

if (countdown < 0) {
clearInterval(countdownId);
document.getElementById("countdown").innerText = "时间到!";
}
}, 1000);

19.9 事件处理

19.9.1 事件绑定

详细解释:

  • 行内绑定:直接在HTML标签中添加事件属性
  • 脚本绑定:通过JavaScript代码绑定事件

示例:

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
// 行内绑定(在HTML中)
// <button onclick="handleClick()">点击我</button>

// 脚本绑定
var button = document.getElementById("myButton");
button.onclick = function() {
alert("按钮被点击");
};

// 使用addEventListener(推荐)
button.addEventListener("click", function() {
alert("使用addEventListener绑定");
});

// 示例:多种事件
var input = document.getElementById("myInput");
input.addEventListener("focus", function() {
this.style.borderColor = "blue";
});

input.addEventListener("blur", function() {
this.style.borderColor = "";
});

input.addEventListener("keyup", function(event) {
console.log("按键: " + event.key);
});

// 示例:事件委托
document.getElementById("list").addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
console.log("点击了列表项: " + event.target.textContent);
}
});

// 示例:移除事件监听器
function handleClick() {
console.log("按钮被点击");
}

button.addEventListener("click", handleClick);
// 之后可以移除
button.removeEventListener("click", handleClick);

// 示例:事件对象
document.addEventListener("keydown", function(event) {
console.log("按键代码: " + event.keyCode);
console.log("按键字符: " + event.key);

if (event.key === "Enter") {
console.log("按下了回车键");
}

// 阻止默认行为
if (event.key === " ") {
event.preventDefault();
}
});

综合大例子

以下是一个包含所有知识点的综合性示例,展示如何将JavaScript基础知识应用于实际项目:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript综合示例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", sans-serif;
}

body {
background: linear-gradient(to right, #f5f7fa, #e4edf5);
color: #333;
line-height: 1.6;
padding: 20px;
}

.container {
max-width: 1200px;
margin: 0 auto;
}

header {
background: #2c3e50;
color: white;
padding: 20px 0;
text-align: center;
margin-bottom: 30px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}

.description {
font-size: 1.2rem;
max-width: 800px;
margin: 0 auto;
opacity: 0.9;
}

.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 25px;
margin-bottom: 30px;
transition: transform 0.3s ease;
}

.card:hover {
transform: translateY(-5px);
}

h2 {
color: #2c3e50;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}

.form-group {
margin-bottom: 20px;
}

label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

input, select, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}

button {
background: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
}

button:hover {
background: #2980b9;
}

.button-group {
display: flex;
gap: 10px;
margin-top: 15px;
}

.output {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border: 1px solid #eee;
border-radius: 4px;
min-height: 50px;
}

.user-card {
display: flex;
align-items: center;
padding: 15px;
border: 1px solid #eee;
border-radius: 8px;
margin-top: 15px;
background: #f8f9fa;
}

.user-avatar {
width: 50px;
height: 50px;
background: #3498db;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 15px;
}

.timer-display {
font-size: 2rem;
text-align: center;
margin: 20px 0;
color: #2c3e50;
}

.notification {
position: fixed;
top: 20px;
right: 20px;
background: #27ae60;
color: white;
padding: 15px 25px;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
transform: translateX(120%);
transition: transform 0.3s ease;
z-index: 1000;
}

.notification.show {
transform: translateX(0);
}

.code-block {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 4px;
font-family: monospace;
overflow-x: auto;
margin: 15px 0;
}

.highlight {
background: #fffde7;
padding: 2px 5px;
border-radius: 2px;
}

footer {
text-align: center;
margin-top: 50px;
padding: 20px;
color: #7f8c8d;
border-top: 1px solid #eee;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>JavaScript综合示例</h1>
<p class="description">本示例展示了JavaScript基础知识点的应用,包括变量、函数、DOM操作、事件处理等</p>
</header>

<div class="card">
<h2>1. 用户信息表单</h2>
<form id="userForm">
<div class="form-group">
<label for="username">用户名 (5-20个字符)</label>
<input type="text" id="username" name="username" required>
</div>

<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" name="email" required>
</div>

<div class="form-group">
<label for="age">年龄</label>
<input type="number" id="age" name="age" min="18" max="100" required>
</div>

<div class="form-group">
<label for="country">国家</label>
<select id="country" name="country">
<option value="cn">中国</option>
<option value="us">美国</option>
<option value="jp">日本</option>
<option value="kr">韩国</option>
</select>
</div>

<div class="form-group">
<label for="bio">个人简介</label>
<textarea id="bio" name="bio" rows="4"></textarea>
</div>

<div class="button-group">
<button type="submit" id="submitBtn">提交</button>
<button type="button" id="resetBtn">重置</button>
</div>
</form>

<div id="userOutput" class="output"></div>
</div>

<div class="card">
<h2>2. 动态内容操作</h2>
<div class="form-group">
<label for="contentInput">输入内容</label>
<input type="text" id="contentInput" placeholder="输入要添加的内容">
</div>

<div class="button-group">
<button id="addContentBtn">添加内容</button>
<button id="clearContentBtn">清空内容</button>
</div>

<div id="contentArea" class="output">
<p>初始内容</p>
</div>
</div>

<div class="card">
<h2>3. 时钟与通知</h2>
<div class="timer-display" id="clock">00:00:00</div>

<div class="button-group">
<button id="startTimerBtn">开始计时</button>
<button id="stopTimerBtn">停止计时</button>
<button id="resetTimerBtn">重置</button>
</div>

<div class="button-group" style="margin-top: 15px;">
<button id="showNotificationBtn">显示通知</button>
</div>
</div>

<div class="card">
<h2>4. 数据处理</h2>
<div class="form-group">
<label for="jsonData">JSON数据</label>
<textarea id="jsonData" rows="4" style="font-family: monospace;">{
"name": "张三",
"age": 25,
"city": "北京",
"hobbies": ["编程", "阅读", "运动"]
}</textarea>
</div>

<div class="button-group">
<button id="parseJsonBtn">解析JSON</button>
<button id="formatJsonBtn">格式化JSON</button>
</div>

<div id="jsonOutput" class="output"></div>
</div>

<div class="card">
<h2>5. 事件委托示例</h2>
<p>点击列表项查看效果:</p>

<ul id="eventList" style="list-style: none; padding-left: 0;">
<li style="padding: 10px; border: 1px solid #eee; margin: 5px 0; border-radius: 4px;">列表项 1</li>
<li style="padding: 10px; border: 1px solid #eee; margin: 5px 0; border-radius: 4px;">列表项 2</li>
<li style="padding: 10px; border: 1px solid #eee; margin: 5px 0; border-radius: 4px;">列表项 3</li>
<li style="padding: 10px; border: 1px solid #eee; margin: 5px 0; border-radius: 4px;">列表项 4</li>
</ul>

<div id="eventOutput" class="output"></div>
</div>

<div class="notification" id="notification">
操作成功!
</div>

<footer>
<p>JavaScript基础综合示例 &copy; 2023 | 所有知识点已应用</p>
</footer>
</div>

<script>
// 1. 变量和作用域
let currentUser = null;
let timerId = null;
let startTime = null;
let elapsedTime = 0;

// 2. 函数定义
function showNotification(message = "操作成功!", duration = 3000) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.classList.add('show');

setTimeout(() => {
notification.classList.remove('show');
}, duration);
}

// 3. DOM操作 - 表单处理
document.getElementById('userForm').addEventListener('submit', function(e) {
e.preventDefault();

// 4. 获取表单值
const username = document.getElementById('username').value;
const email = document.getElementById('email').value;
const age = parseInt(document.getElementById('age').value);
const country = document.getElementById('country').value;
const bio = document.getElementById('bio').value;

// 5. 表单验证
if (username.length < 5 || username.length > 20) {
showNotification('用户名必须在5-20个字符之间', 5000);
return;
}

if (!/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(email)) {
showNotification('请输入有效的邮箱地址', 5000);
return;
}

// 6. 创建用户对象
currentUser = {
id: Date.now(),
username: username,
email: email,
age: age,
country: country,
bio: bio,
createdAt: new Date()
};

// 7. 显示用户信息
displayUser(currentUser);
showNotification('用户信息已保存');

// 8. 重置表单
this.reset();
});

// 9. 重置表单
document.getElementById('resetBtn').addEventListener('click', function() {
document.getElementById('userForm').reset();
document.getElementById('userOutput').innerHTML = '';
currentUser = null;
showNotification('表单已重置');
});

// 10. 显示用户信息
function displayUser(user) {
const output = document.getElementById('userOutput');
output.innerHTML = `
<div class="user-card">
<div class="user-avatar">${user.username.charAt(0).toUpperCase()}</div>
<div>
<h3>${user.username}</h3>
<p>邮箱: ${user.email}</p>
<p>年龄: ${user.age}岁</p>
<p>国家: ${getCountryName(user.country)}</p>
${user.bio ? `<p>简介: ${user.bio}</p>` : ''}
</div>
</div>
`;
}

// 11. 辅助函数
function getCountryName(code) {
const countries = {
'cn': '中国',
'us': '美国',
'jp': '日本',
'kr': '韩国'
};
return countries[code] || code;
}

// 12. 动态内容操作
document.getElementById('addContentBtn').addEventListener('click', function() {
const content = document.getElementById('contentInput').value;
if (content.trim() === '') {
showNotification('请输入内容', 3000);
return;
}

const contentArea = document.getElementById('contentArea');
const newParagraph = document.createElement('p');
newParagraph.textContent = content;

// 13. 添加样式
newParagraph.classList.add('highlight');
newParagraph.style.transition = 'all 0.3s';
newParagraph.style.opacity = '0';

contentArea.appendChild(newParagraph);

// 14. 动画效果
setTimeout(() => {
newParagraph.style.opacity = '1';
}, 10);

document.getElementById('contentInput').value = '';
showNotification('内容已添加');
});

// 15. 清空内容
document.getElementById('clearContentBtn').addEventListener('click', function() {
document.getElementById('contentArea').innerHTML = '';
showNotification('内容已清空');
});

// 16. 时钟功能
function updateClock() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');

document.getElementById('clock').textContent = `${hours}:${minutes}:${seconds}`;
}

// 17. 计时器功能
document.getElementById('startTimerBtn').addEventListener('click', function() {
if (timerId) return;

startTime = Date.now() - elapsedTime;
timerId = setInterval(updateTimer, 10);
showNotification('计时器已开始');
});

document.getElementById('stopTimerBtn').addEventListener('click', function() {
if (timerId) {
clearInterval(timerId);
timerId = null;
elapsedTime = Date.now() - startTime;
showNotification('计时器已停止');
}
});

document.getElementById('resetTimerBtn').addEventListener('click', function() {
clearInterval(timerId);
timerId = null;
elapsedTime = 0;
document.getElementById('clock').textContent = '00:00:00';
showNotification('计时器已重置');
});

function updateTimer() {
const now = Date.now();
elapsedTime = now - startTime;

const hours = Math.floor(elapsedTime / 3600000);
const minutes = Math.floor((elapsedTime % 3600000) / 60000);
const seconds = Math.floor((elapsedTime % 60000) / 1000);
const milliseconds = Math.floor((elapsedTime % 1000) / 10);

document.getElementById('clock').textContent =
`${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(2, '0')}`;
}

// 18. 显示通知
document.getElementById('showNotificationBtn').addEventListener('click', function() {
showNotification();
});

// 19. JSON处理
document.getElementById('parseJsonBtn').addEventListener('click', function() {
const jsonData = document.getElementById('jsonData').value;
try {
const parsed = JSON.parse(jsonData);
document.getElementById('jsonOutput').innerHTML = `
<p><strong>姓名:</strong> ${parsed.name}</p>
<p><strong>年龄:</strong> ${parsed.age}</p>
<p><strong>城市:</strong> ${parsed.city}</p>
<p><strong>爱好:</strong> ${parsed.hobbies.join(', ')}</p>
`;
showNotification('JSON解析成功');
} catch (e) {
document.getElementById('jsonOutput').innerHTML =
`<p style="color: red;">JSON解析错误: ${e.message}</p>`;
showNotification('JSON解析失败', 5000);
}
});

document.getElementById('formatJsonBtn').addEventListener('click', function() {
const jsonData = document.getElementById('jsonData').value;
try {
const parsed = JSON.parse(jsonData);
const formatted = JSON.stringify(parsed, null, 2);
document.getElementById('jsonData').value = formatted;
showNotification('JSON已格式化');
} catch (e) {
showNotification('JSON格式化失败', 5000);
}
});

// 20. 事件委托
document.getElementById('eventList').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
const index = Array.from(e.target.parentNode.children).indexOf(e.target);
document.getElementById('eventOutput').innerHTML =
`点击了列表项 ${index + 1}: ${e.target.textContent}`;

// 21. 添加动画效果
e.target.style.transform = 'scale(1.05)';
e.target.style.transition = 'transform 0.2s';

setTimeout(() => {
e.target.style.transform = 'scale(1)';
}, 200);
}
});

// 22. 初始化
function init() {
// 23. 设置初始时钟
updateClock();
setInterval(updateClock, 1000);

// 24. 添加示例列表项
const eventList = document.getElementById('eventList');
for (let i = 5; i <= 7; i++) {
const li = document.createElement('li');
li.textContent = `列表项 ${i}`;
li.style.padding = '10px';
li.style.border = '1px solid #eee';
li.style.margin = '5px 0';
li.style.borderRadius = '4px';
eventList.appendChild(li);
}

// 25. 显示欢迎通知
setTimeout(() => {
showNotification('欢迎使用JavaScript综合示例', 5000);
}, 500);
}

// 26. 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);

// 27. 键盘事件处理
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
document.getElementById('userOutput').innerHTML = '';
document.getElementById('jsonOutput').innerHTML = '';
document.getElementById('eventOutput').innerHTML = '';
showNotification('所有输出已清空');
}
});

// 28. 拖拽功能示例
const notification = document.getElementById('notification');
let isDragging = false;
let offsetX, offsetY;

notification.addEventListener('mousedown', function(e) {
isDragging = true;
offsetX = e.clientX - notification.getBoundingClientRect().left;
offsetY = e.clientY - notification.getBoundingClientRect().top;
});

document.addEventListener('mousemove', function(e) {
if (isDragging) {
notification.style.position = 'absolute';
notification.style.left = (e.clientX - offsetX) + 'px';
notification.style.top = (e.clientY - offsetY) + 'px';
}
});

document.addEventListener('mouseup', function() {
isDragging = false;
});

// 29. 使用ES6+特性
const formatDateTime = (date) => {
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
};
return new Date(date).toLocaleString('zh-CN', options);
};

// 30. 添加当前时间戳
document.getElementById('jsonData').value = `{
"name": "示例用户",
"timestamp": "${Date.now()}",
"formattedTime": "${formatDateTime(Date.now())}"
}`;
</script>
</body>
</html>

总结

这个JavaScript综合示例展示了以下知识点:

  1. 变量与作用域:使用letconst声明变量,理解局部和全局作用域
  2. 函数:定义和调用函数,包括箭头函数
  3. 数据类型:处理字符串、数字、布尔值、对象和数组
  4. DOM操作
    • 查找元素(getElementById, querySelector)
    • 修改内容(innerText, innerHTML)
    • 操作样式(classList, style)
    • 创建和添加元素(createElement, appendChild)
  5. 事件处理
    • 事件绑定(addEventListener)
    • 事件委托
    • 阻止默认行为
  6. 表单处理
    • 获取表单值
    • 表单验证
    • 动态更新
  7. 定时器
    • setInterval和setTimeout
    • 清除定时器
  8. JSON处理
    • JSON.parse和JSON.stringify
    • 错误处理
  9. ES6+特性
    • 模板字符串
    • 箭头函数
    • 对象解构
    • Promise(隐式使用)
  10. 错误处理
    • try/catch
    • 输入验证
  11. 交互效果
    • 动画
    • 拖拽
    • 通知系统