手搓一个满意的parseInt

60 阅读2分钟
  1. parseInt(string,radix)

用于将字符串转化为十进制数字,相比于Number()和一元+号来说,parseInt不是全量转化,举个例子:字符串'12px',用parseInt就是12,后两者是NaN

  1. 注意事项
    1. string如果是非字符串,会转化为字符串
    2. radix表示第一个参数的进制,具有自动推断能力,0x或0X开头的字符串 -> 16,其余默认是10,需要考虑进制范围:0,[2,32],否则返回NaN
    3. 自动过滤开头字符串
    4. 需要考虑正负号
    5. 第一个字符不能转数字直接返回NaN
  1. 代码实现
const myParseInt = (str, radix) => {
  // 1. 校验第一个参数为字符串类型
  if (typeof str !== 'string') str = str.toString() // 不考虑对象实现[Symbol.toPrimitive]
  // 2. 过滤开头空格
  str = str.trim()
  // 3. 考虑正负号
  let isPositive = true
  if (str.startsWith('+') || str.startsWith('-')){
    isPositive = str[0] === '+'
    str = str.slice(1)
  }
  // 4. 自动推断:识别16进制
  if (str.startsWith('0x') || str.startsWith('0X')){
    str = str.slice(2)
    if (!radix) {
      radix = 16
    } else if (radix !== 16){
      return (radix >= 2 && radix <= 32) ? 0 : NaN
    }
  }
  // 5. 校验第二个参数:范围、类型
  if (!radix) radix = 10 // 0、undefined -> 10
  if ((radix < 2 || radix > 32) || (typeof radix !== 'number')) return NaN
  // 6. 任意进制 -> 10进制:利用ASCII码处理字母->数字
  let res = 0
  let isFirst = true // 校验首字符是否能转数字
  for (let char of str) {
    let code = char.charCodeAt(0)
    if (char >= 'A' && char <= 'Z') {
      code = code - 'A'.charCodeAt(0) + 10
    } else if (char >= 'a' && char <= 'z') {
      code = code - 'a'.charCodeAt(0) + 10
    } else if (char >= '0' && char <= '9') {
      code = code - '0'.charCodeAt(0)
    } else {
      // 不属于合法字符
      if (isFirst) {
        return NaN
      } else {
        break
      }
    }
    // 校验进制
    if (isFirst) {
      isFirst = false
      if (code >= radix) {
        return NaN
      }
    } else {
      if (code >= radix) {
        break
      }
    }
    res = res * radix + code
  }
  return isPositive ? res : -res
}
  1. 检验mdn测试用例:没问题!
console.log(myParseInt('123'));
// 123 (default base-10)
console.log(myParseInt('123', 10));
// 123 (explicitly specify base-10)
console.log(myParseInt('   123 '));
// 123 (whitespace is ignored)
console.log(myParseInt('077'));
// 77 (leading zeros are ignored)
console.log(myParseInt('1.9'));
// 1 (decimal part is truncated)
console.log(myParseInt('ff', 16));
// 255 (lower-case hexadecimal)
console.log(myParseInt('0xFF', 16));
// 255 (upper-case hexadecimal with "0x" prefix)
console.log(myParseInt('xyz'));
// NaN (input can't be converted to an integer)
  1. 总结
    1. 第一大难点是两个参数的校验问题,需要考虑的边界case有点多,但理解了其实这些case都是可以考虑到的
    2. 第二大难点也是核心代码就是任意进制转10进制:其本质就是利用ASCII码表可以将字母转化成数字,再经过简单计算转化成与字母对应的10进制数字
    3. 上述代码基本上95%的测试用例都跟parseInt相符,剩下的5%我还没找出来但归根结底应付面试我觉得足矣,况且我面试估计也写不出来,呜呜呜