js逆向-js基础
前言
人群太吵了
我想去听旷野的风
安静和孤独
踏实又自由
⚠️声明:本文所涉及的爬虫技术及代码仅用于学习、交流与技术研究目的,禁止用于任何商业用途或违反相关法律法规的行为。若因不当使用造成法律责任,概与作者无关。请尊重目标网站的
robots.txt协议及相关服务条款,共同维护良好的网络环境。
1.环境准备
1.1安装node

配置node环境变量
1.2pycharm配置node
在pycharm专业版开发工具安装node插件

pycharm配置node环境

2.js逆向
2.1简介
JavaScript逆向(JavaScript逆向)通常指的是对JavaScript代码进行分析和理解,以便破解或绕过应用的保护机制。这类操作常常在逆向工程、漏洞挖掘或安全测试中出现。JS逆向可能涉及多种技术和方法,以下是一些常见的JS逆向技术:
JavaScript加密(或称为编码、混淆)是指通过各种技术使JavaScript代码变得难以理解,以保护源代码或敏感数据。这种加密技术通常用于防止代码被逆向工程、破解或盗用。
2.2加密
保护数据隐私:确保敏感数据(如密码、个人信息、支付信息)不被未经授权的访问者读取。
防止数据篡改:加密可以确保数据在传输过程中不被恶意篡改,保证数据的完整性。
身份验证与授权:加密用于确认用户或系统的身份,确保只有授权用户可以访问特定资源或执行特定操作。
保护通信安全:确保信息在互联网上传输时不被拦截,防止中间人攻击。
防止密码破解:通过加密技术保护存储的密码,防止密码泄露或被破解。
防止未授权访问:加密保护重要数据,只有授权的用户可以解密并访问。
保证跨平台数据安全:加密保证数据在不同平台、设备和网络间传输时的安全。
防止盗版和反向工程:加密可以保护软件和代码,防止被破解或盗用。
2.3加密方式
2.3.1请求头加密
请求头加密通常是在网络请求中对请求头中的敏感信息进行加密处理,以防止在请求过程中被窃取或篡改。尤其是在 HTTP 请求中,像 API 密钥、Token、认证信息等敏感数据如果以明文传输,可能会被中间人攻击(Man-in-the-Middle,MITM)窃取。因此,通过加密请求头可以增加安全性。
请求头常见的是:User-Agent, Host, Origin,Referer,这些请求头我们很简单就能分辨出来,但是除了这些数据以外,网站会自己带上一些其他的参数,跟平时的数据可以明显看得出来不一样,这些就是请求头加密
地址:https://www.qizhidao.com/check?searchKey=人工智能&tagNum=1&fromRoutePage=check
每次请求请求头的Signature都不一样

2.3.2请求参数加密
请求参数加密是确保在网络请求中传输的敏感数据(如用户的个人信息、密码、API密钥等)不会被未授权的第三方窃取或篡改的一种方法。加密请求参数可以有效提高应用的安全性,尤其是在开放网络环境中,防止中间人攻击(MITM)等安全威胁。
地址:https://www.jizhy.com/44/rank/school
我们发现每次加载数据请求参数ts和sign都不一样

2.3.3cookie加密
Cookie验证是通过浏览器存储的Cookie来进行用户身份验证的一种机制。Cookie是在客户端存储的一个小数据片段,通常用于保存用户的身份信息(如登录状态、身份令牌等)。服务器通过验证Cookie中的数据来确认用户身份,从而实现访问控制、会话管理等功能。
在Web应用中,Cookie验证主要应用于以下场景:
- 用户登录验证:保存用户的登录状态,避免每次请求时都要求重新登录。
- 会话管理:管理用户的会话信息,例如存储会话ID(Session ID)等。
地址:https://www.fangdi.com.cn/index.html

2.3.4响应数据加密
响应数据加密是指在服务器向客户端发送数据时,对响应内容进行加密,以保护数据的机密性和完整性。这样,即使数据被拦截,也无法被未经授权的人员读取或篡改。响应数据加密通常用于传输敏感信息。
地址:https://www.swguancha.com/?promote=baidu07-PC-HX101&type=7&bd_vid=7846692177101707086

2.3.5全加密
该请求的请求头、请求参数、响应数据全部都有加密

3.变量和数据类型
3.1定义变量
在 JavaScript 中,定义变量时有三种常用的方法:var、let 和 const。它们的主要区别在于作用域、可变性和提升行为。
使用 var 定义变量 var 是最早的变量声明方式,声明的变量具有 函数作用域,而非块作用域。它允许变量在函数内被提升,可能会引发一些意外的行为。
var name = "Alice";
使用 let 定义变量 let 是 ES6 引入的变量声明方式,具有 块级作用域。它比 var 更加严格,并且不会造成变量提升问题。
let age = 25;
使用 const 定义变量 const 是 ES6 引入的常量声明方式。它声明的变量是 常量,不能被重新赋值。它也具有 块级作用域
const pi = 3.14;

3.2注释
在 JavaScript 中,注释用于添加代码的说明,不会影响代码的执行。JavaScript 支持两种类型的注释:
1.单行注释 使用 // 可以添加单行注释。
// 这是一个单行注释
let x = 5;
2.多行注释 使用 /* */ 可以添加多行注释,适用于注释较长的代码块或多行内容。
注意:多行注释不能嵌套使用。
/*
这是一个多行注释
用于说明一段较长的内容
*/
let y = 10;

