| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> JavaScript知识库 -> web前端必备的 JavaScript 基础知识梳理总结 -> 正文阅读 |
|
[JavaScript知识库]web前端必备的 JavaScript 基础知识梳理总结 |
如今,JavaScript 已经成为了与 HTML/CSS 完全集成的,使用最广泛的浏览器语言。 有很多其他的语言可以被“编译”成 JavaScript,这些语言还提供了更多的功能。建议最好了解一下这些语言,至少在掌握了 JavaScript 之后大致的了解一下。
let — 现代的变量声明方式。 var — 老旧的变量声明方式。一般情况下,我们不会再使用它。但是,我们会在 旧时的 “var” 章节介绍 var 和 let 的微妙差别,以防你需要它们。 const — 类似于 let,但是变量的值无法被修改。 变量应当以一种容易理解变量内部是什么的方式进行命名。
number 用于任何类型的数字:整数或浮点数,在 ±(253-1) 范围内的整数。 bigint 用于任意长度的整数。 string 用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。 boolean 用于 true 和 false。 null 用于未知的值 —— 只有一个 null 值的独立类型。 undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。 symbol 用于唯一的标识符。 object 用于更复杂的数据结构。 我们可以通过 typeof 运算符查看存储在变量中的数据类型。 两种形式:typeof x 或者 typeof(x)。 以字符串的形式返回类型名称,例如 “string”。 typeof null 会返回 “object” —— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个 object。
字符串转换 —— 转换发生在输出内容的时候,也可以通过 String(value) 进行显式转换。原始类型值的 string 类型转换通常是很明显的。 数字型转换 —— 转换发生在进行算术操作时,也可以通过 Number(value) 进行显式转换。 数字型转换遵循以下规则: 图片 图片 对 “0” 和只有空格的字符串(比如:" ")进行布尔型转换时,输出结果为 true。 字符串的比较,会按照“词典”顺序逐字符地比较大小。 当对不同类型的值进行比较时,它们会先被转化为数字(不包括严格相等检查)再进行比较。 在非严格相等 == 下,null 和 undefined 相等且各自不等于任何其他的值。 在使用 > 或 < 进行比较时,需要注意变量可能为 null/undefined 的情况。比较好的方法是单独检查变量是否等于 null/undefined。
// 当 height 的值为 null 或 undefined 时,将 height 的值设置为 100 如果没有明确添加括号,不能将其与 || 或 && 一起使用。
while —— 每次迭代之前都要检查条件。 do…while —— 每次迭代后都要检查条件。 for (;😉 —— 每次迭代之前都要检查条件,可以使用其他设置。 通常使用 while(true) 来构造“无限”循环。这样的循环和其他循环一样,都可以通过 break 指令来终止。 如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么可以使用 continue 指令。 break/continue 支持循环前的标签。标签是 break/continue 跳出嵌套循环以转到外部的唯一方法。
function name(parameters, delimited, by, comma) { 函数可以访问外部变量。但它只能从内到外起作用。函数外部的代码看不到函数内的局部变量。 函数可以返回值。如果没有返回值,则其返回的结果是 undefined。 为了使代码简洁易懂,建议在函数中主要使用局部变量和参数,而不是外部变量。 与不获取参数但将修改外部变量作为副作用的函数相比,获取参数、使用参数并返回结果的函数更容易理解。 函数命名: 函数名应该清楚地描述函数的功能。当我们在代码中看到一个函数调用时,一个好的函数名能够让我们马上知道这个函数的功能是什么,会返回什么。 一个函数是一个行为,所以函数名通常是动词。 目前有许多优秀的函数名前缀,如 create…、show…、get…、check… 等等。使用它们来提示函数的作用吧。
如果函数在主代码流中被声明为单独的语句,则称为“函数声明”。 如果该函数是作为表达式的一部分创建的,则称其“函数表达式”。 在执行代码块之前,内部算法会先处理函数声明。所以函数声明在其被声明的代码块内的任何位置都是可见的。 函数表达式在执行流程到达时创建。 在大多数情况下,当我们需要声明一个函数时,最好使用函数声明,因为函数在被声明之前也是可见的。这使我们在代码组织方面更具灵活性,通常也会使得代码可读性更高。 所以,仅当函数声明不适合对应的任务时,才应使用函数表达式。
不带花括号:(…args) => expression — 右侧是一个表达式:函数计算表达式并返回其结果。 带花括号:(…args) => { body } — 花括号允许我们在函数中编写多个语句,但是我们需要显式地 return 来返回一些内容。
它们存储属性(键值对),其中: 属性的键必须是字符串或者 symbol(通常是字符串)。 值可以是任何类型。 我们可以用下面的方法访问属性: 点符号: obj.property。 方括号 obj[“property”],方括号允许从变量中获取键,例如 obj[varWithKey]。 其他操作: 删除属性:delete obj.prop。 检查是否存在给定键的属性:“key” in obj。 遍历对象:for(let key in obj) 循环。 我们在这一章学习的叫做“普通对象(plain object)”,或者就叫对象。 JavaScript 中还有很多其他类型的对象: Array 用于存储有序数据集合, Date 用于存储时间日期, Error 用于存储错误信息。 ……等等。 它们有着各自特别的特性,我们将在后面学习到。有时候大家会说“Array 类型”或“Date 类型”,但其实它们并不是自身所属的类型,而是属于一个对象类型即 “object”。它们以不同的方式对 “object” 做了一些扩展。
所有通过被拷贝的引用的操作(如添加、删除属性)都作用在同一个对象上。 为了创建“真正的拷贝”(一个克隆),我们可以使用 Object.assign 来做所谓的“浅拷贝”(嵌套对象被通过引用进行拷贝)或者使用“深拷贝”函数,例如 _.cloneDeep(obj)[1]。
方法允许对象进行像 object.doSomething() 这样的“操作”。 方法可以将对象引用为 this。 this 的值是在程序运行时得到的。 一个函数在声明时,可能就使用了 this,但是这个 this 只有在函数被调用时才会有值。 可以在对象之间复制函数。 以“方法”的语法调用函数时:object.method(),调用过程中的 this 值是 object。 请注意箭头函数有些特别:它们没有 this。在箭头函数内部访问到的 this 都是从外部获取的。
obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。 obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。 obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined。 正如我们所看到的,这些语法形式用起来都很简单直接。?. 检查左边部分是否为 null/undefined,如果不是则继续运算。 ?. 链使我们能够安全地访问嵌套属性。 但是,我们应该谨慎地使用 ?.,仅在当左边部分不存在也没问题的情况下使用为宜。以保证在代码中有编程上的错误出现时,也不会对我们隐藏。
Symbol 是使用带有可选描述(name)的 Symbol() 调用创建的。 Symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 Symbol 相等,那么我们应该使用全局注册表:Symbol.for(key) 返回(如果需要的话则创建)一个以 key 作为名字的全局 Symbol。使用 Symbol.for 多次调用 key 相同的 Symbol 时,返回的就是同一个 Symbol。 Symbol 有两个主要的使用场景: “隐藏” 对象属性。如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 Symbol 并使用它作为属性的键。Symbol 属性不会出现在 for…in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。 JavaScript 使用了许多系统 Symbol,这些 Symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内置行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代[2] 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换[3] 等等。
将 “e” 和 0 的数量附加到数字后。就像:123e6 与 123 后面接 6 个 0 相同。 “e” 后面的负数将使数字除以 1 后面接着给定数量的零的数字。例如 123e-6 表示 0.000123(123 的百万分之一)。 对于不同的数字系统: 可以直接在十六进制(0x),八进制(0o)和二进制(0b)系统中写入数字。 parseInt(str,base) 将字符串 str 解析为在给定的 base 数字系统中的整数,2 ≤ base ≤ 36。 num.toString(base) 将数字转换为在给定的 base 数字系统中的字符串。 要将 12pt 和 100px 之类的值转换为数字: 使用 parseInt/parseFloat 进行“软”转换,它从字符串中读取数字,然后返回在发生 error 前可以读取到的值。 使用 Math.floor,Math.ceil,Math.trunc,Math.round 或 num.toFixed(precision) 进行舍入。 请确保记住使用小数时会损失精度。 更多数学函数: 需要时请查看 Math[6] 对象。这个库很小,但是可以满足基本的需求。 JavaScript 中的字符串使用的是 UTF-16 编码。 我们可以使用像 \n 这样的特殊字符或通过使用 \u… 来操作它们的 unicode 进行字符插入。 获取字符时,使用 []。 获取子字符串,使用 slice 或 substring。 字符串的大/小写转换,使用:toLowerCase/toUpperCase。 查找子字符串时,使用 indexOf 或 includes/startsWith/endsWith 进行简单检查。 根据语言比较字符串时使用 localeCompare,否则将按字符代码进行比较。 还有其他几种有用的字符串方法: str.trim() —— 删除字符串前后的空格 (“trims”)。 str.repeat(n) —— 重复字符串 n 次。 ……更多内容细节请参见 手册[7]。
声明: // 方括号 (常见用法) let arr = [item1, item2…]; // new Array (极其少见) let arr = new Array(item1, item2…); 调用 new Array(number) 会创建一个给定长度的数组,但不含有任何项。 length 属性是数组的长度,准确地说,它是数组最后一个数字索引值加一。它由数组方法自动调整。 如果我们手动缩短 length,那么数组就会被截断。 我们可以通过下列操作以双端队列的方式使用数组: push(…items) 在末端添加 items 项。 pop() 从末端移除并返回该元素。 shift() 从首端移除并返回该元素。 unshift(…items) 从首端添加 items 项。 遍历数组的元素: for (let i=0; i<arr.length; i++) — 运行得最快,可兼容旧版本浏览器。 for (let item of arr) — 现代语法,只能访问 items。 for (let i in arr) — 永远不要用这个。 比较数组时,不要使用 == 运算符(当然也不要使用 > 和 < 等运算符),因为它们不会对数组进行特殊处理。它们通常会像处理任意对象那样处理数组,这通常不是我们想要的。 但是,我们可以使用 for…of 循环来逐项比较数组。
添加/删除元素: push(…items) —— 向尾端添加元素, pop() —— 从尾端提取一个元素, shift() —— 从首端提取一个元素, unshift(…items) —— 向首端添加元素, splice(pos, deleteCount, …items) —— 从 pos 开始删除 deleteCount 个元素,并插入 items。 slice(start, end) —— 创建一个新数组,将从索引 start 到索引 end(但不包括 end)的元素复制进去。 concat(…items) —— 返回一个新数组:复制当前数组的所有元素,并向其中添加 items。如果 items 中的任意一项是一个数组,那么就取其元素。 搜索元素: indexOf/lastIndexOf(item, pos) —— 从索引 pos 开始搜索 item,搜索到则返回该项的索引,否则返回 -1。 includes(value) —— 如果数组有 value,则返回 true,否则返回 false。 find/filter(func) —— 通过 func 过滤元素,返回使 func 返回 true 的第一个值/所有值。 findIndex 和 find 类似,但返回索引而不是值。 遍历元素: forEach(func) —— 对每个元素都调用 func,不返回任何内容。 map(func) —— 根据对每个元素调用 func 的结果创建一个新数组。 sort(func) —— 对数组进行原位(in-place)排序,然后返回它。 reverse() —— 原位(in-place)反转数组,然后返回它。 split/join —— 将字符串转换为数组并返回。 reduce/reduceRight(func, initial) —— 通过对每个元素调用 func 计算数组上的单个值,并在调用之间传递中间结果。 其他: Array.isArray(arr) 检查 arr 是否是一个数组。 这些是最常用的方法,它们覆盖 99% 的用例。但是还有其他几个: arr.some(fn)[8]/arr.every(fn)[9] 检查数组。 这两个方法的行为类似于 || 和 && 运算符:如果 fn 返回一个真值,arr.some() 立即返回 true 并停止迭代其余数组项;如果 fn 返回一个假值,arr.every() 立即返回 false 并停止对其余数组项的迭代。 我们可以使用 every 来比较数组: function arraysEqual(arr1, arr2) { alert( arraysEqual([1, 2], [1, 2])); // true arr.copyWithin(target, start, end)[11] —— 将从位置 start 到 end 的所有元素复制到 自身 的 target 位置(覆盖现有元素)。 arr.flat(depth)[12]/arr.flatMap(fn)[13] 从多维数组创建一个新的扁平数组。 Array.of(element0[, element1[, …[, elementN]]])[14] 基于可变数量的参数创建一个新的 Array 实例,而不需要考虑参数的数量或类型。 有关完整列表,请参阅 手册[15]。
技术上来说,可迭代对象必须实现 Symbol.iterator 方法。 objSymbol.iterator 的结果被称为 迭代器(iterator)。由它处理进一步的迭代过程。 一个迭代器必须有 next() 方法,它返回一个 {done: Boolean, value: any} 对象,这里 done:true 表明迭代结束,否则 value 就是下一个值。 Symbol.iterator 方法会被 for…of 自动调用,但我们也可以直接调用它。 内置的可迭代对象例如字符串和数组,都实现了 Symbol.iterator。 字符串迭代器能够识别代理对(surrogate pair)。(译注:代理对也就是 UTF-16 扩展字符。) 有索引属性和 length 属性的对象被称为 类数组对象。这种对象可能还具有其他属性和方法,但是没有数组的内建方法。 如果我们仔细研究一下规范 —— 就会发现大多数内建方法都假设它们需要处理的是可迭代对象或者类数组对象,而不是“真正的”数组,因为这样抽象度更高。 Array.from(obj[, mapFn, thisArg]) 将可迭代对象或类数组对象 obj 转化为真正的数组 Array,然后我们就可以对它应用数组的方法。可选参数 mapFn 和 thisArg 允许我们将函数应用到每个元素。
方法和属性如下: new Map([iterable]) —— 创建 map,可选择带有 [key,value] 对的 iterable(例如数组)来进行初始化。 map.set(key, value) —— 根据键存储值。 map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined。 map.has(key) —— 如果 key 存在则返回 true,否则返回 false。 map.delete(key) —— 删除指定键的值。 map.clear() —— 清空 map 。 map.size —— 返回当前元素个数。 与普通对象 Object 的不同点: 任何键、对象都可以作为键。 有其他的便捷方法,如 size 属性。 Set —— 是一组唯一值的集合。 方法和属性: new Set([iterable]) —— 创建 set,可选择带有 iterable(例如数组)来进行初始化。 set.add(value) —— 添加一个值(如果 value 存在则不做任何修改),返回 set 本身。 set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false。 set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false。 set.clear() —— 清空 set。 set.size —— 元素的个数。 在 Map 和 Set 中迭代总是按照值插入的顺序进行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其编号来获取元素。
WeakSet 是类似于 Set 的集合,它仅存储对象,并且一旦通过其他方式无法访问它们,便会将其删除。 它们都不支持引用所有键或其计数的方法和属性。仅允许单个操作。 WeakMap 和 WeakSet 被用作“主要”对象存储之外的“辅助”数据结构。一旦将对象从主存储器中删除,如果该对象仅被用作 WeakMap 或 WeakSet 的键,那么它将被自动清除。
解构对象的完整语法: let {prop : varName = default, …rest} = object 这表示属性 prop 会被赋值给变量 varName,如果没有这个属性的话,就会使用默认值 default。 没有对应映射的对象属性会被复制到 rest 对象。 解构数组的完整语法: let [item1 = default, item2, …rest] = array 数组的第一个元素被赋值给 item1,第二个元素被赋值给 item2,剩下的所有元素被复制到另一个数组 rest。 从嵌套数组/对象中提取数据也是可以的,此时等号左侧必须和等号右侧有相同的结构。 月份从 0 开始计数(对,一月是 0)。 一周中的某一天 getDay() 同样从 0 开始计算(0 代表星期日)。 当设置了超出范围的组件时,Date 会进行自我校准。这一点对于日/月/小时的加减很有用。 日期可以相减,得到的是以毫秒表示的两者的差值。因为当 Date 被转换为数字时,Date 对象会被转换为时间戳。 使用 Date.now() 可以更快地获取当前时间的时间戳。 和其他系统不同,JavaScript 中时间戳以毫秒为单位,而不是秒。 有时我们需要更加精准的时间度量。JavaScript 自身并没有测量微秒的方法(百万分之一秒),但大多数运行环境会提供。例如:浏览器有 performance.now()[17] 方法来给出从页面加载开始的以毫秒为单位的微秒数(精确到毫秒的小数点后三位): alert(
JSON 支持 object,array,string,number,boolean 和 null。 JavaScript 提供序列化(serialize)成 JSON 的方法 JSON.stringify[18] 和解析 JSON 的方法 JSON.parse[19]。 这两种方法都支持用于智能读/写的转换函数。 如果一个对象具有 toJSON,那么它会被 JSON.stringify 调用。
递归 是编程的一个术语,表示从自身调用函数(译注:也就是自调用)。递归函数可用于以更优雅的方式解决问题。 递归定义[20] 的数据结构是指可以使用自身来定义的数据结构。 list = { value, next -> list } 就像我们在示例 sumSalary 中看到的那样,可以使用递归函数来遍历它们。 任何递归函数都可以被重写为迭代(译注:也就是循环)形式。有时这是在优化代码时需要做的。但对于大多数任务来说,递归方法足够快,并且容易编写和维护。
有一个简单的方法可以区分它们: 若 … 出现在函数参数列表的最后,那么它就是 rest 参数,它会把参数列表中剩余的参数收集到一个数组中。 若 … 出现在函数调用或类似的表达式中,那它就是 spread 语法,它会把一个数组展开为列表。 使用场景: Rest 参数用于创建可接受任意数量参数的函数。 Spread 语法用于将数组传递给通常需要含有许多参数的列表的函数。 它们俩的出现帮助我们轻松地在列表和参数数组之间来回转换。 “旧式”的 arguments(类数组且可迭代的对象)也依然能够帮助我们获取函数调用中的所有参数。
其中包括 JavaScript 的内建方法,例如 “Array” 和环境特定(environment-specific)的值,例如 window.innerHeight — 浏览器中的窗口高度。 全局对象有一个通用名称 globalThis。 ……但是更常见的是使用“老式”的环境特定(environment-specific)的名字,例如 window(浏览器)和 global(Node.js)。 仅当值对于我们的项目而言确实是全局的时,才应将其存储在全局对象中。并保持其数量最少。 在浏览器中,除非我们使用 modules[21],否则使用 var 声明的全局函数和变量会成为全局对象的属性。 为了使我们的代码面向未来并更易于理解,我们应该使用直接的方式访问全局对象的属性,如 window.x。
我们介绍了它们的一些属性: name —— 函数的名字。通常取自函数定义,但如果函数定义时没设定函数名,JavaScript 会尝试通过函数的上下文猜一个函数名(例如把赋值的变量名取为函数名)。 length —— 函数定义时的入参的个数。Rest 参数不参与计数。 如果函数是通过函数表达式的形式被声明的(不是在主代码流里),并且附带了名字,那么它被称为命名函数表达式(Named Function Expression)。这个名字可以用于在该函数内部进行自调用,例如递归调用等。 此外,函数可以带有额外的属性。很多知名的 JavaScript 库都充分利用了这个功能。 它们创建一个“主”函数,然后给它附加很多其它“辅助”函数。例如,jQuery[22] 库创建了一个名为 $ 的函数。lodash[23] 库创建一个 _ 函数,然后为其添加了 .add、.keyBy 以及其它属性(想要了解更多内容,参查阅 docs[24])。实际上,它们这么做是为了减少对全局空间的污染,这样一个库就只会有一个全局变量。这样就降低了命名冲突的可能性。 所以,一个函数本身可以完成一项有用的工作,还可以在自身的属性中附带许多其他功能。
let func = new Function ([arg1, arg2, …argN], functionBody); 以下三种声明的含义相同: new Function(‘a’, ‘b’, ‘return a + b’); // 基础语法
要取消函数的执行,我们应该调用 clearInterval/clearTimeout,并将 setInterval/setTimeout 返回的值作为入参传入。 嵌套的 setTimeout 比 setInterval 用起来更加灵活,允许我们更精确地设置两次执行之间的时间。 零延时调度 setTimeout(func, 0)(与 setTimeout(func) 相同)用来调度需要尽快执行的调用,但是会在当前脚本执行完成后进行调用。 浏览器会将 setTimeout 或 setInterval 的五层或更多层嵌套调用(调用五次之后)的最小延时限制在 4ms。这是历史遗留问题。 请注意,所有的调度方法都不能 保证 确切的延时。 例如,浏览器内的计时器可能由于许多原因而变慢: CPU 过载。 浏览器页签处于后台模式。 笔记本电脑用的是电池供电(译注:使用电池供电会以降低性能为代价提升续航)。 所有这些因素,可能会将定时器的最小计时器分辨率(最小延迟)增加到 300ms 甚至 1000ms,具体以浏览器及其设置为准。
装饰器可以被看作是可以添加到函数的 “features” 或 “aspects”。我们可以添加一个或添加多个。而这一切都无需更改其代码! 为了实现 cachingDecorator,我们研究了以下方法: func.call(context, arg1, arg2…)[25] —— 用给定的上下文和参数调用 func。 func.apply(context, args)[26] —— 调用 func 将 context 作为 this 和类数组的 args 传递给参数列表。 通用的 呼叫转移(call forwarding) 通常是使用 apply 完成的: let wrapper = function() {
通常我们应用 bind 来绑定对象方法的 this,这样我们就可以把它们传递到其他地方使用。例如,传递给 setTimeout。 当我们绑定一个现有的函数的某些参数时,绑定后的(不太通用的)函数被称为 partially applied 或 partial。 当我们不想一遍又一遍地重复相同的参数时,partial 非常有用。就像我们有一个 send(from, to) 函数,并且对于我们的任务来说,from 应该总是一样的,那么我们就可以搞一个 partial 并使用它。
|
|
JavaScript知识库 最新文章 |
ES6的相关知识点 |
react 函数式组件 & react其他一些总结 |
Vue基础超详细 |
前端JS也可以连点成线(Vue中运用 AntVG6) |
Vue事件处理的基本使用 |
Vue后台项目的记录 (一) |
前后端分离vue跨域,devServer配置proxy代理 |
TypeScript |
初识vuex |
vue项目安装包指令收集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/27 20:38:09- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |