codePointAt を使って基本的な文字列メソッドを定義する

EcmaScript 6 で `String.prototype.codePointAt()` が導入され、サロゲートペアを考慮した文字列関数を定義しやすくなった。[es6-shim](https://github.com/paulmillr/es6-shim/) が配布されているので、古いブラウザーでも利用可能である。io.js では `codePointAt` だけでなく `for..of` を使うことができる。使い方はこちらの[記事](http://qiita.com/masakielastic/items/72544a3448bb8e768f60)を参照。

*2015年9月4月追記*

文字列の長さ、部分文字列を求めるメソッドを利用できる npm の[パッケージ](https://github.com/masakielastic/str32.js)を公開しました。チュートリアルとはメソッドの名前は異なります。

## 文字数を求める

文字数に関しては、正規表現を使ったほうがコードがコンパクトになる。文字列の `length` プロパティからサロゲートペアの組を引けばよい。名前は ICU の UnicodeString#countChar32 から借りた。

```js
if (!String.prototype.countChar32) {
  String.prototype.countChar32 = function() {
    return this.length - (this.match(/[\uD800-\uDBFF][\uDC00-\uDFFFF]/g)||[]).length;
  };
}

var str = '\uD842\uDFB7野家';
console.log(3 === str.countChar32());
```

`codePointAt` から定義すると次のようになる。

```js
if (!String.prototype.countChar32) {
    String.prototype.countChar32 = function() {

        var pos = 0;
        var length = this.length;
        var cp = 0;
        var count = 0;
 
        while (pos < length) {
            cp = this.codePointAt(pos);
            pos += cp < 0x10000 ? 1 : 2;
            ++count;
        }

        return count;
    };
}

var str = '\uD842\uDFB7野家';
console.log(3 === str.countChar32());
```

## 指定したインデックスに対応する1文字取り出す

ICU の `UnicodeString#char32At` から名前を借りた。

```js
if (!String.prototype.char32At) {
    String.prototype.char32At = function(offset) {

        var pos = 0;
        var length = this.length;
        var cp = 0;
        var size = 0;
        var index = 0;

        while (pos < length) {
            cp = this.codePointAt(pos);
            size = cp < 0x10000 ? 1 : 2;

            if (index === offset) {
                return this.substr(pos, size);
            }

            pos += size;
            ++index;
        }

        return '';
    };
}

var str = '\uD842\uDFB7野家';
console.log('\uD842\uDFB7' === str.char32At(0));
```

## 文字列を逆順に並べ替える

```js
if (!String.prototype.reverse) {
    String.prototype.reverse = function() {

        var pos = 0;
        var length = this.length;
        var cp = 0;
        var size = 0;
        var ret = '';

        while (pos < length) {
            cp = this.codePointAt(pos);
            size = cp < 0x10000 ? 1 : 2;
            ret  = this.substr(pos, size) + ret;
            pos += size;
        }

        return ret;
    };
}

var str = '\uD842\uDFB7野家';
console.log('家野\uD842\uDFB7' === str.reverse());
```

## 部分文字列を取り出す

先ほど定義した `countChar32` と `char32At` を組み合わせる。

```js
if (!String.prototype.substr32) {
    String.prototype.substr32 = function(pos, length) {

        var count = this.countChar32();
        var ret = '';
        var last = pos + length - 1;

        for (var i = 0; i < count; ++i) {

            if (i >= pos && last >= i) {
                ret += this.char32At(i);
            }

            if (i === last) {
                return ret;
            }
        }

        return ret;
    };
}

var str = '\uD842\uDFB7野家';
console.log('野' === str.substr32(1, 1));
```

`codePointAt` から定義するのであれば、少しコードが長くなる。

```js
if (!String.prototype.substr32) {
    String.prototype.substr32 = function(pos, length) {

        var offset = 0;
        var count = this.length;
        var cp = 0;
        var size = 0;
        var start = 0;
        var index = 0;
        var last = pos + length - 1;
        var retSize = 0;

        while (offset < count) {

            cp = this.codePointAt(offset);
            size = cp < 0x10000 ? 1 : 2;

            if (start === 0 && index === pos) {
                start = offset;
            }

            if (index >= pos && last >= index) {
                retSize += size;
            }

            if (index === last) {
                 return this.substr(start, retSize); 
            }

            offset += size;
            ++index;
        }

        return String(this);
    };
}

var str = '\uD842\uDFB7野家';
console.log(str.substr32(1, 1));
```

## 1文字ずつコールバックを適用する

名前は Ruby の String#eachChar から借りた。 

```js
if (!String.prototype.eachChar) {
    String.prototype.eachChar = function(callback) {

        var pos = 0;
        var length = this.length;
        var cp = 0;
        var size = 0;

        while (pos < length) {
            cp = this.codePointAt(pos);
            size = cp < 0x10000 ? 1 : 2;
            callback(this.substr(pos, size));
            pos += size;
        }

    };
}

var str = '\uD842\uDFB7野家';
str.eachChar(function(c) {
    console.log(c);
});
```

## 1つずつコードポイントを取り出してコールバックを適用する

上述のメソッドを少し修正すればよい。名前は Ruby の `String#each_codepoint` から借りた。

```js
if (!String.prototype.eachCodePoint) {
    String.prototype.eachCodePoint = function(callback) {

        var pos = 0;
        var length = this.length;
        var cp = 0;
        var size = 0;

        while (pos < length) {
            cp = this.codePointAt(pos);
            size = cp < 0x10000 ? 1 : 2;
            callback(cp);
            pos += size;
        }

    };
}

var str = '\uD842\uDFB7野家';
str.eachCodePoint(function(cp) {
    console.log('U+' + cp.toString(16).toUpperCase());
});
```