雑ノート(仮)

適当なメモ。

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 nearestStation = 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}]}});

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

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

あとevalを使ってJSONをパースするのはアンチパターンなのでJSON.stringifyを利用する

正規表現リテラル

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

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

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

JavaScriptパターン第2章ノート

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

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

4章まで読めたが、アウトプットが捗らない。5章は写経しないと俺の脳みそにはきつい。

補足:即時関数について

本記事では即時関数を利用しています。ピンとこない方は別記事に書いたものを読んでから本記事を読んでみてください。

Javascriptの変数スコープ

関数ごとにスコープを持つか、グローバル変数化のどちらかであるが、プログラミングの鉄則としてグローバル変数は極力使わないほうがいいという前提に立てば、意図せずグローバル変数を作ってしまう事象は極力避けるべきである。

暗黙のグローバルを避けよ

Varを使わない変数宣言は避けるべき。グローバルスコープを意図せず汚染する恐れがあるため

(function(){
  test = "hello" //暗黙のグローバル変数
  console.log(test)
}());

console.log(test); //hello

コード二行目のtestの宣言のvarを略さず影ば以下のようなエラーが出る。

ReferenceError: Can't find variable: test

代入の連鎖は禁止。片方がグローバル変数になる

(function(){
  var b=a="test"
  console.log("a is " + a+ " in function");//test
  console.log("b is " + b+ " in function");//test
}());

console.log("a is " + a+ " outside function");//test
console.log("b is " + b+ " outside function");//エラー(スコープ外)

暗黙的、明示的なグローバル変数の違い

暗黙の変数はdelete演算子で削除できるが 明示的に宣言したグローバル変数は削除でない

暗黙のグローバル変数は変数ではなく グローバルオブジェクトのプロパティとなるため。 変数は削除できないがプロパティは削除できる

var globalVal ="global";

(function(){
   implicitVal = "implict global";
   
}());

//どちらもglobalスコープ
console.log(globalVal);
console.log(implicitVal);

delete globalVal;
delete implicitVal;

console.log(globalVal);//global
console.log(implicitVal);//ReferenceError: Can't find variable: implicitVal

変数の巻き上げ

実に恐ろしい恐ろしい。 一般的に変数は利用箇所の近くで宣言するのが定石だが ES6以前の環境では明示的に関数の先頭に変数を宣言するのが望ましい。let,constの場合はreference errorになるが、巻き上げは起こっているらしい。

var val1 = "test"
console.log(val1); //test
(function(){
  //↓変数の巻き上げ!!!!!↓
  console.log(val1);//undifind
    

  var val1 = "testtest";
  console.log(val1);
}());
console.log(val1)

単独varパターン

ちょっと気持ち悪いが巻き上げによるバグを喰らわないためには致し方ない

(function test(){
  var a=1,b=2,c=3;
  console.log(a+b+c);
}());

eval()はワル

eval関数は引数の文字列をjavascriptのコードとして解析して実行しようとする。 eval("alert('eval is evil!!!!')"); 本書ではJSONのパースにevalを使ってはいけない、という例が出てきた。*1

XSSなどのセキュリティーリスクがあるので注意。

コードの中で eval() が使われているのを見つけたら、「eval() はワル」という呪文を唱えましょう。

余談ですが、この部分はeval() is Evilの有名なダジャレだけど、訳したり説明したりする気が一切ないのがちょっと切ない。

波括弧の位置

細かいイディオムの話は個人の嗜好でしかないと思うし、統一さえ取れて入ればなんでもいいと思うけど、波括弧の前に改行を入れるのはやめた方がいいと思った

function func() {
  return 
    {name: "Batman" };
}
var test  = fund();
console.log(typeof test);//undefined

上記の例だと二行目returnのあとにundifindが補完されreturn undifindとなり、続くオブジェクトリテラルは無視される。

一般的な命名ルール

コンストラクタは大文字始まりのキャメルケース 変数、関数は小文字始まりのキャメルケース

ただし変数にアンダースコアを含めたり、アンダースコアから始めることで変数の意味を補完するノウハウが紹介されていた

ドキュメンテーションについて

