内置对象

coderljw 2024-10-13 JS 基础
  • JS 基础
  • 内置对象
大约 16 分钟

# 1. JSON

  • JSON.stringify(value, [replacer], [space])

    • 第二个参数过滤器可以是数组和函数。为数组时,仅在第一个参数是对象时生效,不存在于数组中的属性会被忽略。
    • 第三个参数缩进字符可以是数字和字符串。为数字时代表空格数,范围在 [0, 10];为字符串时取前 10 个字符。
    • 因多空格会被浏览器默认忽略,搭配 CSS 设置才可在页面中正常显示(JSON 美化)。
    const foo = {
      name: '徐扶墙',
      age: 17,
      wife: '姜姒',
    }
    
    JSON.stringify(foo, ['wife'], 4) // => '{    "wife": "姜姒"}'
    
    JSON.stringify(
      foo,
      (key, value) => {
        if (key === 'wife') return '裴南苇'
        return value
      },
      '--'
    ) // => '{--"name": "徐扶墙",--"age": 17,--"wife": "裴南苇"}'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • JSON.stringify() 在对象中值为 undefined、函数、Symbol 会被忽略,在数组中会转为 null;NaN 和 Infinity 皆转为 null,正则、Set、Map 等其余对象皆转为空对象。

    JSON.stringify({
      undefined: undefined,
      func() {},
      Symbol: Symbol(7),
      NaN: NaN,
      Infinity: Infinity,
      RegExp: /777/,
    }) // => '{"NaN":null,"Infinity":null,"RegExp":{}}'
    
    JSON.stringify([undefined, _ => {}, Symbol(7), NaN, Infinity, /777/]) // => '[null,null,null,null,null,{}]'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • JSON.stringify() 循环引用会报错,不可枚举属性会忽略。

    const foo = {}
    foo.bar = foo
    
    JSON.stringify(foo) // => Uncaught TypeError
    
    JSON.stringify(
      Object.create(null, {
        name: { value: '徐扶墙', enumerable: false },
        wife: { value: '姜姒', enumerable: true },
      })
    ) // => '{"wife":"姜姒"}'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • toJSON(),自定义序列化。

    const foo = {
      name: '徐扶墙',
      age: undefined,
      daughter: '柿子',
      harem: ['姜姒', undefined, '裴南苇'],
      toJSON: _ => '南宫仆射',
    }
    
    JSON.stringify(foo) // => '"南宫仆射"'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • JSON.parse(text, [reviver])

    第二个参数为转换器函数。

    const foo = {
      name: '徐扶墙',
      age: 17,
      wife: '姜姒',
    }
    
    JSON.parse(JSON.stringify(foo), (key, value) => {
      if (key === 'wife') return '裴南苇'
      return value
    }) // => {name: '徐扶墙', age: 17, wife: '裴南苇'}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 2. Date

  • 获取当前时间戳。

    Date.now() // => -28800000
    +new Date() // => -28800000
    new Date().getTime() // => // -28800000
    
    1
    2
    3
  • 转 ISO 时间格式(比北京时间慢 8 小时)。

    new Date().toISOString() // => '1969-12-31T16:00:00.000Z'
    
    1
  • 根据地区(浏览器主语言)格式化时间。

    new Date().toLocaleString() // => '1970/1/1 上午12:00:00'
    new Date().toLocaleTimeString() // => '上午12:00:00'
    
    1
    2
  • 计算季度。

    (new Date().getMonth() + 3) / 3 | 0 // => 1
    
    1
  • 获取间隔日期表。

    const getDateList = options => {
      const { date = new Date(), days = 7, spacing = 1 } = options || {}
      const size = 1000 * 60 * 60 * 24 * spacing
      const ary = []
      while (ary.length < days) {
        ary.push({
          year: date.getFullYear(),
          date: `${(date.getMonth() + 1).toString().padStart(2, 0)}-${date
            .getDate()
            .toString()
            .padStart(2, 0)}`,
          week: `${['日', '一', '二', '三', '四', '五', '六'][date.getDay()]}`,
        })
        date.setTime(date.getTime() + size)
      }
    
      return ary
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • 根据日期获取间隔时间表。

    const getTimeList = options => {
      const {
        date = `${new Date().getFullYear()}/${new Date().getMonth() +
          1}/${new Date().getDate()}`,
        startTime = '09:00:00',
        times = 27,
        spacing = 30,
      } = options || {}
      const startDate = new Date(`${date} ${startTime}`)
      const size = 1000 * 60 * spacing
      const ary = []
      while (ary.length < times) {
        ary.push(
          `${startDate
            .getHours()
            .toString()
            .padStart(2, 0)}:${startDate
            .getMinutes()
            .toString()
            .padStart(2, 0)}`
        )
        startDate.setTime(startDate.getTime() + size)
      }
    
      // 过滤过期时间
      return ary
        .filter(t => +new Date(`${date} ${t}:00`) > Date.now())
        .map((t, i) => ({
          label: t,
          value: i + 1,
        }))
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

# 3. RegExp

  • 先行、后行断言。

    // 正向先行断言
    'clearlove'.replace(/(?=love)/gi, '7') // => 'clear7love'
    
    // 负向先行断言
    'clearlove'.replace(/(?!love)/gi, '7') // => '7c7l7e7a7rl7o7v7e7'
    
    // 正向后行断言
    'clearlove'.replace(/(?<=love)/gi, '7') // => 'clearlove7'
    
    // 负向后行断言
    'clearlove'.replace(/(?<!love)/gi, '7') // => '7c7l7e7a7r7l7o7v7e'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1. 数字的千分位分割法。
    '1234567'.replace(/(?!^)(?=(\d{3})+$)/g, ',') // => '1,234,567'
    
    1
    1. 手机号 3-4-4 分割。
    '17777777777'.replace(/(?=(\d{4})+$)/g, '-') // => '177-7777-7777'
    
    1
    1. 隐藏中间四位手机号码。
    '17777777777'.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2') // => 177****7777
    
    1
    1. 校验中文。
    ['Neo', '徐扶墙', '7酱'].map(i => /^[\u4e00-\u9fa5]{0,}$/.test(i)) // => [false, true, false]
    
    1
    1. 校验密码。
    // 密码长度是 6-12 位,由数字、小写字符和大写字母组成,但必须至少包括 2 种字符
    ['123456', 'admin123'].map(i =>
      /((?=.*\d)((?=.*[a-z])|(?=.*[A-Z])))|(?=.*[a-z])(?=.*[A-Z])^[a-zA-Z\d]{6,12}$/.test(i)
    ) // => [false, true]
    
    1
    2
    3
    4
    1. 校验手机号和座机号。
    ['17777777777', '17979797979'].map(i =>
      /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/.test(i)
    ) // => [true, false]
    
    1
    2
    3
    ['0571-7777777', '0797-7777777'].map(i =>
      /^(?:(?:\d{3}-)?\d{8}|^(?:\d{4}-)?\d{7,8})(?:-\d+)?$/.test(i)
    ) // => [true, true]
    
    1
    2
    3
    1. 校验邮箱。
    ['7777777@163.com', '7酱@qq.com'].map(i =>
      /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(i)
    ) // => [true, true]
    
    1
    2
    3
    1. 校验网址。
    ['https://www.coderljw.ga', 'https://github.com/Matrix-The-One'].map(i =>
      /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#\-\(\)]*[\w@?^=%&/~+#\-\(\)])?$/.test(i)
    ) // => [true, true]
    
    1
    2
    3
    1. 校验身份证。
    // 一代 + 二代
    const IDCardReg = /^\d{6}((((((19|20)\d{2})(0[13-9]|1[012])(0[1-9]|[12]\d|30))|(((19|20)\d{2})(0[13578]|1[02])31)|((19|20)\d{2})02(0[1-9]|1\d|2[0-8])|((((19|20)([13579][26]|[2468][048]|0[48]))|(2000))0229))\d{3})|((((\d{2})(0[13-9]|1[012])(0[1-9]|[12]\d|30))|((\d{2})(0[13578]|1[02])31)|((\d{2})02(0[1-9]|1\d|2[0-8]))|(([13579][26]|[2468][048]|0[048])0229))\d{2}))(\d|X|x)$/
    
    // 一代
    const firstIDCardReg = /^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/
    
    // 二代
    const secondIDCardReg = /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/
    
    1
    2
    3
    4
    5
    6
    7
    8
    1. 校验车牌。
    // 新能源 + 非新能源
    const VRPReg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/
    
    // 新能源
    const newEnergyVRPReg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z](?:((\d{5}[A-HJK])|([A-HJK][A-HJ-NP-Z0-9][0-9]{4}))|[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳])$/
    
    // 非新能源
    const traditionVRPReg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$/
    
    1
    2
    3
    4
    5
    6
    7
    8
    1. 校验 16 进制的颜色值。
    ['#Fc01DF', '#Fff'].every(i => /#([a-fA-F\d]{6}|[a-fA-F\d]{3})/g.test(i)) // => true
    
    1
    1. 校验 24 小时制时间。
    ['43:96', '7:00', '23:59'].map(i =>
      /^(0?\d|1\d|2[0-3]):(0?|[1-5])\d/.test(i)
    ) // => [false, true, true]
    
    1
    2
    3
    1. 校验日期。
    // 易解版(不能判断当月最大天数及平润年)
    ['1970-01-01', '1964/9/2', '2000.02.30'].map(i =>
      /\d{4}([-/.])(0?\d|1[0-2])\1(0?[1-9]|[12]\d|3[01])/.test(i)
    ) // => [true, true, true]
    
    // 完全版
    ['1970-01-01', '1964/9/2', '2002.02.29'].map(i =>
      /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/.test(
        i
      )
    ) // => [true, true, false]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1. HTML 转义和反转义。
    const escapeHTML = str => {
      const escapeChars = {
        '<': 'lt',
        '>': 'gt',
        '"': 'quot',
        "'": 'apos',
        '&': 'amp',
      }
      // 为了得到字符组[<>"'&]
      const regexp = new RegExp(`[${Object.keys(escapeChars).join('')}]`, 'g')
      return str.replace(regexp, c => `&${escapeChars[c]};`)
    }
    
    escapeHTML('<div>Blah blah blah</div>') // => '&lt;div&gt;Blah blah blah&lt;/div&gt;'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const unescapeHTML = str => {
      const htmlEntities = {
        nbsp: ' ',
        lt: '<',
        gt: '>',
        quot: '"',
        apos: "'",
        amp: '&',
      }
      return str.replace(/&([^;]+);/g, ($0, $1) => htmlEntities[$1] || '')
    }
    
    unescapeHTML('&lt;div&gt;Blah blah blah&lt;/div&gt;') // => '<div>Blah blah blah</div>'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    提示

    new RegExp() 可以使用变量,字面量写法(/RegExp/)不行。

  • JavaScript 正则表达式迷你书(1.1 版)- 老姚 (opens new window)

  • 就因为这三个知识点,我彻底学废了”正则表达式“ - 前端胖头鱼 (opens new window)

# 4. Math

  • 获取 [2, 9] 随机整数(Math.random() * (max - min + 1) + min | 0)。

    Math.random() * 8 + 2 | 0
    
    1
  • 四舍五入保留小数。

    function $toFixed(val, exp) {
      return val !== void 0
        ? +(Math.round(val * Math.pow(10, exp)) / Math.pow(10, exp)).toFixed(exp)
        : +(0).toFixed(exp)
    }
    
    1
    2
    3
    4
    5
  • 便捷打乱数组。

    [1, 2, 3, 4, 5].sort(_ => Math.random() - 0.5)
    
    1
  • 生成由 [0-9a-z] 组成的 10 位随机字符串。

    Math.random().toString(36).substr(2, 10)
    
    1

# 5. Promise

  • 用于表示一个异步操作的最终完成(或失败)及其结果值(Promise A+ 规范 (opens new window))。

    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (delay === 4396) reject('clearlove7')
          resolve(delay)
        }, delay)
      })
    }
    
    foo(777)
      .then(res => {
        console.log(res) // => 777
        return foo(4396)
      })
      .then(console.log) // => 未调用
      .catch(console.log) // => 'clearlove7'
      .finally(_ => console.log('javalove')) // => 'javalove'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • async + await

    await 不能捕获 Promise 的 rejected 状态和抛出的错误。

    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (delay === 4396) reject('clearlove7')
          resolve(delay)
        }, delay)
      })
    }
    
    async function bar() {
      const res = await foo(777)
      console.log(res) // => 777
    
      await foo(4396).catch(console.log) // => 'clearlove7'
    }
    
    bar()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • Promise.all()

    • 当数组中所有 promise 都为 fulfilled(成功态)时 Promise.all() 状态为 resolve,返回一个 resolve 结果数组。
    • 反之 Promise.all() 状态为 reject,返回最先 rejected(失败态)promise 的 reject 值。
    • 注意:Promise.all([]) 会立即完成。
    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if ([4396, 2200].includes(delay)) {
            reject({ 4396: 'clearlove7', 2200: 'xiaohu' }[delay])
          }
          resolve(delay)
        }, delay)
      })
    }
    
    async function bar() {
      const res = await Promise.all([foo(777), foo(1557)])
      console.log(res) // => [777, 1557]
    
      await Promise.all([foo(777), foo(4396), foo(2200)]).catch(console.log) // => 'xiaohu'
    }
    
    bar()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • Promise.allSettled()

    • 与 Promise.all() 类似,但数组中 promise 成功与否 Promise.allSettled() 状态都为 resolve,返回由状态和值(成功态)/ 原因(失败态)构成的数组。
    • 注意:Promise.allSettled([]) 会立即完成。
    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (delay === 4396) reject('clearlove7')
          resolve(delay)
        }, delay)
      })
    }
    
    async function bar() {
      const res = await Promise.allSettled([foo(777), foo(4396)])
      console.log(res) // => [{status: 'fulfilled', value: 777}, {status: 'rejected', reason: 'clearlove7'}]
    }
    
    bar()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • Promise.race()

    • 返回数组中状态最先确认的 promise。
    • 注意:Promise.race([]) 会永远挂起。
    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if ([4396, 999].includes(delay)) {
            reject({ 4396: 'clearlove7', 999: 'kid' }[delay])
          }
          resolve(delay)
        }, delay)
      })
    }
    
    async function bar() {
      const res = await Promise.race([foo(777), foo(4396)])
      console.log(res) // => 777
    
      await Promise.race([foo(1557), foo(999)]).catch(console.log) // => 'kid'
    }
    
    bar()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • Promise.any() —— 起草方案

    • 与 Promise.all() 相反,当数组中有一个 promise 为 fulfilled(成功态)时 Promise.any() 状态为 resolve,返回状态最先确认的 promise。
    • 反之 Promise.any() 状态为 reject,返回 'AggregateError: All promises were rejected'。
    • 注意:Promise.any([]) 会立即失败。
    function foo(delay) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if ([4396, 2200].includes(delay)) {
            reject({ 4396: 'clearlove7', 2200: 'xiaohu' }[delay])
          }
          resolve(delay)
        }, delay)
      })
    }
    
    async function bar() {
      const res = await Promise.any([foo(777), foo(1557), foo(4396)])
      console.log(res) // => 777
    
      await Promise.any([foo(4396), foo(2200)]).catch(console.log) // => 'AggregateError: All promises were rejected'
    }
    
    bar()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 值穿透。

    Promise.resolve(777)
      .then(4396)
      .then(console.log) // => 777
    
    Promise.resolve(777)
      .then(Promise.resolve(4396))
      .then(console.log) // => 777
    
    1
    2
    3
    4
    5
    6
    7
  • 【建议星星】要就来 45 道 Promise 面试题一次爽到底(1.1w 字用心整理) - LinDaiDai_霖呆呆 (opens new window)

