# String extension

[JS](/web/js.md) ⟩ [value](/web/js/val.md) ⟩ [primitive](/web/js/val/prim.md) ⟩ [String](/web/js/val/prim/str.md) ⟩ extension

{% hint style="success" %}
add custom methods to String.prototype.

```javascript
// 🔸 str.characters                - iterable iterator of characters
// 🔸 str.codePoints                - array of code points
// 🔸 str.codeUnits                 - array of code units
// 🔹 str.replaceWhitespaces()
```

{% endhint %}

{% tabs %}
{% tab title="💾 程式" %}

* replit ⟩ [String extension](https://replit.com/@pegasusroe/String-extension#ext/String_ext.js)

```javascript
// ⭐ install "grapheme-splitter": `npm install grapheme-splitter`

// 2023.01.03 - 19:40 + .codeUnits, .codePoints (support `GraphemeSplitter`)
//
// ⭐  String extension
// -------------------------------------------------------------------
// 🔸 str.characters                - iterable iterator of characters
// 🔸 str.codePoints                - array of code points
// 🔸 str.codeUnits                 - array of code units
// 🔹 str.replaceWhitespaces()
// -------------------------------------------------------------------

const { range } = require('./Iterable.js');      // iterable methods

// ⭐ import 'grapheme-splitter'
const GraphemeSplitter = require('grapheme-splitter');
const splitter = new GraphemeSplitter();
// -------------------------------------------------------------------

// String extension
Object.defineProperties(String.prototype, {

    // 🔹 str.replaceWhitespaces()
    replaceWhitespaces: {
        value: function(str = ',') {
            return this
                .trim()    // ⭐️ removes whitespace from both ends
                .replace(/\s+/g, str);
        },
    },

    // 🔸 str.characters
    // - real characters (grapheme clusters, may contain many code points)
    characters: {
        get: function() {
            // string -> iterable iterator
            return splitter.iterateGraphemes(this);
        },
    },

    // 🔸 str.codePoints
    // - 1 code point may contain 1 ~ 2 code units.
    codePoints: {
        get: function() {
            let codePoints = [];
            for (const char of this) {    // an iterator of "code points"
                codePoints.push(char.codePointAt(0));
            }
            return codePoints;
        },
    },

    // 🔸 str.codeUnits
    // - 16-bit code units
    codeUnits: {
        get: function() {
            return [...range(0, this.length - 1).map(i => this.charCodeAt(i))];
        },
    },
});

// export
module.exports = {};
```

{% endtab %}

{% tab title="💈範例" %}

```javascript
const _Number = require('./ext/Number_ext.js');      // Number extension
const _String = require('./ext/String_ext.js');      // String extension
const { range } = require('./ext/Iterable.js');      // iterable methods
// ---------------------------------------------------------------------------

// ⭐ import 'grapheme-splitter'
const GraphemeSplitter = require('grapheme-splitter');
const ilove = 'I❤️🏳️‍🌈';

// `.characters` iterates over "grapheme clusters".
ilove.characters.array,    // [ 'I', '❤️', '🏳️‍🌈' ]    (3 characters)
ilove.characters.map(ch => ch.codePoints).array,
// [ [ 73 ], [ 10084, 65039 ], [ 127987, 65039, 8205, 127752 ] ]    (7 code points)
//           [ '❤'  ,     ? ]  [ '🏳'  ,     ?,    ?, '🌈'  ]
//   ╰─I──╯  ╰───── ❤️ ─────╯  ╰──────────── 🏳️‍🌈 ─────────────╯

// `str.map` iterates over "code points"❗
// code point -> 1 or 2 code units
ilove.map(cp => cp.codeUnits).array,            // (9 code units)
// [
//   [ 73 ],
//   [ 10084 ],
//   [ 65039 ],
//   [ 55356, 57331 ],   // surrogate pair
//   [ 65039 ],
//   [ 8205 ],
//   [ 55356, 57096 ]    // surrogate pair
// ]
```

{% endtab %}

{% tab title="🔸 屬性" %}

* [str.isKeyword](/web/js/val/prim/str/ext/iskeyword.md) - check if a string is a [keyword](/web/js/grammar/token/keyword.md)
* [str.isReservedWord](/web/js/val/prim/str/ext/isreservedword.md) - check if a string a [reserved word](/web/js/grammar/token/keyword/reserved.md)
* [str.isPunctuator](/web/js/val/prim/str/ext/ispunctuator.md) - check if a string is a [punctuator](/web/js/grammar/token/punctuator.md)
  {% endtab %}

{% tab title="👥 相關" %}

* [Unicode](/web/js/val/prim/str/unicode.md) - about [code points](/web/js/val/prim/str/unicode/code-point.md), [code units](/web/js/val/prim/str/unicode/encode/utf16/code-unit.md) ...
  {% endtab %}

{% tab title="⬇️ 應用" %}

* [grapheme cluster](/web/js/val/prim/str/unicode/grapheme-cluster.md)
* [flag /u](/web/js/val/builtin/regex/flag/flag-u.md)
  {% endtab %}
  {% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/web/js/val/prim/str/ext.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
