实现 padStart() 和 padEnd() 的 Polyfill

手写实现字符串填充方法 padStart 和 padEnd 的 polyfill,用于在字符串开头或末尾填充指定字符

问题

padStart()padEnd() 是 ES2017 引入的字符串方法,用于在字符串的开头或末尾填充指定字符,直到字符串达到指定长度。在不支持这些方法的旧版浏览器中,我们需要实现 polyfill 来提供相同的功能。

这两个方法的作用:

  • padStart(targetLength, padString):从字符串开头填充
  • padEnd(targetLength, padString):从字符串末尾填充

解答

// padStart polyfill
if (!String.prototype.padStart) {
  String.prototype.padStart = function(targetLength, padString) {
    // 将目标长度转换为整数
    targetLength = targetLength >> 0;
    
    // 获取当前字符串
    let str = String(this);
    
    // 如果目标长度小于等于当前字符串长度,直接返回原字符串
    if (str.length >= targetLength) {
      return str;
    }
    
    // 设置默认填充字符为空格
    padString = String(typeof padString !== 'undefined' ? padString : ' ');
    
    // 如果填充字符为空字符串,直接返回原字符串
    if (padString === '') {
      return str;
    }
    
    // 计算需要填充的长度
    let padLength = targetLength - str.length;
    
    // 重复填充字符串,直到长度足够
    let repeatedPadString = '';
    while (padLength > padString.length) {
      repeatedPadString += padString;
      padLength -= padString.length;
    }
    
    // 添加剩余需要的填充字符
    repeatedPadString += padString.slice(0, padLength);
    
    // 返回填充后的字符串
    return repeatedPadString + str;
  };
}

// padEnd polyfill
if (!String.prototype.padEnd) {
  String.prototype.padEnd = function(targetLength, padString) {
    // 将目标长度转换为整数
    targetLength = targetLength >> 0;
    
    // 获取当前字符串
    let str = String(this);
    
    // 如果目标长度小于等于当前字符串长度,直接返回原字符串
    if (str.length >= targetLength) {
      return str;
    }
    
    // 设置默认填充字符为空格
    padString = String(typeof padString !== 'undefined' ? padString : ' ');
    
    // 如果填充字符为空字符串,直接返回原字符串
    if (padString === '') {
      return str;
    }
    
    // 计算需要填充的长度
    let padLength = targetLength - str.length;
    
    // 重复填充字符串,直到长度足够
    let repeatedPadString = '';
    while (padLength > padString.length) {
      repeatedPadString += padString;
      padLength -= padString.length;
    }
    
    // 添加剩余需要的填充字符
    repeatedPadString += padString.slice(0, padLength);
    
    // 返回填充后的字符串
    return str + repeatedPadString;
  };
}

使用示例

// padStart 示例
console.log('5'.padStart(3, '0'));           // "005"
console.log('hello'.padStart(10, '*'));      // "*****hello"
console.log('abc'.padStart(10, '123'));      // "1231231abc"
console.log('world'.padStart(3, '0'));       // "world" (长度已够,不填充)
console.log('test'.padStart(8));             // "    test" (默认用空格填充)

// padEnd 示例
console.log('5'.padEnd(3, '0'));             // "500"
console.log('hello'.padEnd(10, '*'));        // "hello*****"
console.log('abc'.padEnd(10, '123'));        // "abc1231231"
console.log('world'.padEnd(3, '0'));         // "world" (长度已够,不填充)
console.log('test'.padEnd(8));               // "test    " (默认用空格填充)

// 实际应用场景
// 1. 格式化数字(补零)
const num = 7;
console.log(num.toString().padStart(2, '0')); // "07"

// 2. 对齐文本
const items = ['Apple', 'Banana', 'Cherry'];
items.forEach(item => {
  console.log(item.padEnd(10, '.') + ' $5.00');
});
// Apple..... $5.00
// Banana.... $5.00
// Cherry.... $5.00

// 3. 格式化时间
const hour = '9';
const minute = '5';
console.log(`${hour.padStart(2, '0')}:${minute.padStart(2, '0')}`); // "09:05"

关键点

  • 类型转换:使用 String(this) 确保处理的是字符串类型,使用 targetLength >> 0 将目标长度转换为整数

  • 边界条件处理

    • 当目标长度小于等于原字符串长度时,直接返回原字符串
    • 当填充字符为空字符串时,直接返回原字符串
    • 当 padString 未定义时,默认使用空格填充
  • 填充字符串的构建:通过循环重复填充字符串,直到长度足够,最后使用 slice() 截取需要的部分

  • padStart 和 padEnd 的区别:仅在最后拼接字符串时的顺序不同

    • padStart: repeatedPadString + str
    • padEnd: str + repeatedPadString
  • 性能优化:使用循环累加而不是 repeat() 方法,因为 repeat() 本身也可能需要 polyfill

  • 兼容性检查:通过 if (!String.prototype.padStart) 判断,避免覆盖原生实现