3.3数据类型
JavaScript 中的基本数据类型包括 原始类型 和 引用类型。原始类型包括:
3.3.1原始类型(Primitive Types)
数字(Number) 数字可以是整数或浮动小数。
let num = 42;
let float = 3.14;
字符串(String) 字符串用于表示文本数据,可以用单引号、双引号或反引号(模板字符串)来表示。
let str1 = "Hello";
let str2 = 'World';
let str3 = `Hello ${str2}`;
布尔值(Boolean) 布尔值用于表示逻辑值,true 或 false。
let isTrue = true;
let isFalse = false;
符号(Symbol)(ES6 引入) Symbol 是一种独一无二的、不可变的数据类型,通常用于对象属性的标识。
let sym = Symbol("description");
未定义(undefined) 表示变量已声明但尚未赋值的类型。
let x;
console.log(x); // 输出: undefined
null 是 JavaScript 中的一个原始数据类型,表示“无”或“空”。它用于显式地表示空值,即某个变量不指向任何对象或值。null 是一个特殊的对象类型的值,但它本身并不代表任何对象。
虽然 null 表示空值,但在 JavaScript 中使用 typeof null 返回的是 "object",这属于历史遗留问题。
与 undefined 区别:undefined 是表示变量尚未初始化的状态,而 null 是表示明确赋值为空的状态。两者不同,undefined 表示系统自动赋值,null 是开发者主动赋值。
let emptyValue = null;
console.log(emptyValue); // 输出: null
console.log(typeof emptyValue); // 输出: "object"
原始类型也叫做 基本数据类型,它们是不可变的,具有固定的值。
- Number:表示数字,包括整数和浮点数。
- String:表示字符串,是由字符组成的文本。
- Boolean:表示布尔值,只有两个值:
true或false。 - undefined:表示变量声明但未赋值,默认值是
undefined。 - null:表示“空”或“无值”,明确表示没有任何对象。
- Symbol(ES6引入):表示一个独一无二的值,通常用于对象属性的标识。
- BigInt(ES11引入):表示任意精度的整数。
原始类型的特点:
- 不可变性:一旦赋值,原始类型的值不可更改。
- 按值传递:当将原始类型变量传递给函数时,传递的是变量的值,而不是原始类型的引用。因此,修改传递的副本不会影响原始值。
3.3.2引用类型(Reference Types)
对象(Object) 对象是复杂的数据类型,可以包含多个值,通常表示实体或数据结构。
let person = {
name: "Alice",
age: 25
};
数组(Array)(特殊类型的对象) 数组是对象的一种类型,用来存储一系列数据。
let numbers = [1, 2, 3, 4];
函数(Function)(也是对象) 函数也是一种对象类型,用于存储可以执行的代码块。
function greet() {
console.log("Hello!");
}
引用类型是指存储在内存中的对象,值可以改变。引用类型包括 对象(Object)、数组(Array)、函数(Function) 等。
- Object:用来存储多个值(键值对)。对象可以包含多种类型的数据。
- Array:数组是一种特殊的对象,用于存储一系列数据。
- Function:函数也是对象的一种类型,用于封装一段可复用的代码。
引用类型的特点:
- 可变性:引用类型的值是可变的,你可以修改对象的属性或数组的元素。
- 按引用传递:当将引用类型的变量传递给函数时,传递的是引用,即指向该数据的内存地址。如果在函数内部修改对象的属性或数组的元素,外部的原始对象也会受到影响。
3.3.3总结
| 特性 | 原始类型(Primitive Types) | 引用类型(Reference Types) |
|---|---|---|
| 存储 | 存储在栈内存中 | 存储在堆内存中 |
| 值的修改 | 不可修改,改变变量的值会创建新值 | 可修改,对象的内容可以直接改变 |
| 传递方式 | 按值传递,复制的是值本身 | 按引用传递,复制的是对象的引用 |
| 例子 | Number,String,Boolean,null,undefined,Symbol,BigInt |
Object,Array,Function |

3.4变量命名规范
命名规则:
- 变量名只能包含字母、数字、下划线 (
_) 和美元符号 ($)。 - 变量名不能以数字开头。
- 变量名不能是 JavaScript 的保留关键字(如
if、for等)。 - JavaScript 是 区分大小写 的语言:
myVar和myvar是两个不同的变量。
命名约定:
- 驼峰命名法(Camel Case):变量和函数通常采用驼峰命名法,即第一个单词小写,后续单词首字母大写。例如:
myVariable,getUserInfo。 - 大写常量:如果使用
const声明常量,常常使用全大写字母和下划线分隔。例如:MAX_LIMIT,PI。 - 有意义的命名:变量名应具有描述性,反映其用途或存储的数据。例如:
userAge,totalAmount,而不是a或temp。
避免使用全局变量: 使用 let 或 const 声明变量时,应尽量避免不必要的全局变量,减少冲突和错误的可能性。
使用有意义的名称: 变量名应该能够描述数据的目的或功能,避免使用无意义的名称,如 x 或 temp,尤其在较大项目中。
4.函数定义和调用
4.1函数定义
在 JavaScript 中,函数可以通过多种方式定义。最常见的两种方式是 函数声明 和 函数表达式。
函数声明(Function Declaration) 这是最传统的定义函数的方式。使用 function 关键字来定义一个函数,并且该函数会被提升(hoisted)到作用域的顶部。
function greet() {
console.log("Hello, World!");
}
函数表达式(Function Expression) 函数表达式将函数定义为一个匿名函数,并将其赋值给一个变量。此方式不会被提升,必须在定义之后才能调用。
const greet = function() {
console.log("Hello, World!");
};
箭头函数(Arrow Function)(ES6) 箭头函数是一种更简洁的函数定义方式。它没有 function 关键字,且 this 的指向方式不同。
const greet = () => {
console.log("Hello, World!");
};

4.2函数调用
基本调用
调用函数时,只需要使用函数名,并在括号内传递参数(如果有)。
function greet(name) {
console.log("Hello, " + name + "!");
}
greet("Alice"); // 调用函数,输出: Hello, Alice!
调用表达式中的函数
如果函数是赋值给变量的表达式,也可以通过该变量调用函数。
const greet = function(name) {
console.log("Hello, " + name + "!");
};
greet("Bob"); // 调用函数,输出: Hello, Bob!
箭头函数的调用
箭头函数也可以像普通函数一样调用。
const greet = (name) => {
console.log("Hello, " + name + "!");
};
greet("Charlie"); // 调用函数,输出: Hello, Charlie!

4.3有参有返函数
有参有返函数指的是既接受参数,又返回结果的函数。参数允许在调用时传递不同的值,函数的返回值则可以用于后续操作。
定义有参有返函数
function add(a, b) {
return a + b;
}
调用有参有返函数
let result = add(2, 3); // 调用函数并保存返回值
console.log(result); // 输出: 5
箭头函数有参有返
const multiply = (x, y) => x * y;
let result = multiply(4, 5); // 调用函数并保存返回值
console.log(result); // 输出: 20

