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>匹配简单标签内容(如 Hello)。
<([a-z]+)([^<]+)*(?:>(.*)<\/\1>)?更通用的标签匹配(支持属性和嵌套内容)。
提取 URL 参数[?&]([^=#]+)=([^&#]*)提取查询字符串中的键值对(如 name=Alice&age=30)。
(?<=[?&])([^=&]+)=([^&]*)使用正向后顾匹配参数键值对。
隐藏手机号中间四位(\d{3})\d{4}(\d{4})将手机号中间四位替换为星号(如 138****8000)。
(\d{3})\d{3}\d{4}另一种替换方式(如 138-***-8000)。
千分位分隔符(?]+>删除所有 HTML 标签(如 Hello → 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 = 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)) {
        // 处理自身属性
      }
    }
  1. 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性能优化注意事项

  1. push 和 pop:O(1) 时间复杂度,效率高。
  2. unshift 和 shift:O(n) 时间复杂度,效率较低,尽量避免在大数据量时使用。
  3. splice:性能取决于删除/插入的位置和数量,尽量避免频繁使用。
  4. map 和 filter:遍历整个数组,复杂度为 O(n),适合中小规模数据。
  5. 修改方法:push, pop, shift, unshift, splice, reverse, sort 会改变原数组。
  6. 安全方法:map, filter, slice, concat 返回新数组。
  7. sort() 的默认行为:默认按字符串排序,需传入比较函数进行数值或自定义排序。
  8. 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.事件传播机制

事件传播分为三个阶段:

  1. 捕获阶段(Capture Phase):事件从 window 向下传递到目标元素。
  2. 目标阶段(Target Phase):事件到达目标元素。
  3. 冒泡阶段(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 或 ``
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)
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 = 'New content';
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("Hello");
document.writeln()写入 HTML 内容并换行。document.writeln("Line 1 Line 2");

示例代码

// 通过 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);

最后修改:2026 年 01 月 22 日
如果觉得我的文章对你有用,请随意赞赏