7

JS字符串补全方法padStart()和padEnd()简介

 3 years ago
source link: https://www.zhangxinxu.com/wordpress/2018/07/js-padstart-padend/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=7826
本文可全文转载,但需要保留原作者和出处。

一、关于字符串补全

字符串补全文章缩略图

在JS中,字符串补全是常用操作,用的比较多的就是时间或者日期前面的补0

例如,日期,我们多采用4-2-2的表示形式,例如:

2018-07-23

当我们使用时间戳进行月份获取的时候,是没有前面的0的,例如:

var month = new Date().getMonth() + 1;    // 结果是7

运行结果示意

此时,就需要进行补全,通常做法是这样:

if (month < 10) {
    month = '0' + month;
}

甚至会专门定义一个补'0'方法,例如此日期转时间戳微码中的自定义的zero方法。

补全方法代码示意

然而,随着JS字符串补全方法padStart()padEnd()的出现,类似场景使用就简单多了!

二、关于padStart

padStart可以在字符串的开头进行字符补全。

语法如下:

str.padStart(targetLength [, padString])

targetLength (可选) targetLength指目标字符串长度。

然后,根据我的测试,targetLength参数缺省也不会报错,原本的字符串原封不动返回,不过代码没有任何意义,因此,基本上没有使用的理由。

还有,targetLength参数的类型可以是数值类型或者弱数值类型。在JavaScript中,1 == '1'1是数值,'1'虽然本质上是字符串,但也可以看成是弱数值。在padStart()方法中,数值类型或者弱数值类型都是可以。例如:

'zhangxinxu'.padStart('5');

因此,我们实际使用的时候,没必要对targetLength参数进行强制的类型转换。

最后,如果targetLength设置的长度比字符串本身还要小,则原本的字符串原封不动返回,例如:

'zhangxinxu'.padStart(5);    
// 结果还是'zhangxinxu'

padString (可选) padString表示用来补全长度的字符串。然而,虽然语义上是字符串,但是根据我的测试,任意类型的值都是可以的。无论是Chrome浏览器还是Firefox浏览器,都会尝试将这个参数转换成字符串进行补全。例如下面几个例子:

'zhangxinxu'.padStart(15, false);
// 结果是'falsezhangxinxu'
'zhangxinxu'.padStart(15, null);
// 结果是'nullnzhangxinxu'
'zhangxinxu'.padStart(15, []);
// 结果是'zhangxinxu',因为[]转换成字符串是空字符串
'zhangxinxu'.padStart(15, {});
// 结果是'[objezhangxinxu',只显示了'[object Object]'前5个字符

padString参数默认值是普通空格' '(U+0020),也就是敲Space空格键出现的空格。

从上面几个案例可以看出,如果补全字符串长度不足,则不断循环补全;如果长度超出,则从左侧开始依次补全,没有补到的字符串直接就忽略。

此方法返回值是补全后的字符串。

回到一开始的日期补'0'功能,有了padStart()方法,我们代码可以简化成下面这一行:

var month = String(new Date().getMonth() + 1).padStart(2, '0');    // 结果是'07'

兼容性

IE14以其已下浏览器都不支持,考虑到现在还是windows 7天下,PC端对外项目还不能直接用;移动端,UC浏览器,QQ浏览器也不支持。但是,也不是不能使用,加一个Polyfill就好了,这个后面会展示。

三、关于padEnd

padEnd可以在字符串的后面进行字符补全,语法参数等都和padStart类似。

str.padEnd(targetLength [, padString])

targetLength (可选) targetLength指补全后字符串的长度。

然后,根据我的测试,targetLength参数如果不设置,不会报错,直接返回原始字符串,不过这样代码就没有任何意义,因此,实际开发,此参数肯定是需要设置的。

同样的,targetLength参数的类型可以是数值类型或者弱数值类型。例如下面2个用法都是可以的:

'zhangxinxu'.padEnd('15');
'zhangxinxu'.padEnd(15);

因此,我们实际写代码的时候,没必要强制targetLength参数为数值。

最后,如果targetLength设置的长度比字符串本身还要小,则原字符串原封不动返回,例如:

'zhangxinxu'.padEnd(5);    
// 结果还是'zhangxinxu'

如果targetLength是一些乱七八糟的数值类型,也是返回原始字符串。例如:

'zhangxinxu'.padEnd(false);    
// 结果还是'zhangxinxu'

但是有意义吗?没意义,所以不要这么用。

padString (可选) padString表示用来补全长度的字符串。虽然语义上是字符串,实际上这个参数可以是各种类型。例如下面几个例子:
'zhangxinxu'.padEnd(15, false);
// 结果是'zhangxinxufalse'
'zhangxinxu'.padEnd(15, null);
// 结果是'zhangxinxunulln'
'zhangxinxu'.padEnd(15, []);
// 结果是'zhangxinxu',因为[]转换成字符串是空字符串
'zhangxinxu'.padEnd(15, {});
// 结果是'zhangxinxu[obje',只显示了'[object Object]'前5个字符

