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}(?: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)。
  • 访问器属性:通过getset方法控制访问:

    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 (深拷贝后的对象不受影响)

    局限性

    • 无法处理函数、undefinedDateMapSet 等特殊对象。
    • 无法处理循环引用(会导致栈溢出)。
  • 递归实现深拷贝

    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 (完全独立)

    优点

    • 可以处理复杂对象(如 DateMapSet 等)。
    • 支持循环引用。
  • 使用库(如 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()
  • 作用:结合了 mapflat 的功能,先对数组中的每个元素执行映射函数,然后将结果数组拍平一层。
  • 语法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]]
    • 局限性

      • 无法处理函数、undefinedDateMapSet 等特殊对象。
      • 无法处理循环引用。
  • 递归实现深拷贝

    • 作用:手动递归拷贝数组及其嵌套元素。
    • 实现

      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]
  • 使用 filterindexOf

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

4.高阶函数

4.1.定义

高阶函数指:

  • 接收函数作为参数(如 mapfilter)。

    // 示例:回调函数
    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.keyevent.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()显示输入对话框(返回输入值或 nulllet 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 = '<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);
最后修改:2025 年 05 月 11 日
如果觉得我的文章对你有用,请随意赞赏