Skip to content

Instantly share code, notes, and snippets.

@think49
Last active September 13, 2017 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save think49/d0e01c82c12bda2d27d8 to your computer and use it in GitHub Desktop.
Save think49/d0e01c82c12bda2d27d8 to your computer and use it in GitHub Desktop.
String.prototype.padStart, String.prototype.padEnd の Polyfill (ECMAScript 2017 / ECMA-262 8th edition)

string-prototype-padstart+padend.js

概要

ECMAScript 2017 (ECMA-262 8th edition) 既定の String.prototype.padStart 及び String.prototype.padEnd の Polyfill(互換コード)を提供します。

使い方

String.prototype.padStart( maxLength [ , fillString ] )

String.prototype.padStart は第一引数で「文字列の全長(maxLength)」を指定し、第二引数で「前方に埋める文字列(fillString)」を指定します。 第一引数で指定した「文字列の長さ(maxLength)」になるまで、文字列の前方から第二引数で指定した「前方に埋める文字列(fillString)」を繰り返し指定する事で埋めていきます。 良く使われる用途としては、ゼロパディングがあります。

console.log('9'.padStart(3, '0')); // "009"

第二引数が省略された場合は、第二引数に半角スペース(0x0020)が指定された扱いとなります。

console.log('a'.padStart(3) === '  a'); // true

第一引数で指定された文字列の長さが2文字以上であり、「(第一引数で指定された文字列長 - 現在の文字列長) / 第二引数の文字列長」が割り切れなかった場合、後方が見切れる形で前方が埋まります。

console.log('abc'.padStart(10, '<e>')); // "<e><e><abc"

String.prototype.padEnd( maxLength [ , fillString ] )

String.prototype.padEnd は第一引数で「文字列の全長(maxLength)」を指定し、第二引数で「後方に埋める文字列(fillString)」を指定します。 第一引数で指定した「文字列の長さ(maxLength)」になるまで、文字列の後方から第二引数で指定した「後方に埋める文字列(fillString)」を繰り返し指定する事で埋めていきます。

console.log('9'.padEnd(3, '0')); // "900"

第二引数が省略された場合は、第二引数に半角スペース(0x0020)が指定された扱いとなります。

console.log('a'.padEnd(3) === 'a  '); // true

第一引数で指定された文字列の長さが2文字以上であり、「(第一引数で指定された文字列長 - 現在の文字列長) / 第二引数の文字列長」が割り切れなかった場合、後方が見切れる形で前方が埋まります。

console.log('abc'.padEnd(10, '<e>')); // "abc<e><e><"

制約事項

IE8以下のブラウザでは、Object.defineProperty が DOM オブジェクトにしか適用できない仕様の為、IE8 以下の場合のみ、関数オブジェクト代入のコードで各メソッドを定義しています。

String.prototype.padStart = function padStart () {};
String.prototype.padEnd = function padEnd () {};

それぞれ列挙可能(enumerable: false)なメソッドとして定義されている為、for-in で列挙されてしまう問題があります。

for (var key in 'foo') {
  console.log(key); // ここで列挙されてしまう
}

通常、文字列に対して for-in でプロパティを列挙する事はない為、ほぼ問題ないと思われますが、コードを書く際には Object.keys を使ったり、Object.prototype.hasOwnProperty を併用する事で対策する必要があります。 (※なお、IE9 以降では、Object.defineProperties で列挙不可能にしている為、この問題はありません。)

動作環境

ES3 (ECMA-262 3th edition) を実装しているブラウザであれば、動作します。 IE11 のエミュレーションモードでは、IE5 まで期待通りに動作しました。

参考リンク