从上面几个案例可以看出,如果补全字符串长度不足,则从左往右不断循环补全;如果长度超出可以补全的字符长度,则从左侧尽可能补全,补不到的没办法,只能忽略,例如'zhangxinxu'.padEnd(15, {})等同于执行'zhangxinxu'.padEnd(15, '[object Object]'),最多只能补5个字符,因此,只能补'[object Object]'前5个字符,于是最后结果是:'zhangxinxu[obje'

padString参数如果不设置,则会使用普通空格' '(U+0020)代替,也就是Space空格键敲出来的那个空格。

举一个在后面补全字符串案例
在JS前端我们处理时间戳的时候单位都是ms毫秒,但是,后端同学返回的时间戳则不一样是毫秒,可能只有10位,以s秒为单位。所以,我们在前端处理这个时间戳的时候,保险起见,要先做一个13位的补全,保证单位是毫秒。使用示意:

timestamp = +String(timestamp).padEnd(13, '0');

兼容性

本字符串API属于ES6新增方法,IE14以其已下浏览器都不支持,部分国产移动端浏览器也不支持。目前对外项目使用还需要附上Polyfill代码。

四、Polyfill代码

以下Polyfill代码取自polyfill项目中的string.polyfill.js,其中使用依赖的repeat()也是ES6新增方法,因此,完成Polyfill代码如下,注释部分我做了简单的翻译,代码部分简化了些许逻辑,同时,修复了一个bug,下面代码红色高亮部分就是修复内容:

// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// repeat()方法的polyfill
if (!String.prototype.repeat) {
    String.prototype.repeat = function (count) {
        'use strict';
        if (this == null) {
            throw new TypeError('can\'t convert ' + this + ' to object');
        }
        var str = '' + this;
        count = +count;
        if (count != count) {
            count = 0;
        }
        if (count < 0) {
            throw new RangeError('repeat count must be non-negative');
        }
        if (count == Infinity) {
            throw new RangeError('repeat count must be less than infinity');
        }
        count = Math.floor(count);
        if (str.length == 0 || count == 0) {
            return '';
        }
        if (str.length * count >= 1 << 28) {
            throw new RangeError('repeat count must not overflow maximum string size');
        }
        var rpt = '';
        for (; ;) {
            if ((count & 1) == 1) {
                rpt += str;
            }
            count >>>= 1;
            if (count == 0) {
                break;
            }
            str += str;
        }
        return rpt;
    }
}
// padStart()方法的polyfill
if (!String.prototype.padStart) {
    String.prototype.padStart = function (targetLength, padString) {
        // 截断数字或将非数字转换为0
        targetLength = targetLength>>0; 
        padString = String((typeof padString !== 'undefined' ? padString : ' '));
        if (this.length > targetLength || padString === '') {
            return String(this);
        }
        targetLength = targetLength-this.length;
        if (targetLength > padString.length) {
            // 添加到初始值以确保长度足够
            padString += padString.repeat(targetLength / padString.length); 
        }
        return padString.slice(0, targetLength) + String(this);
    };
}
// padEnd()方法的polyfill
if (!String.prototype.padEnd) {
    String.prototype.padEnd = function (targetLength, padString) {
        // 转数值或者非数值转换成0
        targetLength = targetLength >> 0; 
        padString = String((typeof padString !== 'undefined' ? padString : ' '));
        if (this.length > targetLength || padString === '') {
            return String(this);
        }
        targetLength = targetLength - this.length;
        if (targetLength > padString.length) {
            // 添加到初始值以确保长度足够
            padString += padString.repeat(targetLength / padString.length);
        }
        return String(this) + padString.slice(0, targetLength);
    };
}

以上polyfill代码需要放在调用padStart()/padEnd()方法的代码的前面,只要在合适的位置就这么一粘贴,然后,无论什么版本浏览器浏览器,哪怕IE6,IE8,我们也可以放心使用padStart()或者padEnd()方法了。

polyfill代码下的demo案例

您可以狠狠地点击这里:padStart和padEnd方法polyfill测试demo

原polyfill方法的一个bug就是通过这个测试demo测出来的,下面是修正后的polyfill代码在IE9浏览器的测试结果截图:

测试结果截图

标题虽然是简介,但是本文内容实际上已经非常深入细节了。padStart()padEnd()两个方法参数容错性非常强,非常有JS的特色,我很喜欢。但是,也带来另外的问题,就是,如果我们的参数值因为各种原因,最后并不是我们期望的参数值,则很可能会产生非常隐蔽的bug,因为不会报错啊,运行还是正常的。所以,容错性强,也算是有利有弊。

ES6对字符串还扩展了很多其他方法,除了本文提到的repeat()方法,还有诸如normalize()trimStart()trimEnd()等API方法。这些之后再一个一个深入细节。

话说,我现在才开始深入学习ES6的基础知识应该还不算晚吧,哈哈!

大笑

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK