前端必刷手写题系列 [11]

347 阅读3分钟

这是我参与更文挑战的第 20 天,活动详情查看 更文挑战

这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。

平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态习惯思路清晰程度等。

注意是简单实现,不是完整实现,重要的是概念清晰实现思路清晰,建议先解释清除概念 => 写用例 => 写伪代码 => 再实现具体功能,再优化,一步步来。

20. 对象内存计算

是什么

问题很清楚就是给你一个对象,让你求出它所占用多大内存空间(字节),我们平时可以用 object-sizeof 这个库来计算

这个问题一般考察你的js内存基础知识和写代码能力(递归),和各种边界条件的判断。

JavaScript 中的字符采用 Unicode 来编码 ,也就是说,JavaScript 中的英文字符和中文字符都会占用 2个字节的空间大小。注意是单个字符。

  • string 类型内存空间是:单位长度 2字节(byte)
  • number 类型内存空间是:8字节
  • boolean 类型内存空间是:4字节

我们暂时不考虑 Symbol 等特殊类型

简单手写实现

实现

  1. 写个测试用例先
let testObj = {
  a: 1,
  kv: 'haha',
  123: true,
  d: [1, {f: 111}],
  e: {a: 1, d: '2'}
}

计算下

  • a: 1 => 键 'a': 2B,值 1: 8B (B 是 Byte 字节) 共 10B
  • kv: haha => 键 'kv': 4B,值 'haha': 8B (B 是 Byte 字节) 共 12B
  • 123: true => 6B + 4B = 12B 因为 123 虽然是整数,但对象的 key 会转成字符串 所以占 6B
  • d: [1, {f: 111}] => 2B + 8B + 2B + 8B = 20B
  • e: {a: 1, d: '2'} => 2B + 2B + 8B + 2B + 2B = 16B

最后总计: 10 + 12 + 10 + 20 + 16 = 68B

  1. 实现主逻辑

代码思路很清晰,从主函数开始,只要注意以下2点就行

  • 对于值为对象的情况,可能有重复引用的情况
  • 对象键和值,都需要占用内存

这个过程我觉得应该看注释非常清楚,无非细心而已,思路倒不复杂。

let testObj = {
  a: 1,
  kv: 'haha',
  123: true,
  d: [1, {f: 111}],
  e: {a: 1, d: '2'}
}

// 相同元素引用 Set、 或者用WeakSet
let usedReference = new Set()

// 计算数组的情况
function calcArrMemory(arr) {
  // Array 有可能是下面两种形式, 所以还是需要递归
  // [1, '2', true]
  // [{a: 1}, [1, 2]]
  // 使用 map / reduce 对每个元素实现计算后累计加和
  return arr.map(memoryCalc).reduce((acc, cur) => acc + cur, 0)
}

// 计算对象的情况
function calcArrObject(obj) {
  let res = 0;
  // 对象键和值,都需要占用内存
  let keys = Object.keys(obj)
  for (let i = 0; i < keys.length; i++) {
    let key = keys[i]
    // 先算 key 的空间
    res += memoryCalc(key)
    // 对于值为对象的情况,可能有重复引用的情况
    if (Object.prototype.toString.call(obj[key]) === '[object Object]') {
      if (usedReference.has(obj[key])) {
        continue;
      }
      // 用过的引用加入 Set
      usedReference.add(obj[key])
    }
    // 再算出 value 空间
    res += memoryCalc(obj[key])
  }
  return res
}

// 主函数,分情况讨论,遇到复杂情况递归计算
function memoryCalc(obj) {
  const type = Object.prototype.toString.call(obj)

  switch (type) {
    // string 单位长度`2`字节
    case '[object String]':
      return obj.length * 2;
    // number `8`字节
    case '[object Number]':
      return 8;
    // boolean `4`字节
    case '[object Boolean]':
      return 4;
    case '[object Null]':
      return 0;
    case '[object Undefined]':
      return 0;
    case '[object Array]':
      return calcArrMemory(obj);
    case '[object Object]':
      return calcArrObject(obj);
    default:
      return 0;
  }
}

console.log(memoryCalc(testObj))
// 68 和我们手算结果相同

另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考