/**
* string-prototype-padstart+padend-1.0.0.js
* String.prototype.padStart, String.prototype.padEnd Polyfill (ES8 / ES2017)
*
* @version 1.0.0
* @author think49
* @url https://gist.github.com/think49/d0e01c82c12bda2d27d8
* @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
*/
(function () {
'use strict';
if (typeof this.padStart === 'function' && typeof this.padEnd === 'function') {
return;
}
var toLength = (function (Number, floor, abs, min) {
return function toLength (length) {
length = Number(length);
if (length !== length) {
return 0;
}
length = floor(abs(length));
if (length <= 0) {
return 0;
}
return min(length, 9007199254740991);
};
}(Number, Math.floor, Math.abs, Math.min));
if (typeof this.padStart !== 'function') {
var padStart = (function (String, Array) {
return function padStart (maxLength /* [, fillString] */) {
var string, diffLength, fillString, fillLength, remainder;
if (this === null || typeof this === 'undefined') {
throw new TypeError('String.prototype.padStart called on null or undefined');
}
string = String(this);
diffLength = toLength(maxLength) - string.length;
if (diffLength < 1) {
return string;
}
fillString = arguments[1];
fillString = typeof fillString === 'undefined' ? ' ' : String(fillString);
if (!fillString) { // If fillString is the empty String
return string;
}
fillLength = fillString.length;
remainder = diffLength % fillLength;
return Array((diffLength - remainder) / fillLength + 1).join(fillString) + fillString.slice(0, remainder) + string;
};
}(String, Array));
if (typeof Object.defineProperties === 'function') {
Object.defineProperties(this, {'padStart': {writable: true, enumerable: false, configurable: true, value: padStart}});
} else {
this.padStart = padStart;
}
}
if (typeof this.padEnd !== 'function') {
var padEnd = (function (String, Array) {
return function padEnd (maxLength /* [, fillString] */) {
var string, diffLength, fillString, fillLength, remainder;
if (this === null || typeof this === 'undefined') {
throw new TypeError('String.prototype.padEnd called on null or undefined');
}
string = String(this);
diffLength = toLength(maxLength) - string.length;
if (diffLength < 1) {
return string;
}
fillString = arguments[1];
fillString = typeof fillString === 'undefined' ? ' ' : String(fillString);
if (!fillString) { // If fillString is the empty String
return string;
}
fillLength = fillString.length;
remainder = diffLength % fillLength;
return string + Array((diffLength - remainder) / fillLength + 1).join(fillString) + fillString.slice(0, remainder);
};
}(String, Array));
if (typeof Object.defineProperties === 'function') {
Object.defineProperties(this, {'padEnd': {writable: true, enumerable: false, configurable: true, value: padEnd}});
} else {
this.padEnd = padEnd;
}
}
}.call(String.prototype));
<!DOCTYPE html>
<head>
<title>test</title>
<style>
</style>
</head>
<body>
<script src="string-prototype-padstart+padend-1.0.0.js"></script>
<script>
'use strict';
console.assert('abcd'.padStart(3, 0) === 'abcd');
console.assert('abcd'.padStart(4, 0) === 'abcd');
console.assert('abcd'.padStart(10, '0123456789') === '012345abcd');
console.assert('1'.padStart(3, 0) === '001');
console.assert('1'.padStart(10, 'ab') === 'ababababa1');
console.assert('abcd'.padEnd(3, 0) === 'abcd');
console.assert('abcd'.padEnd(4, 0) === 'abcd');
console.assert('abcd'.padEnd(10, '0123456789') === 'abcd012345');
console.assert('1'.padEnd(3, 0) === '100');
console.assert('1'.padEnd(10, 'ab') === '1ababababa');
</script>
<script>
'use strict';
String.prototype.padStart.call(null); // TypeError: String.prototype.padStart called on null or undefined
</script>
<script>
'use strict';
String.prototype.padStart.call(); // TypeError: String.prototype.padStart called on null or undefined
</script>
<script>
'use strict';
String.prototype.padEnd.call(null); // TypeError: String.prototype.padEnd called on null or undefined
</script>
<script>
'use strict';
String.prototype.padEnd.call(); // TypeError: String.prototype.padEnd called on null or undefined
</script>
<script>
'use strict';
Object.defineProperties = null;
String.prototype.padStart = null;
String.prototype.padEnd = null;
</script>
<script src="string-prototype-padstart+padend-1.0.0.js"></script>
<script>
'use strict';
console.assert('abcd'.padStart(3, 0) === 'abcd');
console.assert('abcd'.padStart(4, 0) === 'abcd');
console.assert('abcd'.padStart(10, '0123456789') === '012345abcd');
console.assert('1'.padStart(3, 0) === '001');
console.assert('1'.padStart(10, 'ab') === 'ababababa1');
console.assert('abcd'.padEnd(3, 0) === 'abcd');
console.assert('abcd'.padEnd(4, 0) === 'abcd');
console.assert('abcd'.padEnd(10, '0123456789') === 'abcd012345');
console.assert('1'.padEnd(3, 0) === '100');
console.assert('1'.padEnd(10, 'ab') === '1ababababa');
</script>
<script>
'use strict';
String.prototype.padStart.call(null); // TypeError: String.prototype.padStart called on null or undefined
</script>
<script>
'use strict';
String.prototype.padStart.call(); // TypeError: String.prototype.padStart called on null or undefined
</script>
<script>
'use strict';
String.prototype.padEnd.call(null); // TypeError: String.prototype.padEnd called on null or undefined
</script>
<script>
'use strict';
String.prototype.padEnd.call(); // TypeError: String.prototype.padEnd called on null or undefined
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment