首页
关于
Search
1
阿里云更换SSL证书
308 阅读
2
Nginx+Lua脚本控制负载均衡
175 阅读
3
地址相似度算法
166 阅读
4
【学习笔记】ES新特性
163 阅读
5
原生Hadoop搭建
140 阅读
默认分类
技术
JAVA
python
前端
大数据
运维
登录
Search
标签搜索
学习笔记
Javascript
前端
算法
负载均衡
Lua
Nginx
Kafka
Hive
Hbase
大数据
Hadoop
gitlab
CSS
HTML
ES语法
规则引擎
Drools
模型
springboot
枉自朝朝暮暮
累计撰写
12
篇文章
累计收到
1
条评论
首页
栏目
默认分类
技术
JAVA
python
前端
大数据
运维
页面
关于
搜索到
3
篇与
的结果
2023-05-11
【学习笔记】ES新特性
ES新特性1.概念ES全程EcmaScript,是脚本语言的规范,Javascript是EcmaScript的一种实现,所以ES新特性就是指JavaScript的新特性。1.1.编年史年份版本名称正式名称主要新增特性重要事件/说明1997ECMAScript 1ES1首个标准版本,定义了 JavaScript 的基础语法和功能。由 ECMA 国际组织发布,对应 ECMA-262 标准第一版。1998ECMAScript 2ES2小幅更新,与 ISO/IEC 16262 标准同步。修正规范格式,无重大功能新增。1999ECMAScript 3ES3正则表达式、try/catch 异常处理、instanceof、typeof、for...in 等。成为 JavaScript 的通行标准,广泛支持,奠定了现代 JavaScript 的基础。2007ECMAScript 4未发布模块、类、静态类型、泛型等激进特性(草案)。草案因争议被终止,部分功能被拆分到后续版本。2008ECMAScript 3.1ES5(重命名)基于 ES3 的小改进(如 JSON 支持、严格模式 use strict)。原计划为 ES4 的子集,后更名为 ES5。2009ECMAScript 5ES5严格模式、JSON 支持、新对象方法(如 Object.create)、数组迭代方法等。正式发布,成为主流浏览器支持的版本。2011ECMAScript 5.1ES5.1与 ISO/IEC 16262 国际标准同步,修正 ES5 的部分缺陷。成为 ISO 国际标准(ISO/IEC 16262:2011)。2013ECMAScript 6 草案ES6(草案冻结)类、模块、箭头函数、解构赋值、Promise、模板字符串等。草案冻结,不再添加新功能,进入讨论期。2015ECMAScript 2015ES6 / ES2015类、模块、箭头函数、解构赋值、let/const、Promise、模板字符串等。正式发布,首次按年份命名(ES6 后改称 ES2015),是 JavaScript 的重大升级版本。2016ECMAScript 2016ES7 / ES2016指数运算符(**)、Array.prototype.includes()。首个年度版本,新增特性较少但保持持续更新。2017ECMAScript 2017ES8 / ES2017Object.values()/keys()、async/await、for...of 与 let/const 支持。引入 async/await 简化异步编程。2018ECMAScript 2018ES9 / ES2018Promise.prototype.finally()、async 函数的 iterable 支持、Rest/Spread 运算符。增强异步编程和语法糖。2019ECMAScript 2019ES10 / ES2019Array.prototype.flat()/flatMap()、Object.fromEntries()、String.trimStart()/trimEnd()。更多数组和字符串方法的优化。2020ECMAScript 2020ES11 / ES2020Nullish 合并运算符(??)、动态导入(import())、BigInt、Promise.allSettled()。引入 BigInt 处理大整数,增强异步控制。2021ECMAScript 2021ES12 / ES2021Logical Assignment Operators(&&=、` 2.ES62.1.迭代器通过实现 Symbol.iterator 方法,可以自定义可迭代对象。const customIterable = { data: [10, 20, 30], [Symbol.iterator]() { let index = 0; const data = this.data; return { next() { if (index < data.length) { return { value: data[index++], done: false }; } else { return { done: true }; } } }; } }; for (const num of customIterable) { console.log(num); // 输出 10, 20, 30 }2.2.生成器生成器是一种特殊的函数,使用 function* 定义,通过 yield 关键字暂停和恢复执行,自动实现迭代器协议。yield 会暂停函数执行,返回一个值给外部迭代器,下次调用 next() 时从暂停处继续执行。function* simpleGenerator() { yield 1; yield 2; yield 3; } const gen = simpleGenerator(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.return(100)); // 提前终止,{ value: 100, done: true }function* infiniteSequence() { let i = 0; while (true) { yield i++; } } const gen = infiniteSequence(); console.log(gen.next().value); // 0 console.log(gen.next().value); // 1 // 无限继续...function* fibonacci() { let a = 0, b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } const fib = fibonacci(); console.log(fib.next().value); // 0 console.log(fib.next().value); // 1 console.log(fib.next().value); // 1 console.log(fib.next().value); // 22.3.PromisePromise 是一个表示异步操作最终完成或失败的对象,代表了某个未来才会知道结果的事件(如网络请求、文件读写等)。用途:处理异步操作(如网络请求、文件读取),避免回调地狱(Callback Hell)。状态:pending(进行中):初始状态,既未成功也未失败。fulfilled(已成功):操作成功完成,调用 resolve 方法。rejected(已失败):操作失败,调用 reject 方法。特点:状态一旦改变(如从 pending 到 fulfilled 或 rejected),就不可逆,且后续操作只能通过 then 或 catch 处理结果。2.3.1.创建Promise通过 new Promise(executor) 构造函数创建:const myPromise = new Promise((resolve, reject) => { // 异步操作(如网络请求) const success = true; if (success) { resolve("操作成功"); // 成功时调用 resolve } else { reject("操作失败"); // 失败时调用 reject } });2.3.2.基础使用then:处理成功状态,接受两个参数(成功回调和失败回调):myPromise .then((value) => { console.log(value); // 输出 "操作成功" }, (error) => { console.error(error); });catch:专门处理失败状态,等同于.then(null, rejectCallback):myPromise .then((value) => console.log(value)) .catch((error) => console.error(error));finally 方法:无论成功或失败,都会执行 finally 回调(ES2018 引入):myPromise .then(data => console.log(data)) .catch(error => console.error(error)) .finally(() => console.log("操作完成"));示例代码:// 模拟异步请求 function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { Math.random() > 0.5 ? resolve("数据加载成功") : reject("网络错误"); }, 1000); }); } // 使用示例 fetchData() .then(data => console.log(data)) .catch(error => console.error(error)) .finally(() => console.log("请求结束"));2.3.3.应用场景网络请求:fetch("https://api.example.com/data") .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error("请求失败", error));文件读写:const readFile = (path) => new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err); else resolve(data); }); });延时操作:const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); delay(1000).then(() => console.log("1秒后执行"));2.3.4.高级用法(1) Promise.all()并行执行多个 Promise,全部成功后返回结果数组:const promise1 = Promise.resolve(1); const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000)); const promise3 = Promise.reject("错误"); Promise.all([promise1, promise2]) .then((results) => console.log(results)); // [1, 2] Promise.all([promise1, promise2, promise3]) .catch((error) => console.error(error)); // "错误"(2) Promise.race()返回最先完成的 Promise 结果或错误:const fastPromise = Promise.resolve("快"); const slowPromise = new Promise((resolve) => setTimeout(() => resolve("慢"), 2000)); Promise.race([fastPromise, slowPromise]) .then((result) => console.log(result)); // "快"(3) Promise.allSettled()(ES2020)等待所有 Promise 完成(无论成功或失败):const promises = [ Promise.resolve("成功"), Promise.reject("失败"), ]; Promise.allSettled(promises).then((results) => { results.forEach((result) => { if (result.status === "fulfilled") { console.log("成功:", result.value); } else { console.log("失败:", result.reason); } }); });2.4.模块化将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。2.4.1.语法export用于暴露规定模块的对外接口import用于引入外部的模块接口示例//util.js export function isNull(){} export function isNotNull(){} //main.js import * from "./utils.js" import {isNUll, isNotNull} from "./utils.js" import {isNUll} from "./utils.js"html中编写:<script type="module"> import语句 </script2.4.2.暴露方式分别暴露export let a = 1; export function isNull(){} export function isNotNull(){}统一暴露let a = ; function isNUll(){} export { a, isNull }默认暴露export default { name: "angsan" play: function (){ console.log("play basketball") } }2.4.3.引入方式通用方式:import * as m1 from "./a.js"解构赋值方式:import {name as nickname, play} from "./a.js"、import {default as m1} from "./a.js"简单形式(针对默认暴露):import m1 from "./a.js"ES83.1.async&awaitasync: 声明一个异步函数,自动将函数返回值包装为 Promise。该函数返回一个 Promise 对象。如果函数返回一个普通值(如 return 1),则自动包装为 Promise.resolve(值)。如果函数抛出异常(如 throw new Error),则自动包装为 Promise.reject(错误)。async function fetchData() { // 异步操作(返回 Promise) return "数据"; } // 调用 async 函数 const result = fetchData(); // 返回 Promise result.then(data => console.log(data)); // "数据"await:暂停异步函数的执行,等待 Promise 完成并返回结果。暂停当前 async 函数的执行,直到等待的 Promise 完成(fulfilled 或 rejected)。不会阻塞主线程,其他代码可以继续执行。只能在 async 函数内部使用async function getData() { try { const response = await fetch("https://api.example.com/data"); // 等待 Promise const data = await response.json(); // 等待下一个 Promise console.log(data); } catch (error) { console.error("错误:", error); } } getData();执行流程示例async function asyncFunc() { console.log("Start"); const result = await slowPromise(); // 暂停此处,等待Promise完成 console.log("Result:", result); console.log("End"); } asyncFunc(); console.log("Main code continues..."); // 立即执行,不阻塞主线程 function slowPromise() { return new Promise(resolve => setTimeout(() => resolve("Done"), 1000)); }应用示例://定义一个返回Promise对象的异步函数 function sendAJAX(url){ return new Promise(()=>{ const x = new XMLHttpRequest(); x.open('GET', url); x.send(); x.onreadystatechange = function () { if(x.readyState === 4){ if(x.status >= 200 && x.status < 300){ resolve(x.response) }else{ reject(x.status) } } } }); } sendAJAX("1").then(value=>console.log(value)); async function send(){ try{ let result = await sendAJAX(""); console.log(result); }catch(error){ } } send(); ES11可选链操作符function readConfig(config){ const dbHost = config?.db?.host; console.log(dbHost);//10.0.0.1 } main({ db:{ host:'10.0.0.1', username:'root' }, cache:{ host:'10.0.0.1', username:'admin' } }); 动态import//方法内部 import('./').then((module)=>{ module.hello(); });globalThis全局对象,
2023年05月11日
163 阅读
0 评论
0 点赞
2023-05-07
【学习笔记】Javascript(二)
Javascript进阶1.字符串操作1.1.基本方法创建字符串// 字面量 const str1 = "Hello World"; // 构造函数 const str2 = new String("Hello World"); // 模板字符串(ES6) const name = "Alice"; const greeting = `Hello, ${name}!`; // "Hello, Alice!"基本属性与方法const str = "JavaScript"; console.log(str.length); // 字符串长度:10 console.log(str[0]); // 访问字符:'J' console.log(str.charAt(5)); // 'S'(索引从0开始) console.log(str.localeCompare("Java")); // 比较字符串:"JavaScript" > "Java" → 1查找与定位方法描述示例indexOf(searchValue)返回子字符串首次出现的索引,未找到返回 -1"apple".indexOf("p") → 1lastIndexOf(searchValue)返回子字符串最后一次出现的索引"apple".lastIndexOf("p") → 2includes(searchValue)是否包含子字符串(ES6)"apple".includes("app") → truestartsWith(searchValue)是否以指定字符串开头(ES6)"apple".startsWith("ap") → trueendsWith(searchValue)是否以指定字符串结尾(ES6)"apple".endsWith("le") → true截取与拼接方法描述示例concat(str1, str2)合并多个字符串"Hello".concat(" ", "World") → "Hello World"slice(start, end)截取子字符串(支持负数索引)"apple".slice(1, -1) → "ppl"substring(start, end)类似 slice,但负数索引会被视为 0"apple".substring(1, 3) → "pp"substr(start, length)从指定位置开始截取指定长度的字符串"apple".substr(1, 3) → "ppl"替换与分割方法描述示例replace(searchValue, newValue)替换子字符串(仅第一次匹配)"apple".replace("p", "x") → "axple"split(separator)按分隔符拆分为数组"a,b,c".split(",") → ["a", "b", "c"]trim()去除首尾空白字符" apple ".trim() → "apple"trimStart()/trimEnd()去除首/尾空白字符" apple ".trimStart() → "apple "大小写转换方法描述示例toLowerCase()转换为小写"Apple".toLowerCase() → "apple"toUpperCase()转换为大写"apple".toUpperCase() → "APPLE"1.2.高阶操作1. 字符串遍历const str = "hello"; for (let char of str) { console.log(char); // 分别输出 h e l l o }统计字符出现次数function countChars(str) { const counts = {}; for (const char of str) { counts[char] = (counts[char] || 0) + 1; } return counts; } console.log(countChars("aabbc")); // { a: 2, b: 2, c: 1 }解析 URL 参数function getUrlParams(url) { const params = {}; const parser = new URL(url); parser.searchParams.forEach((value, key) => { params[key] = value; }); return params; } const url = "https://example.com?name=Alice&age=25"; console.log(getUrlParams(url)); // { name: 'Alice', age: '25' }模板字符串(ES6)const price = 9.99; const message = `Price: $${price.toFixed(2)}`; // "Price: $9.99"1.3.正则表达式1.3.1.创建正则字面量const regex = /hello/; // 基础正则 const regexWithFlags = /\d+/ig; // 添加修饰符(i: 忽略大小写,g: 全局匹配)构造函数const regex = new RegExp("hello", "g"); // 动态创建正则修饰符修饰符作用i忽略大小写g全局匹配(找到所有匹配)m多行匹配(^和$匹配每行)s允许.匹配换行符1.3.2.常用符号元字符:元字符作用示例.匹配任意单个字符(除换行)/a.c/ 匹配 "abc", "a2c"^匹配字符串开头/^hello/ 匹配以"hello"开头$匹配字符串结尾/world$/ 匹配以"world"结尾*匹配前面字符 0 次或多次/ab*/ 匹配 "a", "ab", "abbb"+匹配前面字符 1 次或多次/ab+/ 匹配 "ab", "abbb"?匹配前面字符 0 次或 1 次/colou?r/ 匹配 "color"或"colour"{n}匹配前面字符恰好 n 次/a{3}/ 匹配 "aaa"{n,}匹配前面字符至少 n 次/a{2,}/ 匹配 "aa", "aaa"{n,m}匹配前一个字符 n 到 m 次/a{2,4}/ → 匹配 "aa"~"aaaa"()分组,捕获匹配内容/(ab)+/ → 匹配 "ab"、"abab"字符类:表达式作用\d匹配数字(0-9)\D匹配非数字\w匹配字母、数字、下划线\W匹配非字母、数字、下划线\s匹配空白字符(空格、换行等)[abc]匹配 a、b 或 c[^abc]匹配非 a、b、c 的字符1.3.3.常用方法RegExp.prototype.test()检查字符串是否匹配正则表达式。const regex = /Hello/; console.log(regex.test('Hello, World!')); // true console.log(regex.test('HELLO')); // false未匹配 大小写RegExp.prototype.exec()执行搜索匹配,返回匹配结果或 null。const regex = /Hello/; const result = regex.exec('Hello, World!'); console.log(result); // ['Hello', index: 0, input: 'Hello, World!', groups: undefined]match()在字符串中执行搜索匹配,返回匹配结果数组或 null。const str = 'Hello, World!'; console.log(str.match(/Hello/)); // ['Hello', index: 0, input: 'Hello, World!', groups: undefined]matchAll()返回所有匹配项的迭代器。const str = 'Hello, Hello!'; const regex = /Hello/g; console.log(Array.from(str.matchAll(regex))); // [['Hello', index: 0], ['Hello', index: 7]]search()返回第一个匹配项的索引。const str = 'Hello, World!'; console.log(str.search(/World/)); // 7replace()替换字符串中的匹配项。const str = 'Hello, World!'; console.log(str.replace(/World/, 'Alice')); // 'Hello, Alice!'split()将字符串拆分为数组。const str = 'Hello, World!'; console.log(str.split(/, /)); // ['Hello', 'World!']1.3.4.进阶使用捕获组与非捕获组// 捕获组(可通过 `exec` 或 `matchAll` 获取分组内容) const regex = /(a)(b)/; const str = "ab"; const result = regex.exec(str); console.log(result[1]); // "a"(第一个捕获组) console.log(result[2]); // "b"(第二个捕获组) // 非捕获组(仅分组不捕获) const regex2 = /(?:a)(b)/;前瞻(Lookahead)与后顾(Lookbehind)也叫预查断言// 正向前瞻:匹配后面跟着特定字符的内容 const str = "apple123"; console.log(str.match(/\d+(?=\.)/)); // null(无小数点) console.log("apple123.45".match(/\d+(?=\.)/)); // "123" // 反向前瞻:匹配前面是特定字符的内容 console.log("apple123".match(/(?<=apple)\d+/)); // "123" // 正向后顾:匹配前面是特定字符的内容 console.log("apple123".match(/(?<=apple)\d+/)); // "123"贪婪与非贪婪匹配const str = "<div>Hello</div><div>World</div>"; // 贪婪匹配(默认) console.log(str.match(/<div>.*<\/div>/)); // "<div>Hello</div><div>World</div>" // 非贪婪匹配(添加 ?) console.log(str.match(/<div>.*?<\/div>/g)); // ["<div>Hello</div>", "<div>World</div>"]数据提取const text = "Price: $19.99, Quantity: 5"; // 提取价格 const priceMatch = text.match(/\$\d+\.\d+/); console.log(priceMatch[0]); // "$19.99" // 提取所有数字 const numbers = text.match(/\d+/g); console.log(numbers); // ["19", "99", "5"] //提取URL const url = 'https://example.com?name=Alice&age=30'; const params = url.split('?')[1].split('&').reduce((acc, param) => { const [key, value] = param.split('='); acc[key] = value; return acc; }, {}); console.log(params); // { name: 'Alice', age: '30' }文本替换const str = "Hello WORLD"; // 替换所有大写字母为小写 const newStr = str.replace(/[A-Z]/g, (match) => match.toLowerCase()); console.log(newStr); // "hello world"1.3.5.常用正则表达式场景正则表达式说明邮箱验证^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$基础邮箱验证(允许字母、数字、点、下划线、百分号、加号、减号)。 ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$更严格验证(限制域名长度为 2~6 位)。手机号验证(中国)^1[3-9]\d{9}$验证中国大陆手机号(11 位,第二位为 3-9)。 `^(13[0-9]14[5-9]URL 验证^(https?:\/\/)?([\w.-]+)\.([a-z]{2,})(:\d+)?(\/.*)?$支持 HTTP/HTTPS 协议、域名、端口和路径。 ^https?:\/\/(www\.)?[\w-]+\.[a-z]{2,}(\/.*)?$更简洁的 URL 验证(强制包含 www.)。密码强度验证^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$必须包含大小写字母、数字、特殊字符,长度 ≥8。 ^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{8,}$简化版(至少字母和数字,长度 ≥8)。提取 HTML 标签内容<(\w+)>(.*?)<\/\1>匹配简单标签内容(如 <div>Hello</div>)。 <([a-z]+)([^<]+)*(?:>(.*)<\/\1>)?更通用的标签匹配(支持属性和嵌套内容)。提取 URL 参数[?&]([^=#]+)=([^&#]*)提取查询字符串中的键值对(如 name=Alice&age=30)。 (?<=[?&])([^=&]+)=([^&]*)使用正向后顾匹配参数键值对。隐藏手机号中间四位(\d{3})\d{4}(\d{4})将手机号中间四位替换为星号(如 138****8000)。 (\d{3})\d{3}\d{4}另一种替换方式(如 138-***-8000)。千分位分隔符(?<!\d)(?=(\d{3})+(?!\d))在数字中添加千分位逗号(如 1234567 → 1,234,567)。 /\B(?=(\d{3})+(?!\d))/g另一种实现方式(避免前导零问题)。日期格式化(YYYY-MM-DD → YYYYMMDD)(\d{4})-(\d{2})-(\d{2})移除日期中的短横线(如 2023-10-05 → 20231005)。 `\d{4}(0[1-9]1[0-2])(0[1-9]移除 HTML 标签<[^>]+>删除所有 HTML 标签(如 <p>Hello</p> → Hello)。 <\/?[a-z][\w-]*[^>]*>更全面的标签匹配(支持属性和自闭合标签)。中文标点转英文标点[,。!?;:“”‘’()《》]替换常见中文标点为英文标点(如 ,→ ,)。 [^\x00-\x7F]匹配所有非 ASCII 字符(包括中文标点)。提取所有数字\d+匹配字符串中的连续数字(如 abc123def456 → ["123", "456"])。 \b\d+\b匹配独立数字(避免匹配日期中的数字如 2023-10-05 中的 2023)。验证身份证号(18位)^\d{15}(\d{2}[0-9xX])?$验证 15 位或 18 位身份证号(最后一位可为 X/x)。 `^[1-9]\d{5}(?:1920)\d{2}(?:0[1-9]2.对象操作2.1.对象的创建2.1.1. 对象字面量(Literal)最简洁的创建方式,直接用 {} 定义键值对:const obj = { name: 'dengke', age: 18, sayHi: () => console.log('Hello!'), };2.1.2. Object 构造函数通过 new Object() 创建对象:const obj = new Object(); obj.name = '孙悟空'; // 动态添加属性2.1.3. Object.create()指定原型链创建对象:const prototypeObj = { greet: () => 'Hello' }; const obj = Object.create(prototypeObj); // obj 的原型是 prototypeObj console.log(obj.greet()); // "Hello"2.1.4. 展开运算符(Spread)浅拷贝对象或合并对象:const obj1 = { a: 1 }; const obj2 = { b: 2 }; const merged = { ...obj1, ...obj2 }; // { a:1, b:2 }2.1.5.类实例化class Person { constructor(name, age){ this.name = name; this.age = age; } } const zs = new Person("zangsan",18); // Person {name:"zangsan".age:18}2.2.对象属性操作2.2.1. 增删改查添加/修改属性:obj.newProp = 'value'; // 点语法 obj['dynamicKey'] = 100; // 方括号语法(适合动态键名)删除属性:delete obj.newProp; // 返回布尔值,成功删除返回 true访问属性:console.log(obj.name); // 点语法 console.log(obj['age']); // 方括号语法2.2.2. 特殊键名的访问数字或特殊符号键名必须用方括号语法:const obj = { '1-key': 'value', '-abc': 100 }; console.log(obj['1-key']); // "value"2.3.对象遍历2.3.1. for...in 循环遍历对象自身和继承的可枚举属性:for (const key in obj) { if (obj.hasOwnProperty(key)) { // 过滤原型链属性 console.log(key, obj[key]); } }2.3.2. Object.keys() / Object.values() / Object.entries()Object.keys():返回对象自身可枚举属性的键数组:const keys = Object.keys(obj); // ["name", "age"]Object.values():返回属性值的数组:const values = Object.values(obj); // ["dengke", 18]Object.entries():返回键值对数组:const entries = Object.entries(obj); // [["name", "dengke"], ["age", 18]]2.3.3. Reflect.ownKeys()返回对象自身所有属性(包括不可枚举和符号键):const allKeys = Reflect.ownKeys(obj); // 包含所有属性2.4.对象属性描述符2.4.1. Object.defineProperty()定义或修改单个属性的描述符:const obj = {}; Object.defineProperty(obj, 'name', { value: 'dengke', // 属性值 writable: false, // 是否可修改(默认 false) enumerable: true, // 是否可枚举(默认 false) configurable: false // 是否可删除或修改描述符(默认 false) });2.4.2. Object.defineProperties()批量定义多个属性:Object.defineProperties(obj, { age: { value: 18, writable: true }, gender: { value: 'male', configurable: false } });2.4.3. 属性类型数据属性:直接存储值(如 name)。访问器属性:通过get和set方法控制访问:Object.defineProperty(obj, 'fullName', { get() { return `${this.firstName} ${this.lastName}`; }, set(val) { [this.firstName, this.lastName] = val.split(' '); } });2.5.对象冻结与密封2.5.1. Object.freeze()冻结对象,使其不可修改、不可扩展、不可配置:const obj = { a: 1 }; Object.freeze(obj); obj.a = 2; // 无效,对象被冻结2.5.2. Object.seal()密封对象,禁止添加新属性,但允许修改现有属性值:Object.seal(obj); obj.b = 3; // 无效,无法添加新属性 obj.a = 2; // 允许修改现有属性2.5.3. Object.preventExtensions()仅阻止添加新属性,其他操作仍允许:Object.preventExtensions(obj); obj.b = 4; // 无效2.5.4. 检查对象状态Object.isExtensible(obj); // 是否可扩展 Object.getOwnPropertyDescriptor(obj, 'a'); // 获取属性描述符2.6.对象合并与扩展2.6.1. Object.assign()合并对象,浅拷贝源对象到目标对象:const target = { a: 1 }; const source = { b: 2, a: 3 }; const result = Object.assign(target, source); console.log(result); // { a:3, b:2 }(目标对象被修改)2.6.2. 展开运算符(Spread)与 Object.assign() 类似,但语法更简洁:const merged = { ...target, ...source };2.7.Reflect API(ES6+)2.7.1. 常用方法Reflect.get() / Reflect.set():Reflect.get(obj, 'name'); // 等价于 obj.name Reflect.set(obj, 'age', 20); // 等价于 obj.age = 20Reflect.deleteProperty():Reflect.deleteProperty(obj, 'name'); // 等价于 delete obj.nameReflect.construct():const Person = function(name) { this.name = name; }; const instance = Reflect.construct(Person, ['dengke']);Reflect.has():Reflect.has(obj, 'name'); // 等价于 'name' in obj2.8.对象拷贝2.8.1.浅拷贝浅拷贝是指只拷贝对象的第一层属性,而嵌套对象的引用仍然指向原始对象。如果原始对象中的嵌套对象被修改,浅拷贝后的对象也会受到影响。Object.assign()const obj1 = { a: 1, b: { c: 2 } }; const obj2 = Object.assign({}, obj1); console.log(obj2); // { a: 1, b: { c: 2 } } obj1.b.c = 3; console.log(obj2.b.c); // 3 (浅拷贝的嵌套对象引用被修改)展开运算符 (...)const obj1 = { a: 1, b: { c: 2 } }; const obj2 = { ...obj1 }; console.log(obj2); // { a: 1, b: { c: 2 } } obj1.b.c = 3; console.log(obj2.b.c); // 3 (同样受影响)2.8.2.深拷贝JSON.parse() 和 JSON.stringify()const obj1 = { a: 1, b: { c: 2 } }; const obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj2); // { a: 1, b: { c: 2 } } obj1.b.c = 3; console.log(obj2.b.c); // 2 (深拷贝后的对象不受影响)局限性:无法处理函数、undefined、Date、Map、Set 等特殊对象。无法处理循环引用(会导致栈溢出)。递归实现深拷贝function deepClone(obj, hash = new WeakMap()) { if (obj === null) return null; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (typeof obj !== 'object') return obj; if (hash.has(obj)) return hash.get(obj); const result = Array.isArray(obj) ? [] : {}; hash.set(obj, result); for (const key in obj) { if (obj.hasOwnProperty(key)) { result[key] = deepClone(obj[key], hash); } } return result; } const obj1 = { a: 1, b: { c: 2 }, d: [3, 4] }; const obj2 = deepClone(obj1); obj1.b.c = 3; console.log(obj2.b.c); // 2 (完全独立)优点:可以处理复杂对象(如 Date、Map、Set 等)。支持循环引用。使用库(如 Lodash 的 _.cloneDeep())const _ = require('lodash'); const obj1 = { a: 1, b: { c: 2 } }; const obj2 = _.cloneDeep(obj1); obj1.b.c = 3; console.log(obj2.b.c); // 2 (完全独立)2.9.常见陷阱与注意事项浅拷贝问题:Object.assign()和展开运算符仅复制对象的引用,修改嵌套对象会同步到源对象:const obj1 = { a: { b: 1 } }; const obj2 = { ...obj1 }; obj2.a.b = 2; // obj1.a.b 同样变为 2delete 操作符的限制:无法删除不可配置(configurable: false)的属性。for...in 的陷阱:会遍历原型链属性,需结合hasOwnProperty()过滤:for (const key in obj) { if (obj.hasOwnProperty(key)) { // 处理自身属性 } }Object.is() 的特殊比较:与 === 不同,Object.is(-0, 0) 返回 false,而 NaN === NaN 返回 true。3.数组操作3.1.数组的创建3.1.1. 字面量方式直接用 [] 定义数组:const arr = [1, 'apple', true];3.1.2. Array 构造函数通过 new Array() 创建:const arr1 = new Array(3); // 创建长度为3的空数组 [empty × 3] const arr2 = Array.of(1, 2, 3); // 推荐,避免构造函数的陷阱3.1.3. 填充数组const arr = Array(3).fill(0); // [0, 0, 0]3.2. 添加元素push()作用:向数组末尾添加一个或多个元素,并返回数组的新长度。示例:const arr = [1, 2, 3]; arr.push(4); // arr => [1, 2, 3, 4]unshift()作用:向数组开头添加一个或多个元素,并返回数组的新长度。示例:const arr = [1, 2, 3]; arr.unshift(0); // arr => [0, 1, 2, 3]splice()作用:向数组指定位置插入元素。语法:arr.splice(index, 0, element1, element2, ...)。示例:const arr = [1, 2, 3]; arr.splice(1, 0, 'a', 'b'); // arr => [1, 'a', 'b', 2, 3]3.3. 删除元素pop()作用:删除数组末尾的元素,并返回被删除的元素。示例:const arr = [1, 2, 3]; arr.pop(); // arr => [1, 2], 返回 3shift()作用:删除数组开头的元素,并返回被删除的元素。示例:const arr = [1, 2, 3]; arr.shift(); // arr => [2, 3], 返回 1splice()作用:删除数组指定位置的元素。语法:arr.splice(index, deleteCount)。示例:const arr = [1, 2, 3, 4, 5]; arr.splice(2, 1); // arr => [1, 2, 4, 5], 返回 [3]3.4. 查找元素indexOf()作用:查找元素在数组中的位置,返回第一个匹配的索引,未找到返回 -1。示例:const arr = [1, 2, 3, 2]; arr.indexOf(2); // 返回 1lastIndexOf()作用:从数组末尾开始查找元素,返回最后一个匹配的索引,未找到返回 -1。示例:const arr = [1, 2, 3, 2]; arr.lastIndexOf(2); // 返回 3find()作用:查找数组中第一个满足条件的元素,返回该元素。示例:const arr = [1, 2, 3, 4]; arr.find(item => item > 2); // 返回 3findIndex()作用:查找数组中第一个满足条件的元素的索引,未找到返回 -1。示例:const arr = [1, 2, 3, 4]; arr.findIndex(item => item > 2); // 返回 2findLast() / findLastIndex()作用:查找最后一个符合条件的元素:示例:const arr = [1, 2, 3, 2]; arr.findLastIndex(x => x === 2); // 3 const arr = [1, 2, 3, 2]; arr.findLast(x => x > 2); //33.5.修改元素直接赋值作用:直接修改数组中指定索引的元素。示例:const arr = [1, 2, 3]; arr[1] = 'a'; // arr => [1, 'a', 3]splice()作用:删除并插入新元素,实现替换功能。语法:arr.splice(index, deleteCount, element1, element2, ...)。示例:const arr = [1, 2, 3, 4, 5]; arr.splice(2, 1, 'a'); // arr => [1, 2, 'a', 4, 5]copyWithin作用:将数组的一部分复制到同一数组中的另一个位置,覆盖现有值。语法:array.copyWithin(target, start, end)示例:const array1 = ["a", "b", "c", "d", "e"]; console.log(array1.copyWithin(0, 3, 4)); // ["d", "b", "c", "d", "e"] console.log(array1.copyWithin(1, 3)); // ["d", "d", "e", "d", "e"]3.6. 遍历与转换map()作用:对数组中的每个元素执行函数,并返回一个新数组。示例:const arr = [1, 2, 3]; const newArr = arr.map(item => item * 2); // newArr => [2, 4, 6]filter()作用:过滤数组中的元素,返回满足条件的新数组。示例:const arr = [1, 2, 3, 4]; const newArr = arr.filter(item => item % 2 === 0); // newArr => [2, 4]forEach()作用:遍历数组中的每个元素,执行指定函数。示例:const arr = [1, 2, 3]; arr.forEach(item => console.log(item)); // 输出 1, 2, 3entries作用:返回一个包含数组中每个索引的键值对的迭代器。语法:array.entries()示例:const arr = ['a', 'b', 'c']; for (const [index, value] of arr.entries()) { console.log(index, value); } // 0 'a' // 1 'b' // 2 'c'reduce()作用:对数组中的元素进行累积操作,返回累积结果。语法:arr.reduce(callback(accumulator, currentValue), initialValue)。示例:const arr = [1, 2, 3, 4]; const sum = arr.reduce((acc, curr) => acc + curr, 0); // sum => 10flat作用:创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。语法:arr.flat([depth])示例:const arr1 = [0, 1, 2, [3, 4]]; console.log(arr1.flat()); // [0, 1, 2, 3, 4] const arr2 = [0, 1, [2, [3, [4, 5]]]]; console.log(arr2.flat(2)); // [0, 1, 2, 3, Array [4, 5]] console.log(arr2.flat(Infinity)); // [0, 1, 2, 3, 4, 5]flatMap()作用:结合了 map 和 flat 的功能,先对数组中的每个元素执行映射函数,然后将结果数组拍平一层。语法:arr.flatMap(callback(currentValue[, index[, array]])[, thisArg])示例:const nested = [[1], [2], [3]]; const flat = nested.flatMap(arr => arr); // [1,2,3] const arr = [1, 2, 3]; const result = arr.flatMap(x => [x, x * 2]); console.log(result); // [1, 2, 2, 4, 3, 6]3.6. 排序sort()作用:对数组进行排序,默认按字符串顺序排序。示例:const arr = [3, 1, 4, 2]; arr.sort(); // arr => [1, 2, 3, 4] (注意:默认按字符串排序)按数值排序:const arr = [3, 1, 4, 2]; arr.sort((a, b) => a - b); // arr => [1, 2, 3, 4]3.7. 合并与分割concat()作用:合并两个或多个数组,返回新数组。示例:const arr1 = [1, 2]; const arr2 = [3, 4]; const newArr = arr1.concat(arr2); // newArr => [1, 2, 3, 4]slice()作用:提取数组的一部分,返回新数组,不修改原数组。语法:arr.slice(start, end)(end 不包含)。示例:const arr = [1, 2, 3, 4]; const newArr = arr.slice(1, 3); // newArr => [2, 3]split()(字符串转数组)作用:将字符串分割为数组。示例:const str = 'a,b,c'; const arr = str.split(','); // arr => ['a', 'b', 'c']join()(数组转字符串)作用:将数组元素连接为字符串。示例:const arr = ['a', 'b', 'c']; const str = arr.join('-'); // str => 'a-b-c'3.8. 判断数组isArray()作用:判断一个对象是否为数组。示例:Array.isArray([1, 2, 3]); // true Array.isArray({}); // false3.9. 其他常用方法includes()作用:判断数组是否包含某个值,返回布尔值。示例:const arr = [1, 2, 3]; arr.includes(2); // truereverse()作用:反转数组。示例:const arr = [1, 2, 3]; arr.reverse(); // arr => [3, 2, 1]every()作用:判断数组中所有元素是否满足条件。示例:const arr = [2, 4, 6]; arr.every(item => item % 2 === 0); // truesome()作用:判断数组中是否存在至少一个满足条件的元素。示例:const arr = [1, 2, 3]; arr.some(item => item % 2 === 0); // true解构赋值const [first, second, ...rest] = [1,2,3,4]; console.log(first); // 1 console.log(rest); // [3,4]3.10.数组拷贝3.10.1. 浅拷贝slice()作用:创建一个新数组,包含原数组的元素(从开始索引到结束索引,但不包括结束索引)。语法:arr.slice([start], [end])示例:const arr = [1, 2, 3, 4]; const shallowCopy = arr.slice(); console.log(shallowCopy); // [1, 2, 3, 4]局限性:只拷贝第一层,嵌套对象的引用仍然共享。展开运算符 (...)作用:将数组展开为新数组。语法:const newArr = [...arr]示例:const arr = [1, 2, 3, 4]; const shallowCopy = [...arr]; console.log(shallowCopy); // [1, 2, 3, 4]局限性:同样只拷贝第一层。concat()作用:合并数组并返回新数组。语法:arr.concat()示例:const arr = [1, 2, 3, 4]; const shallowCopy = arr.concat(); console.log(shallowCopy); // [1, 2, 3, 4]局限性:只拷贝第一层。3.10.2. 深拷贝JSON.parse(JSON.stringify())作用:将数组转换为 JSON 字符串,再解析为新数组。语法:JSON.parse(JSON.stringify(arr))示例:const arr = [1, 2, { a: 3 }, [4, 5]]; const deepCopy = JSON.parse(JSON.stringify(arr)); console.log(deepCopy); // [1, 2, { a: 3 }, [4, 5]]局限性:无法处理函数、undefined、Date、Map、Set 等特殊对象。无法处理循环引用。递归实现深拷贝作用:手动递归拷贝数组及其嵌套元素。实现:function deepClone(obj, hash = new WeakMap()) { if (obj === null) return null; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (typeof obj !== 'object') return obj; if (hash.has(obj)) return hash.get(obj); const result = Array.isArray(obj) ? [] : {}; hash.set(obj, result); for (const key in obj) { if (obj.hasOwnProperty(key)) { result[key] = deepClone(obj[key], hash); } } return result; } const arr = [1, 2, { a: 3 }, [4, 5]]; const deepCopy = deepClone(arr); console.log(deepCopy); // [1, 2, { a: 3 }, [4, 5]]优点:支持复杂对象和循环引用。缺点:实现复杂,性能较差。Lodash 的 _.cloneDeep()作用:使用 Lodash 库实现深拷贝。语法:_.cloneDeep(arr)示例:const _ = require('lodash'); const arr = [1, 2, { a: 3 }, [4, 5]]; const deepCopy = _.cloneDeep(arr); console.log(deepCopy); // [1, 2, { a: 3 }, [4, 5]]优点:功能强大,支持复杂对象和循环引用,性能较好。缺点:需要引入外部库。structuredClone()作用:使用现代浏览器的 structuredClone API。语法:structuredClone(arr)示例:const arr = [1, 2, { a: 3 }, [4, 5]]; const deepCopy = structuredClone(arr); console.log(deepCopy); // [1, 2, { a: 3 }, [4, 5]]优点:简单易用,支持多种数据类型,性能较好。缺点:不支持函数、Symbol 等某些特殊类型,浏览器兼容性有限。3.11.数组去重使用 Set:const arr = [1, 2, 2, 3, 4, 4, 5]; const uniqueArr = [...new Set(arr)]; console.log(uniqueArr); // [1, 2, 3, 4, 5]使用 filter 和 indexOf:const arr = [1, 2, 2, 3, 4, 4, 5]; const uniqueArr = arr.filter((item, index, self) => self.indexOf(item) === index); console.log(uniqueArr); // [1, 2, 3, 4, 5]使用 reduce:const arr = [1, 2, 2, 3, 4, 4, 5]; const uniqueArr = arr.reduce((acc, item) => { if (!acc.includes(item)) { acc.push(item); } return acc; }, []); console.log(uniqueArr); // [1, 2, 3, 4, 5]3.12性能优化注意事项push 和 pop:O(1) 时间复杂度,效率高。unshift 和 shift:O(n) 时间复杂度,效率较低,尽量避免在大数据量时使用。splice:性能取决于删除/插入的位置和数量,尽量避免频繁使用。map 和 filter:遍历整个数组,复杂度为 O(n),适合中小规模数据。修改方法:push, pop, shift, unshift, splice, reverse, sort 会改变原数组。安全方法:map, filter, slice, concat 返回新数组。sort() 的默认行为:默认按字符串排序,需传入比较函数进行数值或自定义排序。slice() 和展开运算符 ([...arr]) 仅拷贝一层,嵌套对象仍为引用。4.高阶函数4.1.定义高阶函数指:接收函数作为参数(如 map、filter)。// 示例:回调函数 function repeat(n, callback) { for (let i = 0; i < n; i++) { callback(i); } } repeat(3, (index) => console.log(`Iteration ${index}`)); // 输出:Iteration 0, Iteration 1, Iteration 2返回函数作为结果(如柯里化、闭包)。// 示例:闭包 function createCounter() { let count = 0; return function() { return count++; }; } const counter = createCounter(); console.log(counter()); // 0 console.log(counter()); // 1或两者兼具。4.2.高阶函数用法4.2.1 柯里化将多参数函数转换为单参数函数链:function curryAdd(a) { return function(b) { return a + b; }; } const add5 = curryAdd(5); console.log(add5(3)); // 84.2.2函数组合组合多个函数为一个函数:function compose(f, g) { return function(x) { return f(g(x)); }; } const uppercase = str => str.toUpperCase(); const reverse = str => str.split('').reverse().join(''); const reverseUpper = compose(uppercase, reverse); console.log(reverseUpper("hello")); // "OLLEH"4.2.3.装饰器模式动态增强函数功能:function logger(fn) { return function(...args) { console.log("Calling function with", args); const result = fn.apply(this, args); console.log("Result:", result); return result; }; } const add = logger((a, b) => a + b); add(2, 3); // 输出日志并返回54.3.闭包闭包(Closure) 是一个函数与其词法作用域(Lexical Environment) 的组合。它允许函数访问并操作其外部函数的变量,即使外部函数已经执行完毕。4.3.1.工作原理词法作用域与执行上下文词法作用域:函数的作用域由其定义的位置决定。例如:function outer() { const outerVar = '外层变量'; function inner() { console.log(outerVar); // 引用外层变量 } return inner; } const closure = outer(); closure(); // 输出:外层变量inner 函数在定义时“记住”了 outer 的作用域,即使 outer 已执行完毕,inner 仍能访问 outerVar。词法环境(Lexical Environment)词法环境由两部分组成:环境记录(Environment Record):存储变量、函数声明等。外层环境引用(Outer Environment Reference):指向父级作用域。闭包的持久化:当外部函数执行完毕后,如果内部函数被外部引用,其词法环境不会被垃圾回收机制释放,从而形成闭包。垃圾回收机制JavaScript 的垃圾回收机制会回收不再被引用的变量和对象。闭包的内存问题:如果闭包引用了大量外部变量且长期存在,可能导致内存泄漏。4.3.2.应用场景数据封装与私有变量模拟私有变量:function Counter() { let count = 0; // 私有变量 return { increment: () => count++, get: () => count }; } const counter = Counter(); console.log(counter.get()); // 0 counter.increment(); console.log(counter.get()); // 1count 是外部函数 Counter 的局部变量,通过闭包被内部方法访问,外部无法直接修改。保存状态(持续化变量)计数器示例:function createCounter() { let count = 0; return () => count++; } const counter = createCounter(); console.log(counter()); // 0 console.log(counter()); // 1count 的值在每次调用时被保留,形成“持续状态”。回调函数与异步编程异步操作中保存上下文:function asyncTask(data) { setTimeout(() => { console.log(`处理数据: ${data}`); }, 1000); } asyncTask("Hello"); // 1秒后输出 "处理数据: Hello"setTimeout 的回调函数通过闭包访问了外部函数 asyncTask 的参数 data。5.解构操作5.1.数组结构基本用法const arr = [1, 2, 3]; const [a, b, c] = arr; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3跳过元素//你可以通过在解构模式中留空来跳过数组中的某些元素。 const arr = [1, 2, 3, 4, 5]; const [a, , b, , c] = arr; console.log(a); // 1 console.log(b); // 3 console.log(c); // 5默认值//如果数组中的某些元素不存在,你可以为变量提供默认值。 const arr = [1, 2]; const [a, b, c = 3] = arr; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3嵌套解构const arr = [1, [2, 3], 4]; const [a, [b, c], d] = arr; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 console.log(d); // 4剩余参数const [head, ...tail] = [1, 2, 3, 4]; console.log(head); // 1 console.log(tail); // [2, 3, 4]5.2.对象解构基本用法const obj = { a: 1, b: 2, c: 3 }; const { a, b, c } = obj; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3重命名变量你可以为解构的属性指定不同的变量名。const obj = { a: 1, b: 2 }; const { a: x, b: y } = obj; console.log(x); // 1 console.log(y); // 2默认值如果对象中没有某个属性,你可以为变量提供默认值。const obj = { a: 1 }; const { a, b = 2 } = obj; console.log(a); // 1 console.log(b); // 2嵌套解构对象解构支持嵌套结构。const obj = { a: 1, b: { c: 2, d: 3 }, e: 4 }; const { a, b: { c, d }, e } = obj; console.log(a); // 1 console.log(c); // 2 console.log(d); // 3 console.log(e); // 4剩余参数const { id, ...rest } = { id: 1, name: 'Bob', age: 25 }; console.log(rest); // { name: 'Bob', age: 25 }5.3.函数参数解构// 对象解构 function printUser({ name, age }) { console.log(`Name: ${name}, Age: ${age}`); } printUser({ name: 'Alice', age: 30 }); // Name: Alice, Age: 30// 数组解构function sum([a, b]) { return a + b;}console.log(sum([1, 2])); // 3 #### 5.4.复杂嵌套结构 const data = { user: {name: "Bob", address: { city: "New York" }}, hobbies: ["reading", "coding"]};// 解构嵌套对象const { user: { name, address: { city } }, hobbies: [firstHobby, ...otherHobbies]} = data;console.log(name, city); // "Bob" "New York"console.log(firstHobby, otherHobbies); // "reading" ["coding"] #### 5.5.应用场景 - **交换变量值** 通过解构实现变量交换: let x = 1, y = 2; [x, y] = [y, x]; // x=2, y=1 - **字符串解构** 将字符串视为字符数组进行解构: const [a, b, c] = 'cat'; // a='c', b='a', c='t' - **计算属性名** 用表达式作为属性名: const key = 'age'; const obj = { age: 25 }; const { [key]: userAge } = obj; // userAge=25 - **从函数返回多个值** function getCoordinates() {return { x: 10, y: 20 };} const { x, y } = getCoordinates(); console.log(x, y); // 10 20 - **忽略不需要的值** const [onlyFirst, , third] = [1, 2, 3]; console.log(onlyFirst, third); // 1 3 - **解构与模块导入** // mathUtils.js export const add = (a, b) => a + b; export const subtract = (a, b) => a - b;// main.js import { add, subtract } from "./mathUtils.js"; console.log(add(5, 3)); // 8 ### 6.JSON操作 #### 6.1.`JSON.stringify()` 将对象或数组转为字符串,支持过滤、转换和格式化。 - 语法:`JSON.stringify(value[, replacer[, space]])` - **`value`**: 要序列化的对象。 - **`replacer`**(可选):函数或数组,用于过滤或转换属性。 - **`space`**(可选):格式化缩进(数字或字符串)。 - 示例 const obj = { name: "Alice", age: 25, date: new Date() };// 基础序列化 console.log(JSON.stringify(obj)); // '{"name":"Alice","age":25,"date":"2023-10-01T00:00:00.000Z"}'// 过滤属性(仅保留 name 和 age) console.log(JSON.stringify(obj, ["name", "age"])); // '{"name":"Alice","age":25}'// 使用 replacer 函数 console.log(JSON.stringify(obj, (key, value) => {if (key === "date") return value.toISOString(); // 格式化日期 return value;}));//用于指定缩进空格数,使输出的 JSON 字符串更易读。 const obj = { name: 'Alice', age: 30 }; const jsonString = JSON.stringify(obj, null, 2); console.log(jsonString); // { // "name": "Alice", // "age": 30 // } - 循环引用问题:默认无法处理循环引用,会导致错误。可以通过 `replacer` 函数来处理。 const obj = { a: 1 }; obj.b = obj;try {JSON.stringify(obj); // 抛出错误} catch (e) {console.error('Error:', e.message);}// 使用 replacer 处理循环引用 function replacer(key, value) {if (value === obj) return '[Circular]'; return value;}console.log(JSON.stringify(obj, replacer)); // '{"a":1,"b":"[Circular]"}' - **`JSON.stringify()` 会忽略函数、undefined和 `Symbol` 键**: const obj = {name: 'Alice', greet: function() { console.log('Hello!'); }, id: Symbol('123')}; console.log(JSON.stringify(obj)); // '{"name":"Alice"}' #### 6.2.`JSON.parse()` 将 JSON 字符串转换为 JavaScript 对象或数组。 - **语法**:`JSON.parse(text[, reviver])` - **`text`**:要解析的 JSON 字符串。 - **`reviver`**(可选):函数,用于在解析后转换结果(如类型转换或数据校验)。 - 示例 const jsonString = '{"name":"Alice","age":30}'; const obj = JSON.parse(jsonString); console.log(obj); // { name: 'Alice', age: 30 }//reviver const jsonString = '{"name":"Alice","age":30,"email":"alice@example.com"}'; const obj = JSON.parse(jsonString, (key, value) => {if (key === 'email') return value.toUpperCase(); return value;}); console.log(obj); // { name: 'Alice', age: 30, email: 'ALICE@EXAMPLE.COM' }//如果 JSON 字符串格式不正确,JSON.parse() 会抛出错误。 const invalidJson = '{"name": "Alice",}'; // 额外的逗号 try {JSON.parse(invalidJson);} catch (e) {console.error('Error:', e.message); // Error: Unexpected token } at position 15} ### 7.集合 #### 7.1.Map 用于键值对存储,键可以是任何数据类型(包括对象、函数等)。保持插入顺序。 ##### 7.1.1.方法 | 方法 | 功能描述 | | -------------------- | ------------------------------------------------ | | `new Map()` | 创建一个空的 `Map`。 | | `.set(key, value)` | 设置键值对,若键存在则更新值,返回 `Map` 本身。 | | `.get(key)` | 获取指定键的值,若不存在返回 `undefined`。 | | `.has(key)` | 检查是否存在指定键。 | | `.delete(key)` | 删除指定键的键值对,返回是否删除成功。 | | `.clear()` | 清空所有键值对。 | | `.keys()` | 返回一个包含所有键的迭代器。 | | `.values()` | 返回一个包含所有值的迭代器。 | | `.entries()` | 返回一个包含 `[key, value]` 数组的迭代器。 | | `.forEach(callback)` | 遍历所有键值对,回调参数为 `(value, key, map)`。 | **示例** const map = new Map([ ["name", "Alice"], [42, "The Answer"]]);// 添加/删除元素map.set("age", 25); // Map(3) { "name" → "Alice", 42 → "The Answer", "age" → 25 }map.delete("name"); // true → 移除键 "name"// 查询与遍历map.get(42); // "The Answer"map.has("age"); // truemap.size; // 2// 遍历map.forEach((value, key) => console.log(${key}: ${value}));//使用对象const user = { id: 1 };const userMap = new Map();userMap.set(user, "Alice"); // 键是对象console.log(userMap.get(user)); // "Alice"//动态的键值对const cache = new Map();cache.set("key1", "value1");cache.set(123, { data: "test" });// 对象转 Mapconst obj = { a: 1, b: 2 };const mapFromObj = new Map(Object.entries(obj)); // Map { "a" → 1, "b" → 2 }// Map 转对象const objFromMap = Object.fromEntries(mapFromObj); // { a: 1, b: 2 }//Map转数组const arr = Array.from(map); ##### 7.1.2.和Object比较 | 特性 | `Map` | 对象(Object) | | -------- | ---------------------------- | ----------------------------------- | | 键的类型 | 任意类型(包括对象、函数等) | 仅字符串或符号(Symbol) | | 遍历顺序 | 保持插入顺序 | 无保证(ES5 及之前) | | 大小查询 | `map.size` | 需手动统计或 `Object.keys().length` | | 性能 | 更适合频繁增删改的场景 | 适合简单的键值存储 | #### 7.2.Set 集合(Set)是一种数据结构,用于存储唯一的值。集合中的元素是唯一的,这意味着集合中不能有重复的值。集合提供了多种方法来添加、删除和检查元素。 ##### 7.2.1.基本方法 | 方法 | 功能描述 | | -------------------- | ---------------------------------------------------------- | | `new Set()` | 创建一个空的 `Set`。 | | `.add(value)` | 向集合中添加一个元素,若已存在则不操作,返回 `Set` 本身。 | | `.delete(value)` | 从集合中删除指定元素,返回是否删除成功(`true`/`false`)。 | | `.has(value)` | 检查集合中是否存在指定元素,返回布尔值。 | | `.clear()` | 清空集合中的所有元素。 | | `.size` | 返回集合中元素的数量。 | | `.forEach(callback)` | 遍历集合中的每个元素,执行回调函数。也可以用`for...of` | | `.values()` | 返回一个包含集合中所有值的迭代器。 | // 通过数组初始化const fruits = new Set(["apple", "banana", "cherry"]);// 逐步添加元素const numbers = new Set();numbers.add(1).add(2).add(3); // 链式调用// 通过变量添加元素const name = "Alice";const users = new Set();users.add(name); // 添加字符串users.add({ id: 1 }); // 添加对象//遍历const mySet = new Set([1, 2, 3]);for (const value of mySet) { console.log(value);}// 1// 2// 3const mySet = new Set([1, 2, 3]);mySet.forEach(value => { console.log(value);});// 1// 2// 3//集合转数组const mySet = new Set([1, 2, 3]);const arr = Array.from(mySet); // 或 [...mySet]console.log(arr); // [1, 2, 3] - **`NaN`**:虽然 `NaN !== NaN`,但 `Set` 中只会存储一个 `NaN`。 - **`null`/`undefined`**:会被视为唯一值,但多次添加不会重复。 ##### 7.2.2.高级方法 - **`union()`**:合并两个 `Set`,去重后返回新 `Set`。 - **`intersection()`**:返回两个 `Set` 的交集。 - **`difference()`**:返回在第一个 `Set` 中但不在第二个中的元素。 - **`symmetricDifference()`**:返回两个 `Set` 的非交集元素。 const setA = new Set([1, 2, 3]);const setB = new Set([3, 4, 5]);// 合并const unionSet = setA.union(setB); // {1, 2, 3, 4, 5}// 交集const intersectionSet = setA.intersection(setB); // {3}// 差集const differenceSet = setA.difference(setB); // {1, 2}// 对称差集const symDiffSet = setA.symmetricDifference(setB); // {1, 2, 4, 5} ##### 7.2.3.应用 - **去重与统计频率** const arr = [1, 2, 2, 3, 3, 3]; const frequency = [...new Set(arr)].reduce((acc, num) => {acc[num] = arr.filter(x => x === num).length; return acc;}, {}); // {1:1, 2:2, 3:3} ### 8.日期处理 #### 8.1. 创建日期对象 - **当前日期和时间**: const now = new Date(); console.log(now); // 当前日期和时间 - **指定日期和时间**: const specificDate = new Date(2023, 9, 15, 12, 30, 0); // 年, 月(0-11), 日, 时, 分, 秒 console.log(specificDate); // 2023-10-15T12:30:00 - **日期字符串**: const dateString = new Date('2023-10-15T12:30:00'); console.log(dateString); // 2023-10-15T12:30:00 #### 8.2. 获取日期和时间 - **获取年份**: const year = new Date().getFullYear(); console.log(year); // 2023 - **获取月份**(0-11): const month = new Date().getMonth(); console.log(month); // 9 (十月) - **获取日期**: const day = new Date().getDate(); console.log(day); // 15 - **获取星期几**(0-6,0 表示星期日): const dayOfWeek = new Date().getDay(); console.log(dayOfWeek); // 0-6 - **获取小时**: const hours = new Date().getHours(); console.log(hours); // 0-23 - **获取分钟**: const minutes = new Date().getMinutes(); console.log(minutes); // 0-59 - **获取秒数**: const seconds = new Date().getSeconds(); console.log(seconds); // 0-59 - **获取毫秒**: const milliseconds = new Date().getMilliseconds(); console.log(milliseconds); // 0-999 #### 3. 设置日期和时间 - **设置年份**: const date = new Date(); date.setFullYear(2024); console.log(date.getFullYear()); // 2024 - **设置月份**: const date = new Date(); date.setMonth(11); // 11 表示 12 月 console.log(date.getMonth()); // 11 - **设置日期**: const date = new Date(); date.setDate(20); console.log(date.getDate()); // 20 - **设置小时**: const date = new Date(); date.setHours(15); console.log(date.getHours()); // 15 - **设置分钟**: const date = new Date(); date.setMinutes(30); console.log(date.getMinutes()); // 30 - **设置秒数**: const date = new Date(); date.setSeconds(45); console.log(date.getSeconds()); // 45 - **设置毫秒**: const date = new Date(); date.setMilliseconds(500); console.log(date.getMilliseconds()); // 500 #### 8.4. 日期格式化 - **使用 `toLocaleDateString()`**: const date = new Date(); console.log(date.toLocaleDateString()); // 2023/10/15 (格式取决于浏览器的本地设置) - **自定义格式化**: function formatDate(date) {const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`;}const date = new Date(); console.log(formatDate(date)); // 2023-10-15 - **使用 `Intl.DateTimeFormat`(推荐)** const date = new Date(); const options = {year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'Asia/Shanghai' // 指定时区}; console.log(new Intl.DateTimeFormat('zh-CN', options).format(date)); // "2023/12/25 10:30:00" - **使用第三方库(如 `date-fns`)** import { format } from 'date-fns'; import { zhCN } from 'date-fns/locale';console.log(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: zhCN })); // "2023-12-25 10:30:00"8.5. 日期计算计算两个日期之间的天数差:function daysBetween(date1, date2) { const diffTime = Math.abs(date2 - date1); return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } const date1 = new Date('2023-10-01'); const date2 = new Date('2023-10-15'); console.log(daysBetween(date1, date2)); // 14添加天数到日期:function addDays(date, days) { const newDate = new Date(date); newDate.setDate(newDate.getDate() + days); return newDate; } const date = new Date('2023-10-15'); const newDate = addDays(date, 5); console.log(newDate); // 2023-10-20时间戳const now = new Date(); const timestamp = now.getTime(); // 获取当前时间戳(毫秒) console.log(timestamp); // 例如:1712023102000 // 通过Date.now()快速获取时间戳 console.log(Date.now()); // 等同于new Date().getTime()8.6. 时区处理获取 UTC 时间:const date = new Date(); console.log(date.toUTCString()); // Wed, 18 Oct 2023 04:30:00 GMT获取本地时间:const date = new Date(); console.log(date.toLocaleString()); // 2023/10/15 下午12:30:00 (格式取决于浏览器的本地设置)10.事件10.1.Event对象用户或浏览器触发的交互行为(如点击、键盘输入、页面加载等)。事件处理函数的参数 event 包含交互的详细信息:常用属性属性描述event.type事件类型(如 "click")。event.target触发事件的实际元素(原始目标)。event.currentTarget当前处理事件的元素(绑定监听器的元素)。event.preventDefault()阻止默认行为(如表单提交、链接跳转)。event.stopPropagation()阻止事件冒泡/捕获。event.clientX/Y鼠标事件的坐标(相对于视口)。示例代码:button.addEventListener("click", (event) => { console.log("目标元素:", event.target); // 实际点击的元素 console.log("当前元素:", event.currentTarget); // 绑定事件的元素 event.preventDefault(); // 阻止默认行为(如表单提交) event.stopPropagation(); // 阻止事件冒泡 });10.2.事件传播机制事件传播分为三个阶段:捕获阶段(Capture Phase):事件从 window 向下传递到目标元素。目标阶段(Target Phase):事件到达目标元素。冒泡阶段(Bubble Phase):事件从目标元素向上传递回 window。示例:点击嵌套元素 div > p 时的事件流顺序:window → document → html → body → div → p(捕获阶段) p → div → body → html → document → window(冒泡阶段)事件委托:利用事件冒泡机制,将事件监听器绑定到父元素,处理子元素的事件:<ul id="parent"> <li>Item 1</li> <li>Item 2</li> </ul>const parent = document.getElementById("parent"); parent.addEventListener("click", (event) => { if (event.target.tagName === "LI") { console.log("列表项被点击:", event.target.textContent); } });10.3.自定义事件// 创建自定义事件 const customEvent = new CustomEvent("myEvent", { detail: { message: "Hello!" }, }); // 触发事件 document.dispatchEvent(customEvent); // 监听自定义事件 document.addEventListener("myEvent", (event) => { console.log("自定义事件触发:", event.detail.message); });10.4.常用事件事件类别事件名称触发时机备注用户界面事件click鼠标点击元素时触发最常用事件之一 dblclick双击元素时触发注意防止双击选中文本 contextmenu右键点击元素时触发(默认弹出右键菜单)可用 event.preventDefault() 阻止默认行为 mousedown鼠标按下时触发常用于拖拽功能 mouseup鼠标释放时触发与 mousedown 配合使用焦点事件focus元素获得焦点时触发(如输入框点击、激活)不冒泡 blur元素失去焦点时触发不冒泡 focusin元素获得焦点时触发(支持冒泡)可用于事件委托 focusout元素失去焦点时触发(支持冒泡)同上鼠标事件mouseover鼠标进入元素或其子元素时触发会冒泡 mouseout鼠标离开元素或其子元素时触发会冒泡 mouseenter鼠标进入元素时触发(不检测子元素)不冒泡 mouseleave鼠标离开元素时触发(不检测子元素)不冒泡 mousemove鼠标在元素上移动时触发高频触发,需注意性能键盘事件keydown按下键盘按键时触发(字符键和功能键)可获取 event.key 或 event.keyCode keypress按下字符键时触发(已废弃,推荐用 keydown) keyup松开键盘按键时触发 表单事件submit表单提交时触发(如点击提交按钮)可阻止默认提交行为 change表单元素值改变且失去焦点时触发(如输入框、下拉框)常用于实时验证 input表单元素值改变时立即触发(无需失去焦点)实时性更强 reset表单重置时触发 窗口事件resize浏览器窗口大小改变时触发需节流处理高频触发 scroll页面或元素滚动时触发需优化性能 load页面或图片等资源完全加载完成时触发常用于 window 或 <img> unload页面卸载时触发(如关闭标签页)不推荐使用,存在兼容性问题拖放事件dragstart拖拽操作开始时触发需设置元素为 draggable="true" dragover拖拽元素在目标元素上方移动时触发需调用 event.preventDefault() drop拖拽元素在目标元素上释放时触发 剪贴板事件copy复制操作时触发可修改剪贴板内容 cut剪切操作时触发 paste粘贴操作时触发 资源加载事件error资源加载失败时触发(如图片、脚本) 触摸事件touchstart手指触摸屏幕时触发移动端常用 touchmove手指在屏幕上滑动时触发需调用 event.preventDefault() 防止页面滚动 touchend手指离开屏幕时触发 11.BOM11.1.Window对象代表浏览器窗口(全局对象)常用属性location:访问当前窗口的 Location 对象。history:访问当前窗口的 History 对象。navigator:访问当前浏览器的 Navigator 对象。screen:访问当前屏幕的 Screen 对象。document:访问当前窗口的文档对象(DOM 根对象)。innerWidth/innerHeight:获取窗口内部宽度/高度(包含滚动条)。outerWidth/outerHeight:获取窗口外部宽度/高度(包含浏览器边框和工具栏)。常用方法方法名说明示例alert()显示警告对话框window.alert("提示信息");confirm()显示确认对话框(返回布尔值)if (window.confirm("确定提交?")) { ... }prompt()显示输入对话框(返回输入值或 null)let input = window.prompt("请输入");setTimeout()设置定时器,延迟执行代码setTimeout(() => { console.log("执行"); }, 1000);setInterval()设置周期性定时器setInterval(() => { console.log("每秒执行"); }, 1000);clearTimeout()/clearInterval()清除定时器clearTimeout(timerId);open()打开新窗口或标签页window.open("https://example.com");close()关闭当前或指定窗口window.close();resizeTo()调整窗口大小window.resizeTo(800, 600);moveTo()移动窗口到指定坐标window.moveTo(100, 100);scrollTo()/scrollBy()滚动页面到指定位置window.scrollTo(0, document.body.scrollHeight);11.2.Navigator对象浏览器对象属性描述navigator.appName获取浏览器的名称navigator.appVersion获取浏览器的平台和版本信息navigator.language获取当前浏览器的语言navigator.userAgent获取用户代理字符串navigator.cookieEnabled获取浏览器是否启用cookienavigator.onLine获取浏览器是否处于在线模式navigator.platform获取运行浏览器的操作系统平台方法名说明geolocation.getCurrentPosition()获取用户地理位置vibrate()触发设备振动(需权限)sendBeacon()异步发送小数据到服务器11.3.Location浏览器的地址栏信息属性名说明href完整 URL(如 https://example.com/path?query#hash)protocolURL 的协议(如 https:)host主机名 + 端口号(如 example.com:8080)hostname主机名(如 example.com)port端口号(如 8080)pathname路径部分(如 /path)search查询字符串(如 ?key=value)hash锚点部分(如 #section)origin协议、主机和端口的组合(如 https://example.com)searchParamsURL 查询参数的 URLSearchParams 对象(ES6+)常用方法方法名说明assign(url)加载新 URL(替换当前历史记录)replace(url)替换当前页面且不生成历史记录reload()重新加载当前页面11.4.History属性描述history.length返回历史浏览记录的当前长度方法说明back()返回上一页(等同于浏览器的后退按钮)forward()前进到下一页go(n)跳转到指定历史记录索引(0 为当前页,-1 为上一页)pushState(state, title, url)无刷新添加历史记录条目(SPA 常用)replaceState(state, title, url)替换当前历史记录条目(无刷新)11.5.screen属性名说明availWidth屏幕可用宽度(排除任务栏)availHeight屏幕可用高度(排除任务栏)width屏幕总宽度(含任务栏)height屏幕总高度(含任务栏)colorDepth屏幕颜色深度(位数)11.6.Document方法描述示例代码getElementById(id)根据元素的 ID 获取元素。const element = document.getElementById('myId');querySelector(selector)使用 CSS 选择器获取第一个匹配的元素。const element = document.querySelector('#myId');querySelectorAll(selector)使用 CSS 选择器获取所有匹配的元素,返回一个静态节点列表。const elements = document.querySelectorAll('.myClass');getElementsByClassName(name)根据类名获取元素,返回一个 HTML 集合。const elements = document.getElementsByClassName('myClass');getElementsByTagName(name)根据标签名获取元素,返回一个 HTML 集合。const elements = document.getElementsByTagName('div');createElement(tagName)创建一个新的 HTML 元素。const newElement = document.createElement('div');createTextNode(text)创建一个新的文本节点。const textNode = document.createTextNode('Hello, World!');appendChild(node)将一个节点添加到父节点的子节点列表的末尾。parentElement.appendChild(newElement);insertBefore(node, referenceNode)在参考节点之前插入一个节点。parentElement.insertBefore(newElement, referenceNode);removeChild(node)从父节点中移除一个子节点。parentElement.removeChild(childElement);replaceChild(newNode, oldNode)用新节点替换旧节点。parentElement.replaceChild(newNode, oldNode);addEventListener(type, listener)为元素添加事件监听器。element.addEventListener('click', function() { console.log('Clicked!'); });removeEventListener(type, listener)移除元素的事件监听器。element.removeEventListener('click', myFunction);getAttribute(name)获取元素的指定属性值。const value = element.getAttribute('href');setAttribute(name, value)设置元素的指定属性值。element.setAttribute('href', 'https://example.com');removeAttribute(name)移除元素的指定属性。element.removeAttribute('href');innerHTML获取或设置元素的 HTML 内容。element.innerHTML = '<p>New content</p>';textContent获取或设置元素的文本内容(不解析 HTML)。element.textContent = 'New text';style获取或设置元素的样式。element.style.color = 'red';classList操作元素的类列表。element.classList.add('myClass');getComputedStyle(element)获取元素的计算样式。const style = window.getComputedStyle(element);输出流方法描述示例代码document.write()向文档写入 HTML 内容(慎用,仅在页面加载前使用)。document.write("<h1>Hello</h1>");document.writeln()写入 HTML 内容并换行。document.writeln("<div>Line 1 Line 2</div>");示例代码// 通过 ID 获取元素 const elementById = document.getElementById('myId'); // 通过类名获取元素 const elementsByClass = document.getElementsByClassName('myClass'); // 通过标签名获取元素 const elementsByTag = document.getElementsByTagName('div'); // 使用 CSS 选择器获取元素 const elementBySelector = document.querySelector('#myId'); const elementsBySelector = document.querySelectorAll('.myClass'); const href = element.getAttribute("href"); // 获取属性值 // 创建新元素 const newDiv = document.createElement('div'); newDiv.textContent = 'New content'; // 添加到文档 document.body.appendChild(newDiv); // 在指定位置插入元素 const referenceElement = document.getElementById('reference'); referenceElement.parentNode.insertBefore(newDiv, referenceElement); element.setAttribute("title", "点击查看详情"); // 设置属性 element.removeAttribute("data-old"); // 删除属性 element.checked = true; // 复选框选中状态 element.disabled = false; // 启用按钮 // 添加点击事件监听器 const button = document.getElementById('myButton'); button.addEventListener('click', function() { console.log('Button clicked!'); }); // 移除事件监听器 function handleClick() { console.log('Button clicked!'); } button.removeEventListener('click', handleClick);
2023年05月07日
132 阅读
0 评论
0 点赞
2023-05-03
【学习笔记】Javascript(一)
Javascript基础1.HelloWorld1.1<script>标签<!DOCTYPE html> <html> <body> <script type="script"> document.body.innerHTML = "<h1>Hello World</h1>"; console.log("Hello World"); </script> </body> </html>1.2.独立js文件document.body.innerHTML = "<h1>Hello World</h1>"; console.log("Hello World");<!DOCTYPE html> <html> <body> <!-- script引入外部后,内部无法再写代码,不会被浏览器解析--> <script type="text/javascript" src="./hello.js"></script> </body> </html>1.3.html标签<!DOCTYPE html> <html> <body> <button onclick="alert("Hello World")"></button> <a href="javascript:alert("Hello World")"></a> </body> </html>2.基础语法2.1语法格式严格区分大小写。语句以;·分号结尾,如果不写,浏览器会自动添加。但可能造成错误。JS会忽略多个空格和换行。引号、括号闭合。代码注释:/* 多行注释 多行注释 */ //单行注释2.2.变量声明2.2.1.示例// 使用 let 声明变量(块级作用域) let age = 25; // 使用 const 声明常量(不可重新赋值) const PI = 3.14159; // 不推荐使用 var(函数级作用域,易引发问题) var name = "Alice";2.2.2.标识符使用规则:必须以 字母(A-Z/a-z)、下划线(_) 或 美元符号($) 开头。后续字符可以是 字母、数字(0-9)、下划线或美元符号。不区分大小写(但推荐统一风格,如驼峰命名法 myVariable 或下划线分隔 my_variable)。允许使用 Unicode 转义序列(如 \uXXXX),但需谨慎使用。命名规范:语义清晰:用名称表达用途(如 userAge 而非 a)。驼峰命名法:多单词变量/函数使用小驼峰(getUserName)。常量全大写:常量名用全大写加下划线(MAX_LENGTH)。避免缩写:除非是广泛认可的缩写(如 id、URL)。禁止使用保留字(见下文)。// 合法标识符 let userName = "Alice"; const _privateVar = 10; function calculateTotal() {} // 非法标识符(会报错) let 2ndPlace = "Silver"; // 以数字开头 const class = "Math"; // 使用保留字严格保留字(Strict Mode Reserved Words)在代码中添加 'use strict'; 后,以下关键字禁止使用:关键字用途implements接口实现(ECMAScript 6+)interface接口定义let块级作用域变量声明private类私有字段(实验性)public类公共字段(实验性)static类静态方法/属性yield生成器函数关键字await异步函数关键字2. 未来保留字(Future Reserved Words)目前可用,但未来可能成为保留字,建议避免使用:关键字用途enum枚举类型(ECMAScript 6+ 提案)await异步函数关键字(已部分实现)implements接口实现(ECMAScript 6+)3. 其他保留字以下关键字已在标准中明确定义,不可用作标识符:关键字用途break循环/switch 跳出caseswitch 分支catch异常捕获class类定义const常量声明continue循环继续debugger调试断点defaultswitch 默认分支、模块默认导出delete删除对象属性dodo...while 循环elseif 条件分支export模块导出extends类继承finallytry...catch 最终执行块forfor 循环function函数声明if条件判断import模块导入in成员检测、循环遍历instanceof类型检测let块级变量声明new对象实例化return函数返回值super类父级引用switch多分支选择this当前上下文对象throw抛出异常try异常处理typeof类型检测var函数级变量声明void返回无值whilewhile 循环with对象作用域扩展(不推荐使用2.2.3.var、let和const区别ES5(2009年):引入 var 作为唯一的变量声明关键字。ES6(2015年):新增 let 和 const,引入块级作用域,逐步取代 var。特性varletconst作用域函数作用域(Function Scope)块级作用域(Block Scope)块级作用域(Block Scope)变量提升提升到函数或全局顶部不提升(存在暂时性死区)不提升(存在暂时性死区)重复声明允许禁止禁止重新赋值允许允许禁止(声明时必须初始化)内存分配动态分配(可修改)动态分配(可修改)静态分配(不可修改)作用域var:作用域为最近的函数或全局范围。function test() { if (true) { var x = 10; } console.log(x); // 输出 10(x 在函数内有效) }let/const:作用域为最近的代码块({})。function test() { if (true) { let y = 20; const z = 30; } console.log(y); // 报错(y 未定义) console.log(z); // 报错(z 未定义) }变量提升(Hoisting)var:变量声明会被提升到顶部,但赋值不会。console.log(a); // undefined(声明被提升) var a = 5;let/const:存在“暂时性死区”(Temporal Dead Zone, TDZ),声明前无法访问。console.log(b); // ReferenceError(TDZ) let b = 10;重复声明var:允许重复声明同一个变量。var c = 1; var c = 2; // 合法但危险 let/const:禁止重复声明。let d = 1; let d = 2; // SyntaxError重新赋值var/let:允许重新赋值。let e = 5; e = 10; // 合法const:声明时必须初始化,且不可重新赋值。const f = 10; f = 20; // TypeError3.数据类型类型示例说明数字let num = 10;整数、浮点数均可,不区分类型。字符串let str = "Hello";单引号、双引号或反引号包裹。布尔值let isTrue = true;true 或 false。数组let arr = [1, "a", true];用 [] 定义,元素类型可不同。对象let obj = { name: "Bob" };键值对集合,用 {} 定义。null/undefinedlet n = null; let u;null 表示空值,undefined 表示未定义。3.1.数值let num = 103.1.1.Number所有数字(包括整数和浮点数)均以 64 位双精度浮点数 存储。格式:符号位(1位) + 指数(11位) + 尾数(52位)。范围:安全整数范围:-(2^53 - 1) 到 2^53 - 1(即 -9007199254740991 到 9007199254740991)。最大/最小值:Number.MAX_VALUE = 1.7976931348623157e+308; // 最大正数 Number.MIN_VALUE = 5e-324; // 最小正数 const a = 9007199254740991; // 安全最大值 const b = a + 1; // 可能不精确 console.log(b === a + 1); // false(超出安全范围)精度问题超过安全整数范围时,连续整数可能无法精确表示。console.log(0.1 + 0.2); // 输出 0.30000000000000004 // 解决方案: console.log((0.1 + 0.2).toFixed(2)); // "0.30"(保留两位小数)浮点数运算可能产生舍入误差(如 0.1 + 0.2 !== 0.3)。3.1.2.特殊值值描述检测方法NaN非数字(Not-a-Number),如 0/0 或 parseInt("abc")。isNaN(value)(全局)或 Number.isNaN(value)(ES6 更精确)Infinity正无穷大(如 1 / 0)。value === Infinity-Infinity负无穷大(如 -1 / 0)。value === -Infinity-0负零(与 0 在大多数运算中等价,但可通过 Object.is() 区分)。Object.is(-0, 0) → falseMath 对象常用方法:Math.random(); // 生成 [0, 1) 的随机数 Math.floor(3.7); // 向下取整 → 3 Math.ceil(3.2); // 向上取整 → 4 Math.round(3.5); // 四舍五入 → 4 Math.max(1, 5, 3); // 最大值 → 5 Math.min(-1, -5, 0); // 最小值 → -5// 数学运算 console.log(Math.sqrt(16)); // 4 console.log(Math.pow(2, 3)); // 8 // 特殊值处理 console.log(isNaN(NaN)); // true(全局 isNaN) console.log(Number.isNaN(NaN)); // true(ES6 更精确) // 精度问题 console.log(0.1 + 0.2); // 0.30000000000000004 console.log((0.1 + 0.2).toFixed(2)); // "0.30"3.1.5.Bigint值:用 n 后缀表示的大整数(如 100n)。特点:解决 Number 的精度问题(如超过 2^53 的整数)。与 Number 类型不兼容,需显式转换。示例:const big = 9007199254740991n + 1n; // 9007199254740992n console.log(big + 1); // 报错,需用 big + 1n3.2.字符串3.2.1.定义字符串一旦创建,内容不可修改。所有操作会生成新字符串:const str1 = "Hello World"; // 双引号 const str2 = 'Hello World'; // 单引号 const str3 = `Hello World`; // 反引号(模板字符串)可以跨行 const str4 = `Hello World ${str3}`;//嵌入变量 let s = "abc"; s[0] = "x"; // 无效,严格模式下会报错 console.log(s); // 输出 "abc" s = "def"; //生成新字符串3.2.2.常用操作拼接与重复const name = "Alice"; const greeting = "Hello, " + name + "!"; // 模板字符串更推荐 console.log(greeting); // 输出 "Hello, Alice!" // 重复字符串 console.log("abc".repeat(3)); // "abcabcabc"截取与拼接const text = "JavaScript is awesome!"; console.log(text.slice(0, 10)); // "JavaScript"(左闭右开) console.log(text.substring(0, 10)); // 同 slice console.log(text.split(" ")); // ["JavaScript", "is", "awesome!"]查找与替换const str = "Hello, World!"; console.log(str.indexOf("World")); // 7(返回起始索引) console.log(str.includes("World")); // true(ES6+) // 替换 console.log(str.replace("World", "JS")); // "Hello, JS!" console.log(str.replaceAll("l", "L")); // "HeLLo, WorLD!"(ES2021+)大小写转换const str = "Hello World!"; console.log(str.toUpperCase()); // "HELLO WORLD!" console.log(str.toLowerCase()); // "hello world!"字符串遍历const str = "abc"; for (let char of str) { // 使用 for...of 遍历字符 console.log(char); }字符串填充console.log("5".padStart(3, "0")); // "005"(左侧填充) console.log("5".padEnd(3, "0")); // "500"(右侧填充)字符串截取进阶const text = "Hello World!"; console.log(text.startsWith("Hello")); // true console.log(text.endsWith("!")); // true console.log(text.includes("World")); // true3.2.3字符串转义转义序列作用示例\"双引号(")"他说:\"你好!\""\'单引号(')'他说:\'你好!\''\\反斜杠(\)"路径:C:\\Users\\"`换行符`"第一行第二行"` \t制表符(Tab)"列1\t列2"\r回车符(Carriage Return)`"Windows 换行:\r"` \b退格符"删除前一个字符\b"\f换页符"分页符\f"3.3.布尔值以下值在逻辑判断中会被视为 false:值类型说明false布尔值逻辑假0数字零""字符串空字符串null对象主动置空undefinedundefined未定义NaN数字非数字//`0`、`-0`、`NaN`、`""`(空字符串)、`null`、`undefined` 转换为 `false` console.log(Boolean(0)); // false(Falsy 值) console.log(Boolean("")); // false console.log(Boolean([])); // true(对象始终为 Truthy) console.log(Boolean({})); // true if (0) { /* 不会执行 */ } if ("") { /* 不会执行 */ }3.4.空值3.4.1.undefined含义:表示变量已声明但未赋值,或函数未返回值。常见场景:let name; // 声明未赋值 → name 的值为 undefined function foo() {} // 无返回值 → foo() 返回 undefined检测方法:console.log(typeof name); // "undefined" console.log(name === undefined); // true3.4.2.null含义:表示主动赋值的“空”或“无”,通常用于明确清空值。常见场景:let user = { name: "Alice" }; user = null; // 主动置空检测方法:console.log(user === null); // true console.log(typeof null); // "object"(历史遗留问题,需注意)3.4.3.对比型检测方法典型场景undefinedvalue === undefined变量未声明或函数无返回值nullvalue === null主动置空或明确表示“无”空字符串str.length === 0输入框未填写空数组arr.length === 0数据列表初始状态空对象Object.keys(obj).length === 0配置对象未初始化3.4.4.空值处理检查 null 或 undefinedconst value = null; if (value == null) { // 等价于 (value === null || value === undefined) console.log("值为 null 或 undefined"); }默认值设置const name = inputName || "匿名"; // 如果 inputName 是假值,则用 "匿名" const age = parseInt(inputAge) ?? 0; // 空值或 NaN 时用 0(ES2020+)安全访问深层对象const user = { profile: { address: null } }; // 避免报错:user.profile.address.city → 可能抛出错误 const city = user?.profile?.address?.city ?? "未知"; // 使用可选链和空值合并3.5.Symbol3.5.1.定义用于创建 唯一且不可变的标识符。基础创建const sym1 = Symbol(); // 无描述的 Symbol const sym2 = Symbol("description"); // 带描述的 Symbol全局 Symbol 注册表通过 Symbol.for() 创建全局共享的 Symbol,可通过 Symbol.keyFor() 查找:const globalSym = Symbol.for("sharedKey"); console.log(Symbol.keyFor(globalSym)); // "sharedKey"特性:唯一性每个 Symbol 值都是全局唯一的,即使描述相同,两个 Symbol 也不相等:const sym1 = Symbol("key"); const sym2 = Symbol("key"); console.log(sym1 === sym2); // false不可变性Symbol 的描述(description)不可修改,且一旦创建无法销毁。原始类型Symbol 是原始类型,不能通过 new 关键字实例化:const sym = Symbol(); // 正确 // const sym = new Symbol(); // 报错3.5.2.内置常量JavaScript 提供了一些内置的 Symbol 常量,用于定义语言行为:Symbol 常量用途Symbol.iterator定义对象的默认迭代器Symbol.toStringTag自定义对象的 toString() 结果(如 "[object MyType]")Symbol.hasInstance定义 instanceof 操作符的行为Symbol.unscopables控制 with 语句中哪些属性不可访问Symbol.species指定构造函数创建实例时的替代构造函数3.5.3.应用场景对象属性的唯一键避免对象属性名冲突(尤其适用于第三方库或多人协作项目):const user = { [Symbol("id")]: 123, name: "Alice" }; console.log(user[Symbol("id")]); // undefined(不同 Symbol 实例) const PLUGIN_SYM = Symbol("plugin");定义私有属性结合闭包或 WeakMap 实现私有字段:const privateData = new WeakMap(); class User { constructor(name) { privateData.set(this, { name }); } getName() { return privateData.get(this).name; } }迭代器协议通过 Symbol.iterator 定义对象的默认迭代行为:const iterable = { [Symbol.iterator]() { let step = 0; return { next: () => ({ value: step++, done: step > 2 }) }; } }; for (const value of iterable) { console.log(value); // 0, 1, 2 }自定义 toStringTag通过 Symbol.toStringTag 定制对象的 toString() 结果:class MyClass { get [Symbol.toStringTag]() { return "MyClass"; } } console.log(new MyClass().toString()); // "[object MyClass]"3.5.4.常见问题Symbol 与字符串的混淆const sym = Symbol("key"); console.log(typeof sym); // "symbol"(不是字符串) console.log(sym.toString()); // "Symbol(key)"无法直接序列化Symbol 无法被 JSON 序列化:const obj = { [Symbol("id")]: 123 }; console.log(JSON.stringify(obj)); // "{}"作为对象键的限制Symbol 作为对象键时,无法通过常规方式枚举:const obj = { [Symbol("id")]: 123, name: "Alice" }; console.log(Object.keys(obj)); // ["name"](Symbol 键被忽略) console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)]3.6.类型转换3.6.1转换为布尔值隐式转换:Falsy 值(自动转为 false):false、0、""(空字符串)、null、undefined、NaN。Truthy 值(自动转为 true):非空字符串(如 "0"、" ")非零数字(如 1、-1)数组(如 [])对象(如 {})函数(如 function() {})if ("0") { console.log("Truthy"); } // 输出(字符串非空) if (0) { console.log("Falsy"); } // 不输出(0 是 falsy) if ([]) { console.log("Truthy"); } // 输出(数组是对象)主动调用:方法说明示例Boolean(value)强制转换为布尔值Boolean(0) → false!!value双重否定转换!!0 → falseconsole.log(Boolean(0)); // false console.log(Boolean("")); // false console.log(Boolean(null)); // false console.log(Boolean(undefined)); // false console.log(Boolean(NaN)); // false console.log(Boolean([])); // true(空数组是对象,对象为 Truthy) console.log(Boolean({})); // true(空对象也是 Truthy) console.log(!!0); // false console.log(!!"hello"); // true console.log(!![]); // true console.log(!!{}); // true3.6.2.转换为字符串方法说明示例String(value)强制转换为字符串String(123) → "123"value.toString()对象调用 toString() 方法(123).toString() → "123"${value}模板字符串转换`${123} → "123"`//String() console.log(String(123)); // "123" console.log(String(true)); // "true" console.log(String(null)); // "null" console.log(String(undefined)); // "undefined" console.log(String(Symbol("id"))); // "Symbol(id)" //toString console.log((123).toString()); // "123" console.log(true.toString()); // "true" console.log((NaN).toString()); // "NaN" console.log({}.toString()); // "[object Object]" //${} const num = 456; console.log(`Value: ${num}`); // "Value: 456" console.log(String(NaN)); // "NaN" console.log(String(Infinity)); // "Infinity" console.log(String(undefined)); // "undefined" console.log(String(null)); // "null"隐式转换:字符串拼接console.log("Number: " + 123); // "Number: 123" console.log("Boolean: " + true); // "Boolean: true"模板字符串插值:const obj = { name: "Alice" }; console.log(`User: ${obj}`); // "User: [object Object]"alert() 或 prompt():alert(123); // 显示 "123"3.6.3.转换为数值转换为 Number方法描述示例Number(value)强制转换(严格转换,非数字返回 NaN)。Number("123") → 123parseInt(string)解析字符串为整数(可指定进制)。parseInt("10.5") → 10parseFloat(string)解析字符串为浮点数。parseFloat("3.14") → 3.14+value一元加号转换(隐式转换)。+"123" → 123console.log(Number("")); // 0 console.log(Number(" ")); // 0 console.log(Number(true)); // 1 console.log(Number(false)); // 0 console.log(Number(null)); // 0 console.log(Number(undefined)); // NaN console.log(Number("123")); // 123 // parseInt console.log(parseInt("123.45")); // 123(截断小数) console.log(parseInt("12a3", 10)); // 12(遇到非数字字符停止解析) //parseFloat console.log(parseFloat("12.34")); // 12.34 console.log(parseFloat("12.3.4")); // 12.3(截断到第二个小数点) //一元加号转换(隐式转换)。 console.log(+"123"); // 123 console.log(+""); // 0 console.log(+"abc"); // NaN console.log(+null); // 0 console.log(+undefined); // NaN4.运算符4.1.算术运算符运算符说明示例结果+加法5 + 38-减法10 - 46*乘法2 * 3.57/触发10 / 33.333...(浮点数)%取模10 % 31**幂运算2 ** 38(指数运算)console.log(5 + 3); // 8(数字相加) console.log("5" + 3); // "53"(字符串拼接) console.log([1, 2] + { a: 3 }); // "1,2[object Object]"(类型转换后拼接) console.log("5" + 3 + 2); // "532"(字符串拼接优先级高于加法) console.log(5 + 3 + "2"); // "82"(先数字相加,再拼接) console.log(9007199254740991 + 1); // 9007199254740992(超过安全整数范围) console.log(10 - 4); // 6 console.log("10" - 4); // 6(字符串转数字) console.log("10a" - 4); // NaN(无法转换) console.log(10 / 0); // Infinity console.log(-10 / 0); // -Infinity console.log(3 * 4); // 12 console.log("3" * "4"); // 12 console.log("3" * true); // 3(`true` 转为 1) console.log(10 / 2); // 5 console.log(10 / 3); // 3.333... console.log("10" / 3); // 3.333... console.log("10" / "2"); // 5 console.log(10 % 3); // 1(10 ÷ 3 = 3 余 1) console.log(-10 % 3); // -1(-10 ÷ 3 = -4 余 -1) console.log(10 % -3); // 1(余数符号与被除数一致) console.log(7 % 3); // 1 console.log(-7 % 3); // -1(余数符号与被除数一致) console.log(7 % -3); // 1(余数符号与被除数一致) console.log(2 ** 3); // 8(2 的 3 次方) console.log(10 ** -1); // 0.1(10 的 -1 次方) console.log(2 ** 0.5); // 1.414...(平方根)4.2.赋值运算符运算符示例等价表达式说明+=x += 5x = x + 5加法后赋值-=x -= 3x = x - 3减法后赋值*=x *= 2x = x * 2乘法后赋值/=x /= 4x = x / 4除法后赋值%=x %= 7x = x % 7取余后赋值**=x **= 3x = x ** 3指数运算后赋值(ES7+)<<=x <<= 2x = x << 2左移后赋值>>=x >>= 1x = x >> 1算术右移后赋值>>>=x >>>= 3x = x >>> 3逻辑右移后赋值(无符号)&=x &= 0b1100x = x & 0b1100按位与后赋值^=x ^= 0b1010x = x ^ 0b1010按位异或后赋值`= ``x= 0b0011`??=x ??= 10x = x ?? 10若 x 为 null/undefined,则赋值右侧值&&=x &&= yx = x && (x = y)若 x 为真值,才赋值右侧值(需注意语法)let num = 10; num += 5; // 等价于 num = num + 5 → 15 let num = 10; num -= 3; // 等价于 num = num - 3 → 7 let num = 5; // 二进制 101 num <<= 1; // 左移一位 → 1010 (10)4.3.逻辑运算符运算符作用返回值类型短路行为&&两侧都为真时返回右侧,否则左侧原始操作数是` `两侧任意一侧为真,返回真`左侧为真时返回左侧,否则右侧!取反布尔值布尔值 (true/false)否??左侧为 null/undefined 返回右侧原始操作数否!!将任意值强制转换为布尔值:const value = "hello"; console.log(!!value); // true(非空字符串是 truthy)逻辑运算符的返回值&& 返回第一个 falsy 值或最后一个值:console.log(5 && 3); // 3(均为 truthy) console.log(0 && 3); // 0(0 是 falsy) console.log("" && null);// ""(空字符串是 falsy)|| 返回第一个 truthy 值或最后一个值:console.log(5 || 3); // 5(均为 truthy) console.log(0 || 3); // 3(0 是 falsy) console.log("" || null);// null(均为 falsy)常见用法://条件判断 const age = 20; if (age >= 18 && age < 60) { console.log("成年且未退休"); } //默认值设置 const username = inputUsername || "匿名用户"; //安全访问深层对象属性 const city = user?.profile?.address?.city ?? "未知"; //优先级 console.log(true || false && false); // true(`&&` 优先级高于 `||`) // 等价于 true || (false && false)4.4.关系运算符运算符描述示例结果>大于5 > 3true<小于3 < 5true>=大于等于5 >= 5true<=小于等于3 <= 5true==等于(弱相等,隐式转换)5 == "5"true!=不等于(弱不等)5 != "5"false===全等(严格相等)5 === "5"false!==不全等(严格不等)5 !== "5"true数值与非数值比较,会转换数值后再比较字符串比较时,比较字符的Unicode编码null和undefined相等比较返回true===比较两个值是否全等,比较两个值的类型不同时直接返回false==会先将比较内容转换为相同类型,通常转换为数值console.log(5 == "5"); // true(字符串 "5" 转换为数字 5) console.log(5 === "5"); // false(类型不同) console.log(null == undefined); // true(特殊规则) console.log(null === undefined); // false(类型不同) console.log(0 == ""); // true(0 和空字符串均转为 0) console.log("" == false); // true(空字符串和 false 均转为 0) console.log(NaN == NaN); // false(NaN 不等于任何值,包括自身) console.log(NaN === NaN); // false条件运算符条件?表达式1:表达式2let a = 200; let b = 100; a > b ? console.log(a) : console.log(b)5.if语句5.1.语法if (condition) { // 当 condition 为 true 时执行的代码 } else if { //当条件不满足的其他另一情况 } else { //当上述条件都不满足 }说明:condition:布尔表达式,结果为 true 或 false。若 condition 为 true,执行代码块;否则退出 if 语句。JavaScript 会将非布尔值隐式转换为布尔值(Truthy/Falsy)。语句块中调用return方法后,退出if语句。其他条件分支则不会执行。if (age < 18) { console.log("未成年"); return; } else if (age >= 18 && age < 60) { console.log("成年且未退休"); } else { console.log("退休"); }6.switch语句6.1.基础语法switch (expression) { case value1: // 当 expression === value1 时执行 break; case value2: // 当 expression === value2 时执行 break; default: // 当所有 case 都不匹配时执行 }说明:expression:任意表达式(变量、函数调用、计算式等)。case:匹配 expression 的值,若匹配则执行对应代码块。break:跳出 switch,防止代码继续执行后续 case(必须显式添加)。default:可选分支,当所有 case 不匹配时执行。严格相等(===):case 的值必须与 expression 严格相等。若不写 break,代码会继续执行后续 caseconst fruit = "apple"; switch (fruit) { case "apple": console.log("Red or Green?"); break; case "banana": console.log("Yellow"); break; default: console.log("Unknown fruit"); }6.2.ES6新特性直接返回值,无需 break:const day = 3; const result = switch (day) { case 1 -> "Monday"; case 2 -> "Tuesday"; case 3 -> "Wednesday"; default -> "Other day"; }; console.log(result); // "Wednesday"多个 case 合并const status = 200; switch (true) { case (status >= 200 && status < 300): console.log("Success"); break; case (status === 404): console.log("Not Found"); break; default: console.log("Error"); }7.while语句7.1.基础语法while (condition) { // 当 condition 为 true 时执行的代码块 }说明:condition:布尔表达式,每次循环开始时检查。若为 true,执行循环体;否则退出循环。循环体:缩进的代码块,可包含任意语句。执行流程:1.检查条件 → 2.条件为 true 则执行循环体 → 3.重复步骤 1,直到条件为 false。死循环:while(true){}7.2.do...while会先执行一次循环体,再检查条件,至少执行一次:let count = 0; do { console.log("至少执行一次:", count); count++; } while (count < 3); // 输出:0,1,27.3.循环控制continue:跳过当前循环的剩余代码,进入下一次循环:let i = 0; while (i < 5) { i++; if (i === 3) continue; // 跳过 i=3 if (i === 5) break; // 当 i=5 时退出 console.log("Skipped:", i); } // 输出:1,2,4break:立即跳出当前循环。let num = 0; while (num < 10) { num++; if (num === 5) break; // 当 num=5 时退出循环 if (num === 8) break; // 当 num=8 时退出循环 console.log("Current value:", num); } // 输出:1,2,3,4嵌套循环的控制outer: while (true) { inner: while (true) { break outer; // 直接退出外层循环 } }8.for循环8.1.基础语法for (initialization; condition; iteration) { // 循环体 }说明:initialization:循环变量的初始化(仅在循环开始前执行一次)。condition:布尔表达式,每次循环开始前检查。若为 true,执行循环体;否则退出循环。iteration:迭代表达式,每次循环结束后执行(通常用于更新变量)。三个表达式都可以省略。执行流程:1.初始化变量 → 2. 检查条件 → 3. 条件为 true 则执行循环体 → 4. 执行迭代表达式 → 重复步骤 2。for (let i = 0; i < 5; i++) { console.log(i); // 输出 0, 1, 2, 3, 4 } //死循环 for (;;) {} 嵌套:for (let i = 0; i < 3; i++) { for (let j = 0; j < 2; j++) { console.log(`i=${i}, j=${j}`); } } // 输出:i=0,j=0 → i=0,j=1 → i=1,j=0 → ... 8.2.ES6新特性for...of遍历可迭代对象(如数组、字符串、Map 等):const arr = [1, 2, 3]; for (const value of arr) { console.log(value); // 输出 1, 2, 3 } const str = "hello"; for (const char of str) { console.log(char); // 输出 h, e, l, l, o }for...in遍历对象的可枚举属性(包括原型链属性):const obj = { a: 1, b: 2 }; for (const key in obj) { console.log(key); // 输出 "a", "b" }循环控制同while9.对象9.1.定义对象字面量const person = { name: "Alice", age: 25, greet() { console.log(`Hi, I'm ${this.name}`); } };构造函数function Person(name, age) { this.name = name; this.age = age; this.greet = function() { console.log(`Hi, I'm ${this.name}`); }; } const alice = new Person("Alice", 25);Object.create()const proto = { greet() { console.log("Hello!"); } }; const obj = Object.create(proto); obj.greet(); // 输出 "Hello!"ES6 Class 语法class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hi, I'm ${this.name}`); } } const bob = new Person("Bob", 30);9.2.常用操作属性访问与修改console.log(person.name); // "Alice" person.age = 26; // 修改属性 person["address"] = "New York"; // 动态添加属性计算属性名(ES6)const key = "email"; const user = { [key]: "alice@example.com" }; console.log(user.email); // "alice@example.com"删除属性delete person.age; // 移除 age 属性对象遍历for...in 循环for (const key in person) { if (person.hasOwnProperty(key)) { // 过滤原型链属性 console.log(`${key}: ${person[key]}`); } }Object.keys() / values() / entries()console.log(Object.keys(person)); // ["name", "greet"] console.log(Object.values(person)); // ["Alice", greet 函数] console.log(Object.entries(person)); // [["name", "Alice"], ["greet", ƒ]]for...of 与迭代器const obj = { [Symbol.iterator]() { let step = 0; return { next: () => ({ value: step++, done: step > 2 }) }; } }; for (const value of obj) { console.log(value); // 0, 1, 2 }9.3.Window对象Window 对象是浏览器中的全局对象,代表当前浏览器窗口或标签页。它是 JavaScript 运行环境的核心,所有全局变量、函数和对象均属于 Window 对象的属性和方法。9.3.1.属性属性描述window.navigator提供浏览器信息(如名称、版本、操作系统等)window.location获取或设置当前 URL,支持跳转、刷新等操作window.history管理浏览历史记录(前进、后退、跳转)window.screen获取屏幕信息(分辨率、颜色深度等)window.document代表当前页面的文档对象(DOM 树的入口)window.frames获取所有子框架(<iframe>)的集合window.parent获取当前窗口的父窗口(若为 <iframe>)console.log(window.navigator.userAgent); // 浏览器用户代理字符串 console.log(window.location.href); // 当前页面 URL const element = window.document.getElementById("myElement");9.3.2.方法方法描述setTimeout(fn, ms)延迟执行函数 fn,超时时间为 ms 毫秒setInterval(fn, ms)每隔 ms 毫秒重复执行函数 fnclearTimeout(id)清除由 setTimeout 设置的定时器clearInterval(id)清除由 setInterval 设置的定时器const timer = setTimeout(() => { console.log("延迟 2 秒执行"); }, 2000); clearTimeout(timer); // 取消定时器方法描述window.open(url)打开新窗口或标签页window.close()关闭当前窗口(仅限通过 open() 打开的窗口)window.alert(msg)显示警告对话框window.confirm(msg)显示确认对话框,返回布尔值window.prompt(msg)显示输入对话框,返回用户输入值window.open("https://www.example.com");s9.4.原型对象9.4.1.原型概念每个函数(构造函数)都有一个 prototype 属性,指向一个原型对象。通过 new 创建的实例对象的 [[Prototype]](即 __proto__)会指向构造函数的 prototype 对象。原型对象本身也是一个普通对象,可以包含属性和方法。static 方法属于类本身,不挂载到原型。普通方法挂载到原型对象。function Person(name) { this.name = name; } const p1= new Person('1'); // 原型对象 console.log(Person.prototype); // Person {} console.log(Object.getPrototypeOf(p1)) console.log(p1.__proto__);9.4.2.原型作用将一个该类实例中所有的公共属性(方法)统一存储到原型,这样只需创建一个属性,即可被所有实例访问通过修改原型链实现继承:class Student extends Person { constructor(name, grade) { super(name); this.grade = grade; } } const bob = new Student("Bob", 12); bob.sayHi(); // 继承自 Person 的方法将方法定义在原型上,避免每个实例重复创建:function Person(name) { this.name = name; } Person.prototype.sharedMethod = function() { console.log("Shared method"); }; const alice = new Person("Alice"); const bob = new Person("Bob"); console.log(alice.sharedMethod === bob.sharedMethod); // true(同一函数引用)9.4.3.原型属性查找规则当访问对象的属性时,JavaScript 引擎会:在对象自身查找。若未找到,沿 [[Prototype]] 链向上查找,直到原型链末端(null)。示例:Person.prototype.sayHi = function() { console.log(`Hi, I'm ${this.name}`); }; const alice = new Person("Alice"); alice.sayHi(); // "Hi, I'm Alice"(通过原型链找到方法)原型链的层级所有对象最终通过原型链继承自 Object.prototype。Object.prototype 的 [[Prototype]] 为 null,形成链的终点。console.log(alice.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null9.4.4.操作原型修改原型对象通过构造函数的 prototype 属性添加方法或属性,所有实例共享这些属性。注意:直接修改 Object.prototype 会污染全局对象,应避免。// 添加方法到原型 Person.prototype.age = 30; alice.age; // 30(所有实例共享)使用 Object.create()通过 Object.create(proto) 创建一个新对象,并指定其原型:const bob = Object.create(Person.prototype); bob.name = "Bob"; bob.sayHi(); // "Hi, I'm Bob"10.函数10.1.定义函数(Function)也是一个对象创建方式:// 函数声明(函数名可复用) function greet(name) { return `Hello, ${name}!`; } // 函数表达式(匿名函数赋值给变量) const sayGoodbye = function(name) { return `Goodbye, ${name}!`; }; // 箭头函数(ES6+) const add = () => console.log(111);函数调用:function(name){ return 'Hello' + name; } console.log(greet("Alice")); // "Hello, Alice!" console.log(sayGoodbye("Bob")); // "Goodbye, Bob!" console.log(add(2, 3)); // 510.2.参数定义默认参数:如果调用方未传入参数。function greet(name = "Guest") { return `Hello, ${name}!`; } console.log(greet()); // "Hello, Guest!"剩余参数(Rest Parameters):接收可变参数function sum(...numbers) { return numbers.reduce((acc, num) => acc + num, 0); } console.log(sum(1, 2, 3, 4)); // 10解构赋值参数:function printUser({ name, age }) { console.log(`Name: ${name}, Age: ${age}`); } printUser({ name: "Alice", age: 25 }); // "Name: Alice, Age: 25"函数参数:function executeTwice(fn) { fn(); fn(); } executeTwice(() => console.log("Hello!")); // 输出两次 "Hello!"注意事项:JS不会检查参数类型,可以传递任意类型。实参多余形参,多余的实参不会使用。形参多余实参,多余的形参为undefined。箭头函数只有一个参数时,括号可以省略。function hello(name, content){ alert(`hello,${name},${content}`); } hello('sam');//hello,sam,undefined const read = content => {}10.3.返回值显式返回:使用 return 返回值。隐式返回:箭头函数若无 return,默认返回 undefined。无返回值:未使用 return 或返回 undefined。function multiply(a, b) { return a * b; // 显式返回 } const divide = (a, b) => a / b; // 隐式返回特殊情况:返回函数:function createAdder(a) { return function(b) { return a + b; }; } const add5 = createAdder(5); console.log(add5(3)); // 8箭头函数返回值:可以直接写在箭头后const sum = (a, b) => a + b //直接返回对象时,需要用()包裹 const fn = () => ({name:"sam"})10.4.作用域10.4.1.词法作用域(Lexical Scope)函数内部可以访问外部作用域的变量,但外部无法访问内部变量。const outerVar = "I am outer"; function innerFunction() { const innerVar = "I am inner"; console.log(outerVar); // 输出 "I am outer" } innerFunction(); console.log(innerVar); // 报错(innerVar 未定义)10.4.2.闭包(Closure)闭包是函数与其词法环境的组合,允许函数访问外部变量。function createCounter() { let count = 0; return function() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2JS会优先选择当前作用域的变量进行使用let a = 1; let b = 1; { let a = 2; let sum = a + b; }10.5.立即执行函数格式:(function)()//立即执行,且只会调用一次 (function(){ let a = 10; console.log(111); }());10.6.this关键字10.6.1.普通函数默认绑定:在非严格模式下,this 默认指向全局对象(浏览器中是 window,Node.js 中是 global)。在严格模式下,this 为 undefined。function foo() { console.log(this); } foo(); // 非严格模式:window;严格模式:undefined隐式绑定:当函数作为对象的方法调用时,this 指向调用该方法的对象。const obj = { name: "Alice", sayHi() { console.log(this.name); } }; obj.sayHi(); // "Alice"显式绑定:使用 call、apply 或 bind 显式指定 this。function greet(greeting) { console.log(`${greeting}, ${this.name}`); } const user = { name: "Bob" }; greet.call(user, "Hello"); // "Hello, Bob"new 绑定:使用 new 调用构造函数时,this 指向新创建的对象。function Person(name) { this.name = name; } const alice = new Person("Alice"); console.log(alice.name); // "Alice"10.6.2.箭头函数箭头函数没有自己的 this,继承自外层作用域的 this。const obj = { value: 42, getValue: function() { console.log(this.value); }, getValueArrow: () => { console.log(this.value); // undefined(箭头函数继承全局 this) } }; obj.getValue(); // 42 obj.getValueArrow(); // undefined11.类11.1.类的定义class Person { name = ""; age = 1; // 构造函数 constructor(name, age) { this.name = name; this.age = age; } // 实例方法 greet() { console.log(`Hi, I'm ${this.name}`); } } // 创建实例 const alice = new Person("Alice", 25); alice.greet(); // "Hi, I'm Alice"11.2.属性实例属性:只能通过实例进行访问class Person { name = "lisi"; } const lisi = new Person(); lisi.name;静态属性:static 关键字定义类属性,通过类名.获取class Person { static name = "lisi"; } Person.name私有属性:外部无法直接访问的属性class Person { #name; constructor(name){ this.#name = name; } getName(){ return this.#name } }11.3.方法实例方法:通过实例对象访问class Person { read(){ console.log('read'); } } new Person().read();静态方法:通过类名访问class Person { static read(){ console.log('read'); } } Person.read();11.4.构造函数作用:初始化对象的属性。特点:每个类只能有一个构造函数,若未显式定义,会自动生成一个空构造函数。class Person { constructor(name,age){ this.name = name; this.age = age; } }11.5.封装、继承、多态封装:将属性、方法等封装为一个对象,并将他们私有化,以保护该对象数据。继承:通过 extends 关键字实现继承:class Student extends Person { constructor(name, age, grade) { super(name, age); // 调用父类构造函数 this.grade = grade; } study() { console.log(`${this.name} is studying in grade ${this.grade}`); } } const bob = new Student("Bob", 18, 12); bob.greet(); // "Hi, I'm Bob"(继承自 Person) bob.study(); // "Bob is studying in grade 12"多态:JS不会检查参数类型,调用某个函数,无需指定的类型,只需满足某些条件。11.6.InstanceOf&hasOwnPropertyinstanceOf检查一个对象是否是某个构造函数的实例(即是否存在于其原型链中)。instanceof 会沿着对象的原型链([[Prototype]])向上查找,直到找到匹配的构造函数的 prototype 属性或到达原型链末端(null)。// function Person() {} const alice = new Person(); console.log(alice instanceof Person); // true console.log(alice instanceof Object); // true(所有对象最终继承自 Object) function Animal() {} function Dog() {} Dog.prototype = Object.create(Animal.prototype); const dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true(原型链继承)检查对象自身(不含原型链)是否具有某个属性。const obj = { a: 1 }; console.log(obj.hasOwnProperty("a")); // true console.log(obj.hasOwnProperty("toString")); // false(继承自 Object.prototype) const parent = { inherited: "I am inherited" }; const child = Object.create(parent); child.own = "I am own"; console.log("inherited" in child); // true(原型链上的属性) console.log(child.hasOwnProperty("inherited")); // false12.数组12.1.定义// 定义数组 const arr1 = [1, 2, 3]; // 字面量 const arr2 = new Array(3); // 构造函数(长度为3的空数组) const arr3 = Array.of(1, "a"); // ES6 方法(支持混合类型) console.log(typeof arr1); // "object"(数组本质是对象) console.log(Array.isArray(arr1)); // true(判断是否为数组)特性:有序性:元素按索引(0 开始)排列。灵活性:允许混合类型元素(数字、字符串、对象等)。动态长度:可随时添加或删除元素。12.2.常用操作增删改查方法描述示例push(...items)末尾添加元素,返回新长度arr.push(4) → [1,2,3,4]pop()删除末尾元素,返回被删元素arr.pop() → 4shift()删除头部元素,返回被删元素arr.shift() → 1unshift(...items)头部添加元素,返回新长度arr.unshift(-1) → [-1,1,2]splice(start, deleteCount, ...items)插入/删除元素(修改原数组)arr.splice(1, 1, "a") → 替换索引1的元素截取与合并方法描述示例slice(start, end)截取子数组(不修改原数组)arr.slice(1, 3) → [2,3]concat(...arrays)合并多个数组,返回新数组arr.concat([4,5]) → [1,2,3,4,5]遍历与映射方法描述示例forEach(callback)遍历数组,执行回调函数arr.forEach((item) => console.log(item))map(callback)生成新数组(元素经回调处理)arr.map(x => x * 2) → [2,4,6]filter(callback)筛选符合条件的元素,生成新数组arr.filter(x => x > 1) → [2,3]reduce(callback)累计计算(如求和、求积)arr.reduce((sum, x) => sum + x, 0) → 6扩展运算符(Spread Operator)const arr1 = [1, 2]; const arr2 = [...arr1, 3]; // [1,2,3] const combined = [...arr1, ...arr2]; // [1,2,1,2,3]解构赋值const [first, second] = [1, 2]; console.log(first); // 1 console.log(second); // 2Array.from():将类数组对象如arguments转为数组const listItems = document.querySelectorAll("li"); const arr = Array.from(listItems); // 转为数组Array.find():查找第一个符合条件的元素。const user = users.find(u => u.id === 1);Array.some()/every():检测数组是否满足条件。arr.some(x => x > 3); // 是否存在大于3的元素 arr.every(x => x > 0); // 是否所有元素都大于012.3.实战示例去重const arr = [1, 2, 2, 3]; const uniqueArr = [...new Set(arr)]; // [1,2,3]扁平化嵌套数组const nested = [1, [2, [3]]]; const flat = nested.flat(Infinity); // [1,2,3]统计元素频率const arr = ["apple", "banana", "apple"]; const frequency = arr.reduce((acc, item) => { acc[item] = (acc[item] || 0) + 1; return acc; }, {}); // { apple: 2, banana: 1 }
2023年05月03日
125 阅读
0 评论
0 点赞