# 6. Error

  • throw 抛出 Error 错误,可终止函数运行。
throw new Error('抛出一个错误')

EvalError('eval错误')
InternalError('JavaScript引擎内部异常错误')
RangeError('值不在其所允许的范围或者集合中错误')
ReferenceError('引用错误')
SyntaxError('语法错误')
TypeError('类型错误')
URIError('URI处理函数产生的错误')
1
2
3
4
5
6
7
8
9

# 7. Proxy

const foo = {
  name: '徐扶墙',
  age: 17,
  wife: ['姜姒', '裴南苇'],
  skill: {
    name: '天下第二',
  },
}

const proxy = new Proxy(foo, {
  get(target, key) {
    if (key === 'wife') console.log('风紧,扯呼!')
    return target[key]
  },
  set(target, key, value) {
    console.log('裆下有些忧郁啊!') // 设置 proxy.skill.name 的值时不会打印这句话
    return (target[key] = value)
  },
})

proxy.wife // '风紧,扯呼!'
proxy.name = '徐凤年' // '裆下有些忧郁啊!'
proxy.skill.name = '天下第一'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 8. Reflect

  • Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API(ECMAScript 6 入门 - 阮一峰 (opens new window))。

    1. 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。
    1. 修改某些 Object 方法的返回结果,让其变得更合理。
    // 老写法
    try {
      Object.defineProperty(target, property, attributes)
      // success
    } catch (e) {
      // failure
    }
    
    // 新写法
    if (Reflect.defineProperty(target, property, attributes)) {
      // success
    } else {
      // failure
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    1. 让 Object 操作都变成函数行为。
    // 老写法
    'assign' in Object // => true
    
    // 新写法
    Reflect.has(Object, 'assign') // => true
    
    1
    2
    3
    4
    5
    1. Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。
    const loggedObj = new Proxy(obj, {
      get(target, name) {
        console.log('get', target, name)
        return Reflect.get(target, name)
      },
      deleteProperty(target, name) {
        console.log('delete' + name)
        return Reflect.deleteProperty(target, name)
      },
      has(target, name) {
        console.log('has' + name)
        return Reflect.has(target, name)
      },
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    1. Reflect.apply。
    // 老写法
    Function.prototype.apply.call(Math.floor, undefined, [1.75]) // => 1
    
    // 新写法
    Reflect.apply(Math.floor, undefined, [1.75]) // => 1
    
    1
    2
    3
    4
    5

# 9. Set & WeakSet

  • Set 传入可迭代对象,值是唯一的,可以存储任何类型。

    const set = new Set([7, 7, '7', {}])
    
    // 添加元素
    set.add(4396).add(4396) // => {7, '7', {}, 4396}
    // 检测是否存在
    set.has(7) // => true
    // 删除指定元素
    set.delete(4396) // => true
    // 获取数量
    set.size // => 3
    // 清空所有元素
    set.clear() // => undefined
    // forEach遍历(value = key)
    set.forEach((value, key) => console.log(value, key)) // => 依次打印:7 7 -> '7' '7' -> {} {}
    // keys/values/entries 返回可迭代对象(value = key)
    for (const key of set.keys()) console.log(key) // => 依次打印:7 -> '7' -> {}
    for (const value of set.values()) console.log(value) // => 依次打印:7 -> '7' -> {}
    for (const [key, value] of set.entries()) console.log(key, value) // => 依次打印:7 7 -> '7' '7' -> {} {}
    // 获取第一个元素
    set.keys().next().value // => 7
    set.values().next().value // => 7
    set.entries().next().value // => [7, 7]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • 转为数组。

    Array.from(new Set([7, 7, '7'])) // => [7, '7']
    [...new Set([7, 7, '7'])] // => [7, '7']
    
    1
    2
  • 集合运算。

    const jack = new Set(['love', 'ashui'])
    const clear = new Set(['love', 'cangzhang'])
    
    // 交集
    new Set([...jack].filter(i => clear.has(i))) // => {'love'}
    // 并集
    new Set([...jack, ...clear]) // => {'love', 'ashui', 'cangzhang'}
    // 差集
    new Set([...jack].filter(i => !clear.has(i))) // => {'ashui'}
    // 对称差集
    new Set(
      [...new Set([...jack, ...clear])].filter(
        u => !new Set([...jack].filter(i => clear.has(i))).has(u)
      )
    ) // => {'ashui', 'cangzhang'}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • WeakSet 与 Set 类似,但只能存储引用数据类型,为弱引用类型,无遍历操作。

    可以用来存储 Dom 节点,当 Dom 节点被删除及无额外引用变量时会自动回收,提升性能。

    // ['jack'] 会被垃圾回收机制回收,届时 weakSet 上不会有 ['jack']
    const weakSet = new WeakSet([['jack']])
    const pony = /pony/
    
    // 添加元素
    weakSet.add(pony).add(pony) // => {['jack'], /pony/}
    // 检测是否存在
    weakSet.has(pony) // => true
    // 删除指定元素
    weakSet.delete(pony) // => true
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 10. Map & WeakMap

  • Map 与普通对象类似,传入可迭代对象,但可以使用任何类型作为键,并且结构稳定(能够记住所插入的顺序)。

    const foo = {
      name: '徐扶墙',
      age: 17,
    }
    const harem = {}
    
    const map = new Map(Object.entries(foo)) // => {'name' => '徐扶墙', 'age' => 17}
    // 添加键
    map.set(harem, '姜姒').set(harem, '裴南苇') // => {'name' => '徐扶墙', 'age' => 17, {} => '裴南苇'}
    // 读取值
    map.get(harem) // => '裴南苇'
    // 检测是否存在
    map.has(harem) // => true
    // 删除指定元素
    map.delete(harem) // => true
    // 获取数量
    map.size // => 2
    // 清空所有元素
    map.clear() // => undefined
    // forEach遍历
    map.forEach((value, key) => console.log(value, key)) // => 依次打印:徐扶墙 name -> 17 'age'
    // keys/values/entries 返回可迭代对象
    for (const key of map.keys()) console.log(key) // => 依次打印:'name' -> 'age'
    for (const value of map.values()) console.log(value) // => 依次打印:'徐扶墙' -> 17
    for (const [key, value] of map.entries()) console.log(key, value) // => 依次打印:'name' '徐扶墙' -> 'age' 17
    // 获取第一个元素
    map.keys().next().value // => 'name'
    map.values().next().value // => '徐扶墙'
    map.entries().next().value // => ['name', '徐扶墙']
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
  • WeakMap 与 Map 类似,但键只能是引用数据类型,为弱引用类型,无遍历操作。

    1. 可以用来保存 Dom 节点,当 Dom 节点被删除及无额外引用变量时会自动回收,提升性能。
    2. 可实现私有变量,当实例销毁后回收私有变量(有点鸡肋了)。
    // ['jack'] 会被垃圾回收机制回收,届时 weakMap 上不会有 ['jack']
    const weakMap = new WeakMap([[['jack'], 'jack']])
    const pony = /pony/
    
    // 添加键
    weakMap.set(pony, 'pony') // => {['jack'] => 'jack', /pony/ => 'pony'}
    // 读取值
    weakMap.get(pony, 'pony') // => 'pony'
    // 检测是否存在
    weakMap.has(pony) // => true
    // 删除指定元素
    weakMap.delete(pony) // => true
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 11. WeakRef

  • 保留对一个对象的弱引用。

    可以用来保存 Dom 节点,当 Dom 节点被删除及无额外引用变量时会自动回收,提升性能。

// /LeSe/ 会被垃圾回收机制回收,届时从 weakRef 取值为 undefined
const weakRef = new WeakRef(/LeSe/)

// 读取值
weakRef.deref() // => /LeSe/
1
2
3
4
5

# 12. ArrayBuffer

  • 用来表示通用的、固定长度的原始二进制数据缓冲区。不能直接操作,可以通过 TypedArray 或 DataView 对象操作二进制数据,可用于合成/生成 Blob 或 File 对象。

    new Uint8Array(3) // => [0, 0, 0, buffer: ArrayBuffer(3), byteLength: 3, byteOffset: 0, length: 3]
    
    // 同 Array.form()
    Uint8Array.from([1, 2, 3]) // => [1, 2, 3, buffer: ArrayBuffer(3), byteLength: 3, byteOffset: 0, length: 3]
    
    // 同 Array.of()
    Uint8Array.of(1, 2, 3) // => [1, 2, 3, buffer: ArrayBuffer(3), byteLength: 3, byteOffset: 0, length: 3]
    
    1
    2
    3
    4
    5
    6
    7
  • Blob/File 转 ArrayBuffer。

    function blob2Buffer(blob) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsArrayBuffer(blob)
        reader.onload = () => resolve(reader.result)
        reader.onerror = error => reject(error)
      })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

# 13. Blob

  • Binary Large Object 的缩写,表示一个不可变、原始数据的类文件对象。

    blob.slice() 方法可用于文件切片上传功能。

    new Blob(['clearlove7'], { type: 'text/plain' }) // => {size: 10, type: 'text/plain'}
    
    // File 转 Blob
    new Blob([file], { type: file.type })
    // ArrayBuffer 转 Blob
    new Blob([buffer], { type: 'application/json' })
    
    1
    2
    3
    4
    5
    6
  • Base64 转 Blob。

    function dataUrl2Blob(dataUrl) {
      let ary = dataUrl.split(','),
        mime = ary[0].match(/:(.*?);/)[1],
        base64 = atob(ary[1]),
        n = base64.length,
        u8ary = new Uint8Array(n)
      while (n--) u8ary[n] = base64.charCodeAt(n)
      return new Blob([u8ary], { type: mime })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 下载 Blob。

    const blob = new Blob([JSON.stringify({ name: 'Neo' }, null, 2)], {
      type: 'application/json',
    })
    
    download(blob)
    
    function download(blob) {
      const url = window.URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = 'Matrix.json'
      a.click()
      window.URL.revokeObjectURL(url)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# 14. File

  • 基于 Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。

    new File(['clearlove7'], 'EDG.txt', { type: 'text/plain' }) // => {name: 'EDG.txt', lastModified: 1635493540169, lastModifiedDate: Fri Oct 29 2021 15:45:40 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 10, type: "text/plain", webkitRelativePath: ""}
    
    // Blob 转 File
    new File([blob], 'Matrix.json', { type: blob.type })
    // ArrayBuffer 转 File
    new File([buffer], 'Matrix.json', { type: 'application/json' })
    
    1
    2
    3
    4
    5
    6
  • Base64 转 File。

    function dataUrl2File(dataUrl) {
      let ary = dataUrl.split(','),
        mime = ary[0].match(/:(.*?);/)[1],
        base64 = atob(ary[1]),
        n = base64.length,
        u8ary = new Uint8Array(n)
      while (n--) u8ary[n] = base64.charCodeAt(n)
      return new File([u8ary], 'Matrix.png', { type: mime })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 下载 File。

    const file = new File(
      [JSON.stringify({ name: 'Neo' }, null, 2)],
      'Matrix.json',
      {
        type: 'application/json',
      }
    )
    
    download(file)
    
    function download(file) {
      const url = window.URL.createObjectURL(file)
      const a = document.createElement('a')
      a.href = url
      a.download = file.name
      a.click()
      window.URL.revokeObjectURL(url)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • 文件切片。

    function section(file) {
      return new Promise((resolve, reject) => {
        let fileName = file.name
            .split('.')
            .slice(0, -1)
            .join('.'),
          ext = file.name.split('.').slice(-1)[0],
          chunkSize = 1024 * 1024 * 10,
          count = Math.ceil(file.size / chunkSize),
          n = 0,
          chunks = []
        const reader = new FileReader(file)
        reader.readAsArrayBuffer(file)
        reader.onload = _ => {
          const hashName = jsMd5(reader.result)
          while (n < count) {
            chunks.push({
              file: file.slice(n * chunkSize, (n + 1) * chunkSize),
              name: `${hashName}_${fileName}_${n + 1}.${ext}`,
            })
            n++
          }
          resolve({s
            name: `${hashName}_${file.name}`,
            size: file.size,
            count,
            chunks,
          })
        }
        reader.onerror = _ => reject()
      })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

# 15. FileReader

  • 可以异步读取文件。

    • FileReader.abort():中断读取。
    • FileReader.readAsArrayBuffer():result 属性返回 ArrayBuffer 数据对象,可用来修改数据生成新文件。
    • FileReader.readAsDataURL():result 属性返回 Base64 格式数据,可用来图片转 Base64 格式。
    • FileReader.readAsText():result 属性返回字符串表示的文本内容,可用来读取 .html.css.js.txt.md 等 Text 类型文件。
// 图片转 Base64
function picture2DataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => resolve(reader.result)
    reader.onerror = error => reject(error)
  })
}
1
2
3
4
5
6
7
8
9

# 16. FormData

  • 通过 key/value 格式存储数据(可迭代),一般用来向后端传输文件。
const formData = new FormData()

// 添加元素
formData.append('name', '徐扶墙') // => undefined
formData.append('age', 17) // => undefined
formData.append(
  'file',
  new File(['clearlove7'], 'EDG.txt', { type: 'text/plain' }),
  '给后端的文件名称'
) // => undefined
// 若属性存在,set 会覆盖该属性的值,append 则会追加一个值
formData.set('name', '徐扶墙')
// 检测是否存在
formData.has('name') // => true
// 获取元素
formData.get('name') // => '徐扶墙'
// 获取指定键的所有值
formData.getAll('name') // => ['徐扶墙']
// 删除指定元素
formData.delete('name') // => undefined
// 迭代 formData
for (const [key, value] of formData) console.log(key, value) // => 依次打印:'age' '17' -> 'file' {File对象}
// keys/values/entries 返回可迭代对象
for (const key of formData.keys()) console.log(key) // => 依次打印:'age' -> 'file'
for (const value of formData.values()) console.log(value) // => 依次打印:'17' -> File对象
for (const [key, value] of formData.entries()) console.log(key, value) // => 依次打印:'age' '17' -> 'file' {File对象}
// 获取第一个元素
formData.keys().next().value // => 'age'
formData.values().next().value // => '17'
formData.entries().next().value // => ['age', '17']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
以父之名
周杰伦.mp3