【JavaScript修行】シンボルって?イテレーターを自作する


本日はJavaScript本格入門の270p〜から学習しています。
参考サイト:https://qiita.com/kura07/items/cf168a7ea20e8c2554c6
https://uhyohyo.net/javascript/16_4.html

シンボル

ES6から導入された新しいプリミティブ型。
オブジェクトのプロパティのキーにできる。文字列ではない!

const obj ={};
const sym = Synbol();
obj[sym] = "mow";

キーとして、シンボルのsymが使われています。

キーの特徴はこちらです。

let sym1 = Symbol('sym');
let sym2 = Symbol('sym');
console.log(sym1 === sym2); //falseになる

同じsymにも関わらず、比較演算子では異なる結果になっています。
唯一絶対なんですね。
しかも暗黙の型変換が行われないので、文字列/数値になりません。(booleanは除く)


console.log(sym1 + ''); //エラーになる

symbolどう使う?

定数指定に便利です。
普通定数っていうと

const MONDAY = 0;
const TUESDAY = 1;

って感じで書きますが、これにはいくつか問題があります。
・const FRIDAY = 0 などが追加されるとバグに
・数字比較を行うと可読性が悪い
・数値としての利用ができてしまう(識別したいだけなので本来必要ない)

そこでSymbolの出番です。

const MONDAY = Symbol();
const TUESDAY = Symbol();

同じ名前でもシンボルはユニーク(唯一絶対無二)なので、これで識別可能です。

プライベートメンバにもなる!

App.js
const SECRET = Symbol();    //シンボル

export default class{
   constructior(secret){
      this[SECRET] = secret;   //symbolで作られたプロパティで初期化を行う。
   }

   checkValue(secret){
      return this[SECRET] === secret;   //プライベートメンバを扱うメソッド
   }
}
private.js
import App from ./App.js

let app = new App('1234');
for(let key in app){
   console.log(key);    //secretは表示されない
}

console.log(JSON.stringify(app));  //JSON文字列にしても表示されない

console.log(app.checkValue('1234')); //true

ただし注意点がいくつかあります。

  • Object.getOwnPropertySymbolsでアクセスできてしまう
  • モジュールの中ではSECRETからアクセスできてしまう。

イテレーターって?

イテレーター・・・オブジェクトの内容を列挙するための仕組みを備えたオブジェクト!
組み込みオブジェクト系もfor..ofを使って列挙できますね。

let ary = ['いぬ','さる','ねずみ'];
for(let item of ary){
    console.log(item);
}

そういった処理が実際内部でどうなっているか探るため、
自分で内部処理を書いてみます。

let ary = ['いぬ','さる','ねずみ'];
let itr = ary[Symbol.iterator]();
let d;
while(d = itr.next()){
    if(d.done){break;}
    console.log(d.done);
    console.log(d.value);
}

みたこのないメソッドがいっぱいあります!
一個一個みていきます。

Symbol.iterator・・・Iteratorオブジェクトを返す。
next()・・イテレーターがもつメソッド。配列の要素を取り出す。
done・・・イテレータが終端に達したかをブーリアンで返す。
value・・・次の要素の値を返す。

上のコードでは、d.doneがfalseになるまで(終端に達するまで)
itr.next()で要素をとり続けています。

for..ofはこのような内部処理でvalueを取り出すシンタックスシュガーとも言えます。

イテレーターを自作してみよう!

for..of文は[Symbol.iterator]メソッド経由でイテレータを取り出していたので、それと同じようにメソッドを実装すればOKです。

自作イテレータ
class Iterator{
    constructor(str){
       this.str = str;
    }

    [Symbol.iterator](){
       let current = 0;
       let that = this;
       return{
          next(){
             return current < that.str.length ?
                {value: that.str[current++],done:false} :
                {done:true};
          }
       };
    }
 }

 let itr = new Iterator(['ごま','さば','食べたい']);
 for(let item of itr){
    console.log(item);
 }

[Symbol.iterator]()の内部処理でvalueとdoneが含まれたオブジェクトを返しています。
for..ofで1つ前のコードのような処理が走って、見事オブジェクトの内容を列挙するイテレーターが
作れました!

今回ちょっと難しかった...