4.4内部函数外部调用
JavaScript 中的函数可以在另一个函数内部定义,这种函数被称为 内部函数。外部函数可以调用内部函数,但内部函数不能直接访问外部函数外的变量(除非通过闭包)。
内部函数的定义与调用
function outer() {
function inner() {
console.log("Inside the inner function.");
}
inner(); // 在外部函数中调用内部函数
}
outer(); // 输出: Inside the inner function.
闭包(Closure):外部函数返回内部函数时,内部函数仍然能够访问外部函数的变量。
function outer() {
let outerVar = "I am from outer function!";
return function inner() {
console.log(outerVar); // 内部函数可以访问外部函数的变量
};
}
const closureExample = outer();
closureExample(); // 输出: I am from outer function!
内部函数外部调用
立即执行函数表达式(IIFE) 的用法,并在其中定义了一个函数 xl,然后将其赋值给外部的变量 _xl。接着,通过 _xl() 来调用该函数。
var _xl;
!(function () {
function xl(){
console.log('hello')
}
_xl = xl;
})();
_xl()

4.5reurn
return 语句用于从函数返回一个值,并终止函数的执行。return 后面的值将作为函数的返回值。
基本用法
function sum(a, b) {
return a + b; // 返回两个数字的和
}
let result = sum(3, 4);
console.log(result); // 输出: 7
返回值的类型
return 语句可以返回任意类型的数据,包括原始类型、对象、数组,甚至另一个函数。
function getPerson() {
return {
name: "Alice",
age: 30
};
}
let person = getPerson();
console.log(person.name); // 输出: Alice
返回函数
JavaScript 函数也可以返回另一个函数。
function outer() {
return function inner() {
console.log("I am the inner function.");
};
}
const innerFunction = outer(); // 返回 inner 函数
innerFunction(); // 输出: I am the inner function.
提前退出函数
return 也可以用于提前结束函数的执行,跳过后面的代码。
function checkAge(age) {
if (age < 18) {
return "Underage"; // 如果年龄小于18,提前返回
}
return "Adult";
}
在 JavaScript 中,当你在函数中使用逗号运算符时,它会依次计算每个表达式,但 返回值是最后一个表达式的值。

5.变量作用域
变量作用域是指变量在程序中可以被访问的范围。JavaScript 中的作用域控制着变量的生命周期和可访问性。作用域对于理解代码的行为和变量的存储位置至关重要,特别是在涉及闭包、异步编程和函数作用域时。
5.1简介
全局作用域(Global Scope):
- 全局作用域中的变量和函数可以在程序的任何地方访问。
- 如果在函数外部声明一个变量,那么这个变量就属于全局作用域。
- 注意:在浏览器中,全局变量会被挂载到
window对象上,在 Node.js 中则是挂载到global对象上。
函数作用域(Function Scope):
- 在函数内部声明的变量,只能在该函数内访问。
- JavaScript 的早期版本(ES5 及之前)只有函数作用域,没有块级作用域(例如
if、for中的作用域)。
块级作用域(Block Scope):
- 由
let和const关键字引入的作用域,在 ES6 中引入。 - 块级作用域是指代码块(如
if、for等)内部的作用域,声明的变量只在代码块内有效。
词法作用域(Lexical Scope):
- 函数的作用域是由函数在代码中定义的位置决定的,而不是由函数的调用位置决定的。
- 这就是所谓的“词法作用域”,即作用域在函数定义时就已经决定,而与函数调用时的位置无关。
5.2变量声明周期
声明阶段:
- 当执行一段代码时,首先会创建一个执行上下文(Execution Context),并为其中的变量分配内存。
- 在函数内部声明变量时,这些变量会进入该函数的作用域链。对于全局作用域的变量,它们会进入全局执行上下文。
初始化阶段:
- 在变量声明后,会为它们赋予默认值(例如,对于
let和const声明的变量是undefined,而var声明的变量可以提前访问)。 - 如果变量没有被显式赋值,则在执行代码时它们会被初始化为
undefined(对于let和const是暂时性死区(TDZ))。
执行阶段:
- 代码在执行过程中,变量会被赋予相应的值。
- 在这一阶段,变量的值可以被修改或重新赋值(具体规则取决于变量声明的类型,如
let、const和var)。 - 一旦变量的值被赋予,它就进入了执行阶段。
销毁阶段:
- 当函数的执行结束时,该函数的局部变量会被销毁。
- 对于全局变量来说,它们通常会一直存在,直到页面关闭或脚本结束。
- JavaScript 的垃圾回收机制会自动清理那些没有引用的变量,即使它们不再被使用,内存也会被释放。
6.条件语句
6.1简介
条件语句是编程中的基础结构,用于根据某些条件决定程序的执行路径。在 JavaScript 中,条件语句使得我们能够控制代码的流程,根据不同的条件执行不同的代码块。JavaScript 提供了多种方式来进行条件判断和决策。
常见的条件语句包括:
if语句else if语句else语句switch语句
条件语句的作用
- 判断一个条件是否成立。
- 根据不同的条件执行不同的代码。
- 可以通过多种方式组合不同的条件判断。
6.2语法
if 语句
if 语句用于根据一个条件表达式判断是否执行某块代码。
if (condition) {
// 如果 condition 为 true,执行这里的代码
}
if-else 语句
if-else 用于判断一个条件,如果条件为 true 执行 if 部分,否则执行 else 部分。
if (condition) {
// 如果 condition 为 true,执行这里的代码
} else {
// 如果 condition 为 false,执行这里的代码
}
if-else if 语句
当有多个条件需要判断时,可以使用 else if 来扩展 if 语句的判断范围。
if (condition1) {
// 如果 condition1 为 true,执行这里的代码
} else if (condition2) {
// 如果 condition2 为 true,执行这里的代码
} else {
// 如果上述条件都不为 true,执行这里的代码
}
switch 语句
switch 语句是另一种条件判断的方式,适用于多个条件的情况。它将变量与多个条件值进行匹配。
注意:
break语句用于跳出switch语句块。- 如果没有
break,switch会继续执行下一个case代码,直到遇到break或switch语句结束。
switch (expression) {
case value1:
// 如果 expression === value1,执行这里的代码
break;
case value2:
// 如果 expression === value2,执行这里的代码
break;
default:
// 如果没有匹配到任何条件,执行这里的代码
}

