这是我参与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, 主要是排查undefined和null:
| Argument Type | Result |
|---|---|
| Undefined | Throw a TypeError exception. |
| Null | Throw a TypeError exception. |
| Boolean | Return argument. |
| Number | Return argument. |
| String | Return argument. |
| Symbol | Return argument. |
| BigInt | Return argument. |
| Object | Return 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**28 到 2**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也是会出错的。
小结
今天你收获了吗?