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")
→1
lastIndexOf(searchValue)
返回子字符串最后一次出现的索引 "apple".lastIndexOf("p")
→2
includes(searchValue)
是否包含子字符串(ES6) "apple".includes("app")
→true
startsWith(searchValue)
是否以指定字符串开头(ES6) "apple".startsWith("ap")
→true
endsWith(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/)); // 7
replace()
替换字符串中的匹配项。
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}(?:19 | 20)\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 = 20
Reflect.deleteProperty()
:Reflect.deleteProperty(obj, 'name'); // 等价于 delete obj.name
Reflect.construct()
:const Person = function(name) { this.name = name; }; const instance = Reflect.construct(Person, ['dengke']);
Reflect.has()
:Reflect.has(obj, 'name'); // 等价于 'name' in obj
2.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 同样变为 2
delete
操作符的限制:无法删除不可配置(
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], 返回 3
shift()
- 作用:删除数组开头的元素,并返回被删除的元素。
示例:
const arr = [1, 2, 3]; arr.shift(); // arr => [2, 3], 返回 1
splice()
- 作用:删除数组指定位置的元素。
- 语法:
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); // 返回 1
lastIndexOf()
- 作用:从数组末尾开始查找元素,返回最后一个匹配的索引,未找到返回
-1
。 示例:
const arr = [1, 2, 3, 2]; arr.lastIndexOf(2); // 返回 3
find()
- 作用:查找数组中第一个满足条件的元素,返回该元素。
示例:
const arr = [1, 2, 3, 4]; arr.find(item => item > 2); // 返回 3
findIndex()
- 作用:查找数组中第一个满足条件的元素的索引,未找到返回
-1
。 示例:
const arr = [1, 2, 3, 4]; arr.findIndex(item => item > 2); // 返回 2
findLast()
/ findLastIndex()
- 作用:查找最后一个符合条件的元素:
示例:
const arr = [1, 2, 3, 2]; arr.findLastIndex(x => x === 2); // 3 const arr = [1, 2, 3, 2]; arr.findLast(x => x > 2); //3
3.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, 3
entries
- 作用:返回一个包含数组中每个索引的键值对的迭代器。
- 语法:
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 => 10
flat
- 作用:创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。
- 语法:
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({}); // false
3.9. 其他常用方法
includes()
- 作用:判断数组是否包含某个值,返回布尔值。
示例:
const arr = [1, 2, 3]; arr.includes(2); // true
reverse()
- 作用:反转数组。
示例:
const arr = [1, 2, 3]; arr.reverse(); // arr => [3, 2, 1]
every()
- 作用:判断数组中所有元素是否满足条件。
示例:
const arr = [2, 4, 6]; arr.every(item => item % 2 === 0); // true
some()
- 作用:判断数组中是否存在至少一个满足条件的元素。
示例:
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)); // 8
4.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); // 输出日志并返回5
4.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()); // 1
count
是外部函数Counter
的局部变量,通过闭包被内部方法访问,外部无法直接修改。
保存状态(持续化变量)
计数器示例:
function createCounter() { let count = 0; return () => count++; } const counter = createCounter(); console.log(counter()); // 0 console.log(counter()); // 1
count
的值在每次调用时被保留,形成“持续状态”。
回调函数与异步编程
异步操作中保存上下文:
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"); // true
map.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" });
// 对象转 Map
const 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
// 3
const 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.BOM
11.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 | 获取浏览器是否启用cookie |
navigator.onLine | 获取浏览器是否处于在线模式 |
navigator.platform | 获取运行浏览器的操作系统平台 |
方法名 | 说明 |
---|---|
geolocation.getCurrentPosition() | 获取用户地理位置 |
vibrate() | 触发设备振动(需权限) |
sendBeacon() | 异步发送小数据到服务器 |
11.3.Location
浏览器的地址栏信息
属性名 | 说明 |
---|---|
href | 完整 URL(如 https://example.com/path?query#hash ) |
protocol | URL 的协议(如 https: ) |
host | 主机名 + 端口号(如 example.com:8080 ) |
hostname | 主机名(如 example.com ) |
port | 端口号(如 8080 ) |
pathname | 路径部分(如 /path ) |
search | 查询字符串(如 ?key=value ) |
hash | 锚点部分(如 #section ) |
origin | 协议、主机和端口的组合(如 https://example.com ) |
searchParams | URL 查询参数的 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);