6.3比较运算符
常用的比较运算符包括:
==: 等于,判断两个值是否相等。===: 严格等于,判断两个值是否相等并且类型相同。!=: 不等于,判断两个值是否不相等。!==: 严格不等于,判断两个值是否不相等或者类型不同。>: 大于,判断左边的值是否大于右边的值。<: 小于,判断左边的值是否小于右边的值。>=: 大于或等于,判断左边的值是否大于或等于右边的值。<=: 小于或等于,判断左边的值是否小于或等于右边的值。
示例:
let a = 5;
let b = 10;
console.log(a == b); // false
console.log(a != b); // true
console.log(a === 5); // true
console.log(a < b); // true
注意:
-
== 和 ===
的区别:
==会进行类型转换,所以即使值类型不同,只要值相同,它就会返回true。===不会进行类型转换,只有当值和类型都相同才返回true。
示例:
console.log(5 == '5'); // true (因为 == 会进行类型转换)
console.log(5 === '5'); // false (因为 === 不会进行类型转换)
6.4三目运算符
三目运算符是一个简短的条件判断表达式,用于在一行中根据条件返回不同的值。
condition ? expression1 : expression2
- 如果
condition为true,返回expression1。 - 如果
condition为false,返回expression2。
示例:
let age = 20;
let result = (age >= 18) ? "Adult" : "Minor";
console.log(result); // "Adult"
使用场景:
三目运算符适用于简短的条件判断,能够在一行内执行条件判断并返回结果。

6.5逻辑运算符
逻辑运算符用于组合多个条件表达式,使得条件判断更复杂。常见的逻辑运算符有:
&&: 逻辑与(AND),当两个条件都为true时,结果为true。||: 逻辑或(OR),当任意一个条件为true时,结果为true。!: 逻辑非(NOT),将一个条件的布尔值取反。
示例:
let a = 5;
let b = 10;
console.log(a > 3 && b < 15); // true (a > 3 和 b < 15 都为 true)
console.log(a > 3 || b > 20); // true (a > 3 为 true)
console.log(!(a > 3)); // false (a > 3 为 true,取反后为 false)

7.数组
7.1介绍
数组是用来存储多个值的数据结构。在 JavaScript 中,数组可以存储任何类型的数据,包括基本数据类型(如数字、字符串等)以及对象、函数等。数组是一个非常常用的工具,能够有效地管理和操作一组数据。
数组的特点:
- 数组是一个有序的集合,元素按索引排序。
- 数组的索引从
0开始,最后一个元素的索引为数组长度 - 1。 - 数组的元素可以是不同类型的。
7.2定义
在 JavaScript 中,定义数组可以通过两种方式:
使用数组字面量
数组字面量是创建数组最常见和最简洁的方法,使用方括号 [] 来表示数组。
let fruits = ['apple', 'banana', 'orange'];
使用 new Array() 构造函数
你也可以通过 new Array() 来创建数组,但这种方式不如字面量常用,且容易产生误解(例如,传递一个数字时会创建一个指定长度的空数组)。
let fruits = new Array('apple', 'banana', 'orange');
或者使用指定长度创建一个空数组:
let numbers = new Array(3); // 创建一个长度为 3 的空数组
数组的初始化
let numbers = [1, 2, 3, 4, 5]; // 数字数组
let mixed = [1, 'apple', true, { name: 'John' }, [1, 2]]; // 混合类型数组
7.3多维数组
多维数组是数组中的数组,通常用于存储表格数据或者矩阵等复杂结构。
二维数组
二维数组就是数组的元素为数组的情况,可以通过 [][] 进行访问。
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix[0][0]); // 1
console.log(matrix[1][2]); // 6
三维数组
三维数组就是数组的元素是二维数组的情况,可以用 [][][] 来访问。
let cube = [
[
[1, 2],
[3, 4]
],
[
[5, 6],
[7, 8]
]
];
console.log(cube[0][1][0]); // 3
console.log(cube[1][0][1]); // 6
7.4数组的操作
常用方法:
push(),pop(),shift(),unshift():用于数组的增删操作。concat(),slice(),splice():用于数组的合并、切割和删除。forEach(),map(),filter(),reduce():用于遍历、转换和合并数组元素。find(),findIndex():用于查找数组中的元素或其索引。length:获取数组长度
7.4.1push() 和 pop()
push():向数组末尾添加一个或多个元素。pop():从数组末尾删除一个元素。
let fruits = ['apple', 'banana'];
fruits.push('orange'); // ['apple', 'banana', 'orange']
let lastFruit = fruits.pop(); // 'orange'
7.4.2shift()和unshift()`
shift():从数组开头删除一个元素。unshift():向数组开头添加一个或多个元素。
let fruits = ['apple', 'banana'];
fruits.unshift('orange'); // ['orange', 'apple', 'banana']
let firstFruit = fruits.shift(); // 'orange'
7.4.3concat()`
concat() 方法用于合并两个或多个数组,返回一个新数组。
let fruits1 = ['apple', 'banana'];
let fruits2 = ['orange', 'grape'];
let allFruits = fruits1.concat(fruits2); // ['apple', 'banana', 'orange', 'grape']
7.4.4join()
join() 方法用于将数组的所有元素连接成一个字符串,元素之间由指定的分隔符分隔。
let fruits = ['apple', 'banana', 'orange'];
let fruitsString = fruits.join(', '); // 'apple, banana, orange'
7.4.5slice()
slice() 方法用于返回数组的一个子数组。它接受两个参数:起始位置和结束位置(不包含该位置)。
let fruits = ['apple', 'banana', 'orange', 'grape'];
let someFruits = fruits.slice(1, 3); // ['banana', 'orange']
7.4.6splice()
splice() 方法用于从数组中添加或删除元素。它接受三个参数:起始位置、删除的元素数量和要添加的元素。
let fruits = ['apple', 'banana', 'orange', 'grape'];
fruits.splice(1, 2, 'pear', 'mango'); // 删除 'banana' 和 'orange',并添加 'pear' 和 'mango'
console.log(fruits); // ['apple', 'pear', 'mango', 'grape']
7.4.7forEach()
forEach() 方法用于遍历数组中的每个元素,并对每个元素执行指定的回调函数。
let fruits = ['apple', 'banana', 'orange'];
fruits.forEach(function(fruit) {
console.log(fruit);
});
// 输出:apple banana orange
7.4.8 map()
map() 方法返回一个新数组,新数组的每个元素是调用回调函数返回的结果。适用于对数组元素进行转换。
let numbers = [1, 2, 3, 4];
let doubled = numbers.map(function(number) {
return number * 2;
});
console.log(doubled); // [2, 4, 6, 8]
7.4.9filter()
filter() 方法返回一个新数组,包含通过指定条件的所有元素。
let numbers = [1, 2, 3, 4, 5];
let evenNumbers = numbers.filter(function(number) {
return number % 2 === 0;
});
console.log(evenNumbers); // [2, 4]
7.4.10reduce() 和 reduceRight()
reduce() 方法用于将数组中的所有元素按照指定的规则合并成一个值。reduceRight() 从右到左进行合并。
let numbers = [1, 2, 3, 4];
let sum = numbers.reduce(function(accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
console.log(sum); // 10
7.4.11find() 和 findIndex()
find() 方法返回数组中第一个符合条件的元素,而 findIndex() 返回该元素的索引。
let numbers = [1, 2, 3, 4];
let firstEven = numbers.find(function(number) {
return number % 2 === 0;
});
console.log(firstEven); // 2

8.对象
8.1简介
在 JavaScript 中,对象是一种数据类型,它允许你将多个值(可以是不同类型)组织在一起。每个值都以 键-值 对的形式存在,键也叫做 属性,而值可以是任何数据类型,包括基本数据类型和其他对象。
对象在 JavaScript 中非常常用,是一种非常重要的数据结构。它通常用来表示实体的属性(如学生的姓名、年龄、成绩等),或者用来实现类似于字典、哈希表等的数据结构。
对象的特点:
- 对象的值通过 属性名(键)来访问。
- 属性名(键)可以是字符串或符号(Symbol),如果是字符串,自动会被转换为字符串类型。
- 对象的值可以是任何类型的值,包括原始类型和其他对象。
- 对象没有顺序,它是无序的。
8.2创建
8.3.1字面量
对象字面量是创建对象最常见和最简洁的方法,使用一对花括号 {} 来包裹键值对。
let person = {
name: 'Alice',
age: 30,
greet: function() {
console.log('Hello, ' + this.name);
}
};
console.log(person.name); // 'Alice'
console.log(person.age); // 30
person.greet(); // 'Hello, Alice'

8.3.2new Object()
使用 new Object() 构造函数创建一个空对象,然后通过添加属性进行赋值。这种方法不如字面量简洁,通常不推荐使用。
let person = new Object();
person.name = 'Bob';
person.age = 25;
person.greet = function() {
console.log('Hello, ' + this.name);
};
console.log(person.name); // 'Bob'
person.greet(); // 'Hello, Bob'

8.3.3Object.create()
Object.create() 方法用于创建一个新的对象,并将其原型设置为指定的对象。这个方法适用于想要通过已有对象创建新对象的情况。
let animal = {
speak: function() {
console.log('Animal makes a sound');
}
};
let dog = Object.create(animal);
dog.speak(); // 'Animal makes a sound'

8.3.4类(ES6)
ES6 引入了 class 语法,它为创建对象提供了更为直观的方式。类可以看作是一个模板,通过类可以实例化多个对象。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log('Hello, ' + this.name);
}
}
let person1 = new Person('Charlie', 35);
person1.greet(); // 'Hello, Charlie'

