字符串 repeat 下

333 阅读1分钟

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

前言

ECMAScript 6.0 简称ES6 , 是 JavaScript 语言的新一代的标准,于在 2015 年 6 月发布,正式名称就是《ECMAScript 2015 标准》。一般情况,泛指, 5.1版以后的标准,涵盖了 ES2015、ES2016、ES2017、ES2018、ES2019、ES2020、ES2021 等等

我们一起来学习String.prototype.repeat

上篇 字符串 repeat 上 我们已经详细说了repeat函数使用, 参数不同值下的执行情况。

我们今天的重点是手写repeat方法, 由浅入深。

手写repeat

当然要先看ES标准,我们打开标准 String.prototype.repeat ( count ) The following steps are taken:

1. Let O be ? RequireObjectCoercible(this value).
2. Let S be ? ToString(O).
3. Let n be ? ToIntegerOrInfinity(count).
4. If n < 0 or n is +∞, throw a RangeError exception.
5. If n is 0, return the empty String.
6. Return the String value that is made from n copies of S appended together.

第一步: RequireObjectCoercible是判断 this, 主要是排查undefinednull

Argument TypeResult
UndefinedThrow a TypeError exception.
NullThrow a TypeError exception.
BooleanReturn argument.
NumberReturn argument.
StringReturn argument.
SymbolReturn argument.
BigIntReturn argument.
ObjectReturn argument.

第二步: 就是转字符串
第三步: count专为数字
第四步: 判断n的范围
第五步: 单独处理0的情况
第六步: 重复字符串并返回。

我们按部就班的抒写:

String.prototype.repeat = function (count) {

    // 第一步:判断this
    if (this === null || this === undefined) {
        throw new TypeError("can't convert " + this + " to object");
    }

    // 第二步:转为字符串
    var str = this + '';

    // 如果为空,重复多少次都没意义
    if (str.length === 0) {
        return '';
    }

    // 第三步: count转为数字
    var count = +count;

    // 第四步: 处理小于0或者等于Infinity的情况
    if (count < 0) {
        throw new RangeError('count must be non-negative');
    }
    if (count == Infinity) {
        throw new RangeError('count must be less than infinity');
    }

    // 第五步: 处理等于0的情况
    if (count === 0) {
        return '';
    }

    // 第六步: 重复并发返回
    count = ~~count;
    var result = '';
    for (var i = 0; i < count; i++) {
        result += str
    }
    return result;
}

测试一下:

console.log('stt'.repeat(2)); // 'sttstt'
console.log('stt'.repeat(-1)); // RangeError: count must be non-negative

几个疑问和问题

count的最大值

我们在上篇中,已经验证过,其最大值在 2**282**29 之间,否则会报错。 这块我们应该怎么处理。

这个MDN是 String repeat 这么处理的:

if (str.length * count >= 1 << 28) {
      throw new RangeError('repeat count must not overflow maximum string size');
}

core-js 2021-11-22的版本是没有处理的。

module.exports = function repeat(count) {
  var str = toString(requireObjectCoercible(this));
  var result = '';
  var n = toIntegerOrInfinity(count);
  if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
  for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
  return result;
};

for循环重复

这种方式过于低效,有没有改进的方法。

MDN和core-js都采用了 >>>符号,这是啥呢,就是无符号位移, 相当于除以2。

8 >>> 1   // 4

除以2,最后怎么终止呢,当然是 n & 1 = 1的时候。

this为何要进行 null和undefined的判断

什么情况下会出现这种场景呢? 答案是使用call进行调用:

String.prototype.repeat.call(null, 2) 
// Uncaught TypeError: String.prototype.repeat called on null or undefined
String.prototype.repeat.call(Symbol.for("x"), 2)
// Uncaught TypeError: Cannot convert a Symbol value to a string

别看我们上面只判断了 null和undefined, 其实传入 Symbol也是会出错的。

小结

今天你收获了吗?