Yabu.log

ITなどの雑記

JavaScriptパターン第3章ノート

JavaScriptパターン ―優れたアプリケーションのための作法

JavaScriptパターン ―優れたアプリケーションのための作法

コンストラクタよりリテラルの方が良い・・・が一貫したこの章のテーマです。

オブジェクトリテラル記法

キーと値の組みをカンマで区切り、波括弧で囲う記法でオブジェクトを定義できる。

数値、文字、関数、正規表現、配列、別のオブジェクト を含めることができる。

コンストラクタを使う方法は冗長なのでオブジェクトの宣言はなるべくオブジェクトリテラルで行うべき

var obj1 = {
  sample_string:"normal val",// string literal
  sample_number:5, //number literal
  sample_function:function(){
    console.log("this is test function");
  }, //function literal
  sample_regexp:/normal/gi, //regure expression literal
  sample_array:[1,1,9], // array literal
  obj2:{
    sample_string:"this is nested object"
  }
}

//実験
console.log(obj1.sample_number + 15);//20
console.log(obj1.sample_regexp.test(obj1.sample_string));//true
console.log(obj1.sample_array.find((num)=> {return num > 8}));//9
console.log(obj1.obj2.sample_string);//this is nested object
var val1= obj1.sample_function;
val1();//this is test function
console.log(obj1);//{sample_string: "normal val", sample_number: 5, sample_regexp: /normal/gi, sample_array: Array(3), sample_function: ƒ, …}

カスタムコンストラクタ関数

Javascriptにはクラスがないので、クラスをインスタンスするためのコンストラクタ、というより、Objectを作成する関数という扱いなのか?そこがちょっとよくわからなかった。

var Person = function (name) { this.name = name;    
  this.say = function () {
  return "I am " + this.name; };
};
val John = new Person("john");
John.say();// I am John

コンストラクタにnewをつけ忘れると

コンストラクタは只の関数なので、newをつけずとも呼べてしまうが、その場合コンストラクタ内部でのthisはグローバルオブジェクト(window)を指してしまう

//※上記のPersonカスタムコンストラクタ関数を流用しています
var ken = Person("ken");
ken.say();//Uncaught TypeError: Cannot read property 'say' of undefined

console.log(ken)//undefined
window.say();//I am ken
say();//I am ken

初期化中のオブジェクトにプロパティを追加していると思いきや、グローバル変数を作ってるという事態になりかねない。対策としては

  • strictモード
  • コンストラクタ内で別のオブジェクトリテラルを返す
  • コンストラクタ内でthisを使わない
  • thisの型を検査し、適切に自オブジェクトをnew付きで呼び出す(自己呼び出しコンストラクタ)

のどれかが対策としては推奨される

配列リテラル記法

配列も組み込みコンストラクタではなくリテラルを使うべき。理由は数値型を単発で与えた時の挙動が特殊なため

var arr1 = new Array(3); //空の、長さ3の配列を作成
var arr2 = [3];//要素が 3 一つの配列を作成

arr1//[empty x 3]
arr2//[3]

var arr3 = new Array(3.14);//長さに小数点は採用できない・・・(エラー)
//Uncaught RangeError: Invalid array length

JSON

JSONでは関数や正規表現リテラルは使えないが、配列と文字だけを扱うならオブジェクトリテラルとして解釈可能。

JSONってweb apiとかで帰ってくる値でよく見る気がするが、そのままオブジェクトリテラルとして解釈できるのに驚いた。

例:WEB APIから住所を取得

geoapi.heartrails.com

最寄駅を取得可能なハートレイルズ様のAPIを利用しました。郵便番号はGoogle日本法人のオフィス(〒106-6126)にしました。

http://geoapi.heartrails.com/api/json?method=getStations&postal=1066126

{"response":{"station":[{"name":"\u516d\u672c\u6728","kana":"\u308d\u3063\u307d\u3093\u304e","line":"\u6771\u4eac\u30e1\u30c8\u30ed\u65e5\u6bd4\u8c37\u7dda","y":35.662799,"x":139.731151,"postal":"1060032","prev":"\u795e\u8c37\u753a","next":"\u5e83\u5c3e","prefecture":"\u6771\u4eac\u90fd","distance":328.0940048843225}]}}

こちらの取得結果をevalでjavascriptで評価して見ます*1

var json_for_eval = '{"response":{"station":[{"name":"\u516d\u672c\u6728","kana":"\u308d\u3063\u307d\u3093\u304e","line":"\u6771\u4eac\u30e1\u30c8\u30ed\u65e5\u6bd4\u8c37\u7dda","y":35.662799,"x":139.731151,"postal":"1060032","prev":"\u795e\u8c37\u753a","next":"\u5e83\u5c3e","prefecture":"\u6771\u4eac\u90fd","distance":328.0940048843225}]}}'
eval('var nearestStation = ' + json_for_eval);

nearestStation.response.station[0].name;//六本木
nearestStation.response.station[0].line;//東京メトロ日比谷線

ただしJSONではオブジェクトリテラルでは表現可能な関数、正規表現リテラルは記載できません。

あとevalを使ってJSONをパースするのはアンチパターンなのでJSON.parseを利用するのがベターでしょう。

正規表現リテラル

リテラルを使う場合はコンストラクタで必要なエスケープが一部不要。簡潔性のため積極的にリテラルを使うべき。

ただし正規表現リテラルのオブジェクトは構文解析時にしか作られないため、実行時に組み立てるようなものはリテラルではなくコンストラクタを使うべき。

*1:evalは引数の文字列ををjavascriptとして解釈して実行しようとする関数です