8.3.5工厂函数
工厂函数是返回一个新对象的函数,可以用于创建具有相同属性和方法的多个对象。它允许通过函数来构建对象,而不是使用类。
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function() {
console.log('Hello, ' + this.name);
}
};
}
let person2 = createPerson('David', 40);
person2.greet(); // 'Hello, David'

8.3.6构造函数
构造函数是一个普通的函数,它的首字母通常大写,以区分普通函数。使用 new 关键字调用构造函数时,会创建一个新的空对象,赋予该对象构造函数定义的属性和方法,并将该对象的 __proto__ 指向构造函数的 prototype。
// 定义构造函数
function Person(name, age) {
this.name = name; // 设置属性
this.age = age;
this.greet = function() { // 设置方法
console.log('Hello, my name is ' + this.name);
};
}
// 使用构造函数创建实例
let person1 = new Person('Alice', 30);
let person2 = new Person('Bob', 25);
console.log(person1.name); // 'Alice'
console.log(person2.age); // 25
person1.greet(); // 'Hello, my name is Alice'
person2.greet(); // 'Hello, my name is Bob'

9.定时器
9.1介绍
定时器是 JavaScript 提供的一种机制,用于在一定时间后执行某个操作,或者周期性地执行某个操作。它常常用于延时任务、轮询、动画、定时事件等场景。
JavaScript 提供了两个常用的定时器方法:setTimeout() 和 setInterval()。这两个方法都可以用来安排某个函数在未来某个时刻执行。
setTimeout():用于在指定的延迟时间后执行一次指定的代码或函数。setInterval():用于每隔指定的时间间隔执行一次指定的代码或函数,直到清除定时器。
9.2使用
9.2.1setTimeout()
setTimeout() 用于在指定的延迟时间(以毫秒为单位)后执行一次指定的代码或函数。
-
function:要执行的代码或函数。 -
delay:延迟时间(单位:毫秒)。函数会在延迟时间之后执行一次。 -
arg1, arg2, ...:可选,传递给回调函数的额外参数。
setTimeout(function, delay, arg1, arg2, ...)
示例:
// 设置一个 3 秒后执行的定时器
setTimeout(function() {
console.log("3秒后执行的代码");
}, 3000);
9.2.2setInterval()
setInterval() 用于每隔指定的时间间隔(以毫秒为单位)执行一次指定的代码或函数,直到清除定时器。
function:要执行的代码或函数。interval:时间间隔(单位:毫秒)。函数会每隔这个时间间隔执行一次。arg1, arg2, ...:可选,传递给回调函数的额外参数。
setInterval(function, interval, arg1, arg2, ...)
示例:
// 每隔 2 秒执行一次的定时器
let intervalId = setInterval(function() {
console.log("每 2 秒执行一次");
}, 2000);
9.3清除
JavaScript 提供了清除定时器的方法,分别为:
clearTimeout():清除通过setTimeout()创建的定时器。clearInterval():清除通过setInterval()创建的定时器。
9.3.1clearTimeout()
clearTimeout() 可以用来取消一个定时器,它通过定时器的返回值来识别需要取消的定时器。
示例:
第一个 setTimeout() 会在 3 秒后执行,但第二个定时器会在 1 秒后清除第一个定时器,因此 "这条消息不会显示" 永远不会输出,"定时器被清除" 会在 1 秒后输出。
let timer = setTimeout(function() {
console.log("这条消息不会显示");
}, 3000);
// 在 1 秒后清除定时器
setTimeout(function() {
clearTimeout(timer);
console.log("定时器被清除");
}, 1000);
9.3.2clearInterval()
clearInterval() 用来取消由 setInterval() 创建的定时器。
示例:
上面的代码会每 2 秒输出一次 "每 2 秒执行一次",但在 6 秒后清除定时器,因此 "定时器已清除" 会在 6 秒后输出,之后不再输出 "每 2 秒执行一次"。
let intervalId = setInterval(function() {
console.log("每 2 秒执行一次");
}, 2000);
// 在 6 秒后清除定时器
setTimeout(function() {
clearInterval(intervalId);
console.log("定时器已清除");
}, 6000);