JSDoc Toolkitというjavadocのようなものがあり、そちらを利用できる。 多分Eclipseのようにメソッドの上で/**と入力し改行すると、ドキュメントのテンプレートが入るようなプラグインが各エディタにあるはず。昔salesforceのapex開発をしていた時にsublime textに似たようなアドオンを入れた覚えがある。

*1:JSONがオブジェクトリテラルだったという驚くべき話は次回へ続く

JavaScriptパターン第1章ノート

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

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

Javascriptはオブジェクト思考の言語である

ネィティブオブジェクト 言語側が用意 Arrayなど

Hostオブジェクト:ホストや環境が用意 Windodwなど

が、クラスという概念はない

Prototype

継承などの再利用の鍵となる

Prptopypeはオブジェクトであり、あらゆる関数はprototypeプロパティを持つ

ES5

ES3が99、ES4がポシャったため出版時の最新バージョンとなる

本書執筆時点で搭載ブラウザがないとかいてあるが、こんにちではつぎのバージョンであるES6がまともなブラウザには全て搭載れているという点にフロントエンドの変化の速さを感じる

Strict modeはes5から導入らしい。勝手に6からだと思っていた

JSLintの紹介

JavaScriptインタプリタ言語のため静的な構文解析が存在しない JSLintという静的解析ツールを使い、実行前に構文エラーなどはチェックすべき

私向けの本じゃありませんでした。:レガシーコード改善ガイド感想

レガシーコード改善ガイド

レガシーコード改善ガイド

本中のテクニック、所々の感想などをちょこちょこ八月くらいから書き溜めていましたが、ここで結論を投下。

レガシーコード改善ガイドを読み始めた。 - 雑ノート(仮)

接合部と許容点 - 雑ノート(仮)

時間がないときの機能追加(スプラウト/ラップ) - 雑ノート(仮)

仕様化テストについて - 雑ノート(仮)

わけわからんコードにはとりあえず影響スケッチ。 - 雑ノート(仮)

テストのためだけのコード変更は許されるのか? - 雑ノート(仮)

テストの自動化ありき

この本はレガシーシステムのメンテナンス方法(改善方法)をざっくり説明したあと、Q&A式でノウハウを共有するスタイルになっている。

この本には大きくわけて

の4つがかかれているが、テストコードを書かない場合は下2つは読む意味があまりない。 QA形式の箇所は「XXで困っています」といったものが多いが、気になったのはテストを導入していない人に対するメッセージが何もないこと。

どうやらこれからテストを導入する人々はサポート外のようだ。

そもそも門前払い

この本はレガシー化したシステムにテストコードという武器を使って徹底的にリファクタリングしていきレガシーシステムを治療=改善していくようなビジョンがある。

  • 動いているものは絶対触らない
  • リファクタリングは行わない
  • バグは言われたものだけ直す

上記のような、保守方針がレガシーシステムの治療ではなく延命や対症療法を行うような意識の低い連中は鼻っから想定読者にしていない。

結局そういう風にしか仕事しか回ってこない私のような人間向けに書かれた本ではないな。*1

前向きに

希望の光はないことはない。色々周りの人に話しを聞いていると、別に納品要求になくてもjUnitでテストコードをわざわざ書くレガシーコードレジスタンスとでも言える人々がいるようだ。*2*3 というより普通のプログラマーからはテストを書くのは当たり前、といった態度さえ感じるが。

仕事はそういうもんだと割り切って、普段の取り組みからちょっとづつ変えていけばいいと思う。

前向きに、前向きに・・・

*1:私には編集して祈ることしかできない

*2:JavaならタダでJUnitが使えるから色々できるんだけど

*3:vbでもnUnitという.NET向けのテスト環境があるみたいだけど、フリーソフトなど勝手に入れられない現場だと困る

自動化したテストがたくさんエラーを吐いている

さて、最近導入してみたKarma + jasmineなテスト環境だが ローカルでは成功するが、travis上のものがエラーを吐いている。

Travis CI - Test and Deploy Your Code with Confidence

const Hoek = require('hoek');
^^^^^
SyntaxError: Use of const in strict mode.

strict modeやらconstやらまたES6か、と検討をつけつつエラーメッセージをググってみる。nodeのバージョン指定から7を消したらうまくいったとの事例を発見。*1

github.com

ググりまくった結果、travis.ymlにnodeのバージョンが設定されていなかったため、travis上ではかなり古いバージョンが動いていた?

ローカル

$ node -v
v8.8.1

travis

$ node --version
v0.10.48

ES6が対応していないバージョンのnodeが動いていたのか?とりあえずtravisで動かすnodeなどのバージョンはtravis.ymlで設定できるらしいから、バージョン指定を追加したところテストが成功した。

今まで動いていたものが急に壊れた理由はES6の文法を無理やり突っ込んだからだろう。phantomJsではlet文は使えなかったが、constは使えたので入れ込んだままにしていた。それが原因だと思う。

初心者が適当に作ってググりながらなんとか運用している感が強いが、英語の情報を我慢して読めば、トラブルシューティングになかなか役立つ情報に出会える(今回の事象は英語にアレルギーを持ったまま日本語オンリーでググってたら解決できなかった)

私のようなSIの底辺コピペプログラマーでも英語が我慢できるかできないかで結構差がつくと思う。

やっぱり英語の情報を根気よく読むことが大事だ。

*1:最近気になってるbabelのリポジトリじゃないですか

PhantomJSではlet文が使えない

用語

  • ES6:大きな変更が入った最近のjavascriptの仕様。モダンなブラウザは大体対応している
  • Jasmine : javascriptのテスト環境
  • Karma:テストランナー。Jasmineを動かす
  • PhantomJS:CUI用のブラウザー。テストランナーが利用している

テストを書いたのでリフアクタリング!の勢いでとりあえず変数宣言のvarを全てletに置き換えたが*1テストでエラーになってしまった。

PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
  SyntaxError: Expected an identifier but found 'arr2' instead

Karma runs your tests in real browser (in your case it is PhantomJs) so you should check browser support 'let' and 'const' or not. OR you should use preprocessors which compile your es6 code to es5.

ES6 variables (const and let) · Issue #2442 · karma-runner/karma · GitHub

調べてみたところ jkarma(jasmineのテストランナー)が利用するPhantomJSというテスト用ブラウザがES6をサポートしていないため 使えないようだ。

ES6にはバージョン2.5から対応するらしい(最新は2.1)

ES2015 (also colloquially known as ES6) will be possible with the new effort towards version 2.5 (see #14458 for details).

https://github.com/ariya/phantomjs/issues/14506

調査中に同じトラブルがissueに上がっているossのプロジェクト*2が何個かあったが普通にletをvarに置き換えて対応していた。ES6向けの環境に向けて作っているなら、本来ならletのままでいいはずだが、テストのためにコードを変更しているということだろうか。

レガシーコード改善ガイドじゃないけど、テストのためにスコープいじるのはやだぁ。 対策としてはプリプロセッサとしてBabelというES6→commonJSの変換ができるトランスコンパイラを指定することでテスト時には commonJSにコンパイル後のソースで実行しながら、プロジェクトのソースはES6のバージョンで管理できる。

前例を調査中・・・

*1:関数の中でスコープが共通でない別関数のvar宣言された変数をグローバルスコープとして参照するような変なことをしていない限り、この置換は問題ないはず。

*2:https://github.com/aurelia/cli/issues/259

multi-word-replacerのgithub-pagesにbootstrapを被せた

https://yuyabu.github.io/multiple-word-replacer/

bootstrapの公式ドキュメントを読み込んでレスポンシブデザインに対応した。 暇な人はブラウザのウインドウのサイズを縮めてみたり、携帯からアクセスしてみて欲しい。

まぁ、このツールを携帯から参照することなんてないと思うけど・・・

ちなみにcssは一行も書いていない。 過去にbootstrap含む複数のフレームワークcssと勝手に追加した意味不明な自作cssが干渉しあって大変な目にあった反省から できるだけbootstrap標準のものを利用する方針で適応した。

フロントエンドで大規模なものを作るときに、構成管理とかがきちんとできていないと炎上するんだろうなぁ。 凄まじいことになりそうだ。cssのクラスやidなどの管理方針などをきちんと考えられない・画面の設計ノウハウのないプログラマーcssなんて一行も書かないに限るね。

過去に俺がググってコピペで適当に組み合わせたcssを直してる人いたらごめんなさい。

フロントエンドエンジニアさん南無〜