10.json
10.1介绍
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它的语法非常简洁,主要由两种结构组成:对象(Object)和数组(Array)。
10.2格式
对象格式
对象格式的json数据,使用一对大括号({}),大括号里面放入key:value形式的键值对,多个键值对使用逗号分隔。
{
"name": "John",
"age": 30,
"city": "New York"
}
数组格式
数组格式的json数据,使用一对中括号([]),中括号里面的数据使用逗号分隔。
["apple", "banana", "cherry"]
实例
{
"name": "Alice",
"age": 25,
"isStudent": false,
"address": {
"street": "123 Main St",
"city": "Wonderland"
},
"courses": ["Math", "Science", "History"],
"score": null
}
10.3JSON.parse
JSON.stringify():将 JavaScript 对象(person)转换为 JSON 格式的字符串。
JSON.parse() 将字符串转回对象。
var person ={
name:'Tom',
age:18
}
var jsonstr = JSON.stringify(person)
console.log(jsonstr)
var jsonObj = JSON.parse(jsonstr)
console.log(jsonObj)

11.ajax
11.1介绍
AJAX(Asynchronous JavaScript and XML)是一种用于创建异步 Web 应用程序的技术。它允许网页在不重新加载整个页面的情况下,与服务器进行数据交换并更新部分页面内容。
11.2使用
url:指定请求的 URL,这里使用了一个公开的 API https://jsonplaceholder.typicode.com/posts/1 来模拟数据请求。
type:请求类型,GET 请求用于从服务器获取数据。
data :设置发送给服务器的数据,没有参数不需要设置
dataType:指定预期的响应类型,这里期望返回 JSON 格式的数据。
success:请求成功时执行的回调函数,response 参数包含服务器返回的数据。
error:请求失败时执行的回调函数,xhr 包含请求对象,status 包含状态信息,error 包含错误信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJAX Demo with jQuery</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<h1>AJAX Demo with jQuery</h1>
<button id="loadDataBtn">Load Data</button>
<div id="response"></div>
<script>
$(document).ready(function () {
$('#loadDataBtn').click(function () {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts/1', // API 地址
type: 'GET', // 请求方式
dataType: 'json', // 期望的响应数据格式
success: function (response) { // 请求成功时
// 更新网页内容
$('#response').html(`
<h2>Post Title:</h2>
<p>${response.title}</p>
<h2>Post Body:</h2>
<p>${response.body}</p>
`);
},
error: function (xhr, status, error) { // 请求失败时
$('#response').html('Error: ' + error);
}
});
});
});
</script>
</body>
</html>
12.window对象属性
window 对象是浏览器中的全局对象,它代表浏览器的窗口,并包含了与浏览器相关的许多属性和方法。所有的全局变量和函数都可以通过 window 对象访问,因此它是 JavaScript 中最重要的对象之一。
12.1JavaScript的组成
JavaScript 主要可以分为三个部分:ECMAScript 标准、DOM(文档对象模型)和 BOM(浏览器对象模型)。这三者共同作用,组成了 JavaScript 在浏览器中的实际功能。

12.1.1ECMAScript(语言规范)
ECMAScript 是 JavaScript 的核心标准,它定义了语言的基本结构和行为。ECMAScript 规范由 ECMA International 组织维护,是一个平台无关的标准,不涉及浏览器或其他环境的具体实现。
主要功能:
- 语法:如变量声明(
var,let,const)、控制结构(if,for,switch)、函数声明、异常处理(try-catch)等。 - 数据类型:如
number,string,object,array,boolean等。 - 运算符:如算术运算符(
+,-,*,/等)、比较运算符(==,===,!=,>等)、逻辑运算符(&&,||等)。 - 内置对象:如
Array,Object,String,Function,Math,Date,RegExp等。 - 异步编程:如
Promise,async/await等。
ECMAScript 规范并不涉及浏览器的 DOM 操作或窗口的控制,而是聚焦于 JavaScript 语言本身的语法和功能。
12.1.2DOM(文档对象模型)
DOM 是浏览器中的文档对象模型,它为 JavaScript 提供了操作网页内容的能力。DOM 是一种接口,它允许程序和脚本动态访问和更新文档的内容、结构和样式。
主要功能:
- 访问和操作网页元素:通过
document.getElementById(),document.querySelector()等方法获取页面上的元素。 - 修改元素内容:使用
.innerHTML,.textContent,.value等属性修改元素的内容。 - DOM 事件处理:可以通过
addEventListener()等方法为页面元素添加事件监听器,处理用户的交互(如点击、键盘输入等)。 - 动态创建和删除元素:通过
document.createElement(),appendChild(),removeChild()等方法,可以动态地操作页面元素。

12.1.3BOM(浏览器对象模型)
BOM 是浏览器对象模型,它允许 JavaScript 访问和控制浏览器窗口或框架的功能。BOM 主要关注浏览器环境,而非网页的内容和结构。
主要功能:
- 控制浏览器窗口:通过
window对象可以访问浏览器的窗口属性,如window.innerHeight(浏览器视口的高度)、window.location(浏览器的 URL)等。 - 浏览器历史记录管理:通过
window.history来控制浏览器的历史记录,如back()、forward()和go()方法。 - 定时器:使用
setTimeout()和setInterval()来设置延时执行或定时执行的代码。 - 浏览器通信:使用
XMLHttpRequest或fetch()进行 AJAX 请求,实现与服务器的异步通信。

12.2docment
它是 DOM(文档对象模型)的一部分,代表整个 HTML 或 XML 文档。document 对象提供了许多方法和属性,使得 JavaScript 能够操作和修改网页的内容、结构和样式。
document对象其实是window对象下的一个子对象,它操作的是HTML文档里所有的内容。事实上,浏览器每次打开一个窗口,就会为这个窗口生成一个window对象,并且会为这个窗口内部的页面(即HTML文档)自动生成一个document对象,然后我们就可以通过document对象来操作页面中所有的元素。
| 属性 | 说明 |
|---|---|
document.title |
获取文档的title |
document.forms |
获取所有form元素 |
document.images |
获取所有img元素 |
document.links |
获取所有a元素 |
document.cookie |
文档的cookie |
document.URL |
当前文档的URL |
document.referrer |
返回使浏览者到达当前文档的URL |
document.write |
页面载入过程中,用脚本加入新的页面内容 |
document.getElementById() |
通过id获取元素 |
document.getElementsByTagName() |
通过标签名获取元素 |
document.getElementsByClassName() |
通过class获取元素 |
document.getElementsByName() |
通过name获取元素 |
document.querySelector() |
通过选择器获取元素,只获取第1个 |
document.querySelectorAll() |
通过选择器获取元素,获取所有 |
document.createElement() |
创建元素节点 |
document.createTextNode() |
创建文本节点 |
document.write() |
输出内容 |
document.writeln() |
输出内容并换行 |
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Document Example</title>
</head>
<body>
<h1 id="title">Hello, World!</h1>
<p class="description">This is a paragraph.</p>
<button id="changeTextBtn">Change Text</button>
<script src="script.js"></script>
</body>
<script>
console.log("document.title:"+document.title)
console.log("document.forms:"+document.forms)
console.log("document.images:"+document.images)
console.log("document.links:"+document.links)
console.log("document.cookie:"+document.cookie)
console.log("document.URL:"+document.URL)
console.log("document.referrer:"+document.referrer)
document.write('<h1>这是写入的内容</h1>')
console.log(document.getElementById('title'))
</script>
</html>

12.3window对象的navigator属性
window.navigator 是浏览器中的一个内置对象,提供了关于浏览器本身的信息,包括浏览器的版本、操作系统、语言等。它是 window 对象的一部分,通常用于获取浏览器的详细信息和用户的环境。
| 方法 | 说明 |
|---|---|
navigator.appCodeName |
浏览器代号 |
navigator.appName |
浏览器名称 |
navigator.appVersion |
浏览器版本 |
navigator.cookieEnabled |
启用Cookies |
navigator.platform |
硬件平台 |
navigator.userAgent |
用户代理 |
navigator.language |
用户代理语言 |

12.4window对象的Location属性
Location代表了当前文档的 URL,并且提供了一些方法和属性,可以让我们操作浏览器的地址栏 URL,进行页面跳转、重新加载等操作。它属于 BOM(浏览器对象模型) 的一部分。
window.location.href:获取或设置当前页面的完整 URL。它包括协议、主机、路径和查询参数。window.location.hostname:返回当前页面的主机名部分(不包括协议、端口和路径)。window.location.protocol:返回当前页面的协议部分(例如http:或https:)。window.location.port:返回当前页面的端口号(如果指定了端口)。如果没有指定端口,则返回空字符串。window.location.pathname:返回当前页面的路径部分(不包括域名和查询参数)。window.location.search:返回当前页面的查询字符串(包括问号?)。如果没有查询字符串,则返回空字符串。window.location.hash:返回当前页面的哈希部分(即#后的部分)。如果没有哈希,则返回空字符串。window.location.assign:通过指定的 URL 加载一个新的页面。assign()方法会将当前页面的 URL 替换为新的 URL,并保留在浏览器的历史记录中。window.location.replace:加载新的页面并替换当前页面在历史记录中的条目。也就是说,使用replace()方法后,用户不能使用浏览器的“后退”按钮返回到原来的页面。window.location.reload():重新加载当前页面。可以通过传递true参数来强制浏览器重新加载页面,而不是使用缓存。
代码
<script>
// 获取当前 URL
console.log(window.location.href); // 输出当前页面的完整 URL
// 修改页面 URL(跳转到新页面)
// window.location.href = 'https://www.example.com'; // 页面跳转到指定 URL
// 获取页面的协议、主机名、路径、查询字符串等
console.log('Protocol: ' + window.location.protocol); // 输出 "https:"
console.log('Hostname: ' + window.location.hostname); // 输出 "www.example.com"
console.log('Pathname: ' + window.location.pathname); // 输出 "/page.html"
console.log('Search: ' + window.location.search); // 输出 "?id=123"
// 跳转到一个新页面
// window.location.assign('https://www.newpage.com');
// 替换当前页面的 URL
// window.location.replace('https://www.replacement.com');
// 重新加载当前页面
// window.location.reload();
</script>

12.5window对象的frames属性
window.frames 是一个指向当前浏览器窗口中所有 <iframe> 元素的集合的属性,它是 window 对象的一部分。使用 frames 属性,开发者可以访问并操作嵌套在当前窗口中的所有 <iframe> 元素。
window.frames[0]:获取第一个 iframe。
window.frames["frame1"]:获取 name="frame1" 的 iframe。
iframe.document:一旦你获得了对 iframe 的引用,你可以访问其内部的 document 对象,类似于访问主页面中的 document,并且可以修改它的内容、样式等。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>window.frames 示例</title>
</head>
<body>
<!-- 创建多个 iframe -->
<iframe src="https://www.taobao.com" name="frame1" width="300" height="200"></iframe>
<iframe src="https://www.jd.com/" name="frame2" width="300" height="200"></iframe>
<iframe src="https://www.pinduoduo.com/" name="frame3" width="300" height="200"></iframe>
<script>
console.log(window.frames)
// // 访问第一个 iframe,并改变它的背景色
// window.frames[0].document.body.style.backgroundColor = "lightblue";
//
// // 通过 name 访问第二个 iframe,并改变其 URL
// // window.frames['frame2'].location.href = 'https://www.example.com';
//
// // 获取第一个 iframe 中的 document 对象,并访问其中的内容
// var iframeDoc = window.frames[0].document;
// console.log(iframeDoc.title); // 输出第一个 iframe 的文档标题
</script>
</body>
</html>

12.6window对象的history属性
window.history 是 BOM(浏览器对象模型) 中的一个属性,提供了对浏览器会话历史记录的访问和控制功能。通过 window.history,你可以控制浏览器的前进、后退、跳转等操作,允许你在不重新加载页面的情况下操控浏览器的历史记录。
window.history 是一个 History 对象,代表浏览器的历史记录栈。它提供了几个方法和属性来控制浏览器的历史记录,允许你向历史记录中添加、移除、前进、后退等。
history.back():相当于点击浏览器的“后退”按钮,回到上一个历史记录项。
history.forward():相当于点击浏览器的“前进”按钮,回到下一个历史记录项(如果有的话)。
history.go():允许你通过给定的数字,向前或向后跳转指定数量的历史记录项。可以使用负数表示后退,正数表示前进。
history.pushState():将新的状态对象添加到历史记录栈中,同时修改浏览器的 URL,但不重新加载页面。适用于单页面应用(SPA),允许你在页面之间切换时更新 URL。
history.replaceState():与 pushState() 类似,但它不会添加新的历史记录项,而是替换当前的历史记录项。这对于修改当前页面的 URL 而不改变历史记录非常有用。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>History API 示例</title>
</head>
<body>
<h1>浏览器历史记录操作示例</h1>
<button id="goBack">后退</button>
<button id="goForward">前进</button>
<button id="goToPage2">跳转到 Page 2</button>
<button id="replaceStateButton">替换当前历史记录</button>
<p>当前页面: <span id="currentPage">Page 1</span></p>
<script>
// 获取 DOM 元素
const goBackButton = document.getElementById("goBack");
const goForwardButton = document.getElementById("goForward");
const goToPage2Button = document.getElementById("goToPage2");
const replaceStateButton = document.getElementById("replaceStateButton");
const currentPageSpan = document.getElementById("currentPage");
// 初始化页面
function updatePageState(page) {
currentPageSpan.textContent = page;
}
// 为按钮添加事件处理程序
// 后退按钮
goBackButton.addEventListener("click", function () {
window.history.back(); // 后退一步
});
// 前进按钮
goForwardButton.addEventListener("click", function () {
window.history.forward(); // 前进一步
});
// 跳转到 Page 2
goToPage2Button.addEventListener("click", function () {
// 使用 pushState 改变 URL 和状态,跳转到 Page 2
history.pushState({ page: 2 }, "Page 2", "page2.html");
updatePageState("Page 2"); // 更新页面显示的内容
});
// 替换当前历史记录
replaceStateButton.addEventListener("click", function () {
// 使用 replaceState 替换当前历史记录为 Page 3
history.replaceState({ page: 3 }, "Page 3", "page3.html");
updatePageState("Page 3"); // 更新页面显示的内容
});
// 监听历史记录的变化
window.addEventListener("popstate", function (event) {
// 监听浏览器历史记录的变化,根据状态更新页面内容
if (event.state) {
if (event.state.page === 2) {
updatePageState("Page 2");
} else if (event.state.page === 3) {
updatePageState("Page 3");
} else {
updatePageState("Page 1");
}
}
});
</script>
</body>
</html>
12.7window对象的screen属性
window.screen 是 BOM(浏览器对象模型) 中的一个属性,代表浏览器窗口的屏幕信息。它提供了关于显示设备(如分辨率、屏幕宽度和高度等)的一些信息。这些信息主要是与浏览器窗口的物理显示区域相关的,而不是浏览器视口的尺寸。
window.screen 返回一个 Screen 对象,包含与用户显示设备相关的多个属性。
screen.width:返回屏幕的宽度(以像素为单位)。screen.height:返回屏幕的高度(以像素为单位)。screen.availWidth:返回浏览器可用的屏幕宽度(即不包括操作系统的任务栏等占用的区域)。screen.availHeight:返回浏览器可用的屏幕高度(即不包括操作系统的任务栏等占用的区域)。screen.colorDepth:返回显示器的颜色深度,表示每个像素的位数(通常是 8、16、24 或 32 位)。screen.pixelDepth::与colorDepth类似,返回显示器的像素深度(通常是每个像素的位数)。screen.orientation(现代浏览器支持):返回屏幕的当前方向。它可以是 "portrait"(竖屏)或者 "landscape"(横屏)。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Screen 属性示例</title>
</head>
<body>
<h1>浏览器屏幕信息</h1>
<p>屏幕宽度:<span id="screenWidth"></span></p>
<p>屏幕高度:<span id="screenHeight"></span></p>
<p>可用宽度:<span id="availWidth"></span></p>
<p>可用高度:<span id="availHeight"></span></p>
<p>颜色深度:<span id="colorDepth"></span></p>
<p>屏幕方向:<span id="orientation"></span></p>
<script>
// 获取并显示屏幕信息
document.getElementById('screenWidth').textContent = window.screen.width;
document.getElementById('screenHeight').textContent = window.screen.height;
document.getElementById('availWidth').textContent = window.screen.availWidth;
document.getElementById('availHeight').textContent = window.screen.availHeight;
document.getElementById('colorDepth').textContent = window.screen.colorDepth;
// 判断屏幕方向(适用于支持的浏览器)
if (window.screen.orientation) {
document.getElementById('orientation').textContent = window.screen.orientation.type;
} else {
document.getElementById('orientation').textContent = "无法获取屏幕方向";
}
</script>
</body>
</html>

📌 创作不易,感谢支持!
每一篇内容都凝聚了心血与热情,如果我的内容对您有帮助,欢迎请我喝杯咖啡☕,您的支持是我持续分享的最大动力!

浙公网安备 33010602011771号