JavaScriptで多重ループを抜けるラベル構文について

Facebook にシェア
Pocket

最近、改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用までという本を読んでいるのですが、ES2015も含めたJavaScriptについて本格的に書かれてあって、とても分かりやすいです。

JavaScriptはある程度分かっていたつもりなのですが、それでもこういうまとまった本を読むと、何個かは知らなかった記述がでてきます(ES2015についてはほとんど知らないことばかりですが)。

今回はその中で、2.5.8に記載されていた多重ループを一気に抜けるラベル構文についてメモ。調べてみたら、よくある記述らしいんで、ブログに書こうか迷ったのですが、前に多重ループを抜けるときは匿名関数にして即時実行したほうがいいというようなことを書いてしまったので……。
for文の二重ループを一重ループにする方法 | while(isプログラマ)

で、上記記事に書いた多重ループの抜ける方法をラベル構文で書くと下記のようになる。

変更した箇所は、最初のfor文の前に『kuku』というラベルをつけ、多重ループを抜けるときには『break kuku;』とやるだけ。上で紹介した本が改行してあったので、ラベル指定の後は改行しましたが、『kuku: for(i=0;i<9;i++){』と改行しないほうが分かりやすいかも? このラベル構文はcontinue文でも利用できるとのことで、またswitch文の中でループを抜ける際にはラベル構文でなければならないとのこと(そりゃ、switch命令内でbreakとすると、switch命令を抜けるだけですしね)。 参考:break文とcontinue文でのラベル指定 – 繰り返し処理 – JavaScript入門

改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで
山田 祥寛
技術評論社
売り上げランキング: 4,391

JavaScriptで関数の引数名を取得する方法とその利用例

Facebook にシェア
Pocket

自分が今関わってるプロジェクトですが、AngularJSでサイトを作っていた人が別のプロジェクトに移動することになってとりあえず自分が引き継ぐことになりました。

AngularJSはちょっとだけ触ったことがあるのですが、正直よく分かっていません。Backbone.jsやReactはなんとなく分かるのですが、AngularJSはほとんど全く分かりません。で、案の定、コードを見てどのような動きをしているのか調べようと思っても全く分からず。特に、ある関数(確か、controllerの引数に含めた無名関数)の引数の順番がどうなっているのか全く分かりません。複数のファイルで同じような記述があったのですが、全く順番が異なるように思えました。いくら調べても分からなかったので、仕方なく尋ねると、「Angularが引数名で判断してるんです」。

はぁ? JavaScriptはある程度分かってるつもりの自分ですが、引数名を判断する方法があるなんて知らずに、驚きました。

調べてみると、すぐにその方法が見つかりました。単純に、関数自体のtoStringメソッドを呼んで、分解しているそうです。
参考:関数の引数名を取得する – Qiita

上記のgetParams関数は、引数に関数を入れると、その関数の引数名の配列を返すようになっているようです。replaceの中身の正規表現をよくよく見てみると、一つ目の|の左側(\/\/.*$)が行コメント、二つ目の|の左側(\/\*[\s\S]*?\*\/)が複数行コメント、二つ目の|の右側(\s)がスペースやタブや改行文字などのホワイトスペース文字を表しており、フラグにはg(グローバルサーチ)とm(複数行検索)を指定しているそうです。その正規表現にヒットした文字をまず無くしているわけですね。

続いて、matchを使って、丸括弧内を検索して引数のみを取得しているようです(ちなみに、インデックスを0にするとカッコを含む文字列を取得できる)。

実際にAngularJS 1.5.8のソースコードを調べてみると少しだけ異なるようで、3923行目のextraArgs関数でtoStringの結果を分解しているようですが、replaceの中身の正規表現は『/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg』となっています。カッコが多いのはともかく、この時点ではホワイトスペースを省いていません。さらに、matchも『/^([^\(]+?)=>/』と『/^[^\(]*\(\s*([^\)]*)\)/m』の二種類になっています(前者はES2015の対応っぽいですね)。変数名に)は使えないのだから、『[^\)]*』なんてしなくていいような気はするんですが、念のためそうしているのかもしれません。

なお、実際にextraArgs関数を呼んでいる箇所は3955行目のannotate関数の中のようです。この下のforEach関数(※:念のため言っておくと、この関数はAngularJSで定義している関数です)内のreplaceでホワイトスペースを削除しているようです。replaceの第一引数の正規表現は『/^\s*(_?)(\S+?)\1\s*$/』となっています。どうやら、引数の前後にアンダースコア(_)がついてもいいようですね(ただし、実際に試しとぁけではないので本当にそうなのかは分かりません)。

とりあえず、引数名を取得する方法はわかったので、自分でも実際に試してみることにしました。そこで書いてみたのが以下のようなコード。

第二引数に指定した関数の引数名を取得して、その引数名に対応する第一引数のオブジェクトの値を引数に含めて実行するという関数です。この関数を書くのにあたってこまったのが、どうすれば関数の引数の数を動的に変更できるのかということ。調べてみたら、Functionオブジェクトのapplyメソッドを使えば、配列内の値が引数になって実行されるということが分かって、なんとかできました(参考:Function.prototype.apply() – JavaScript | MDN)。applyの第一引数のthisには特に意味がありません。意味がないのでむしろ、nullとかでもしておくべきだったかもしれません。

というわけでさらにこの関数を実行するサンプルを作成してみました。
サンプル:関数から引数名を取得するサンプル

コードは下記のようになっています。

もう少し実用的なサンプルを作成できればよかったのですが、思いつきませんでした。

それにしても、あらためてJavaScriptは自由度の高いプログラミング言語だと思いしらされました。自由度が高すぎて、AngularJSみたいに、パット見わけがわからないライブラリまでできてしまうという。まあ、AngularJSのせいにするのもなんなので、もう少しAngularJSを勉強しようと思います。

paizaの恋愛SLGの水着とサンタ服がゲットできない……

Facebook にシェア
Pocket

先日、paizaからプログラミングで彼女を作るゲームが発表されました。
恋愛SLG: プログラミングで彼女をつくる|paizaオンラインハッカソン7
参考:プログラミングさえできれば誰でも彼女がつくれる恋愛シミュレーションゲーム「プログラミングで彼女をつくる」 – GIGAZINE

内容は出題されたプログラミングの問題を解くと、その問題にあわせて彼女用のパーツや衣装をゲットできるというもの。

とりあえずコツコツやって、全部ゲットできたらブログに「彼女ができました」というエントリーでも書こうかと思ったのですが、何度やっても「水着」と「サンタ服」をゲットできないでいます。どちらも、提出前動作確認では合格するのですが、テストケースで失敗します(サンタ服のほうなんて、テストケース3までは合格しますが、4で失敗します)。テストケースはデバッグできないのでほとほと困っています。

試したコードは下記です。

これを実際にブラウザ上で動作するようにしてみたのが下記のページ
paizaのサンタ服と水着ゲットチャレンジ

水着ゲットチャレンジで38を入力すると742912となりますし、サンタ服ゲットチャレンジで問題ページの入力例2を入力すると240になり、何が間違っているのか全く分からないでいます。環境がNode.jsとブラウザ(Chrome)で違うというのがあるのかもしれませんが、paiza.IOでもうまくいっているように見受けられるので、多分環境が原因ではないと思います。

誰か失敗する原因が分かる方はヒントを教えて下さい(失敗するテストケース等)。

amazonの「持っています」に登録するブックマークレット作った

Facebook にシェア
Pocket

どうもいまだに、『amazon 持っています』と検索すると、このブログの過去記事が上位に表示されるようです(amzon 持っています – Google 検索)。
過去記事:amazonの「持っています」にチェックを入れる3つの方法 | while(isプログラマ)

ただし、上記記事を書いた時と今では登録方法が異なっています。いや、異なっているというより正確には減っています。上記ページで3種類の登録方法を書きましたが、うち2種類が現在使えなくなっています。1ヶ月ぐらい前まではウィッシュリストに入れて削除すると持っていますのチェックがでるので、それにチェックを入れると登録することができたのですが、それも無くなりました(参考:アマゾンの「持っています」登録方法: アマゾンの「持っています」登録が変更された!)。これにより、おすすめ商品に表示されている商品しか「持っています」に登録できなくなってしまいました。

これは正直困ります。困ったすえ、試行錯誤して、「持っています」に登録するブックマークレットを作ってみました。
ブックマークレット:持っています登録

使い方は簡単。上記リンクをブックマークバーにドラッグ&ドロップするか右クリックしてブラウザのお気に入りに登録します。その後、持っていますに登録したいamazonの商品ページに移動して、このブックマークをクリックすると、「持っています」に登録します。成功すれば、『登録しました』というアラートが表示されます(といっても、ログインしてなくても『登録しました』と表示されるので、後で「持っています」にチェックを入れた商品のページで確認したほうがいいです)。なお、削除機能はありません。削除は「持っています」にチェックを入れた商品ページで行ってください。

具体的なコードは下記のとおりです。

見て分かるとおりjQuery依存となっています。もし、amazonがjQueryを利用しなくなったらこのブックマークレットは利用できなくなります。後、amazonの仕様が変更されたら利用できなくなる可能性は十分あります。

それにしても何で、おすすめ商品に表示されてる商品しか持っていますに登録できなくなったんだ……。まあ、おすすめ機能だけ利用されて、商品を買うのは別の場所というのはamazonに利益がありませんしね(自分のことです……)。そもそも、これまでの流れからして、『持っています』の機能が無くなるんじゃないかと思わなくもないです。

JavaScriptでlocationオブジェクトのようなオブジェクトを作成する関数を作ってみた

Facebook にシェア
Pocket

locationオブジェクトは現在表示しているページのURLの情報を含んだオブジェクトです(参考:window.location – Web API インターフェイス | MDN)。

ただ、リファラ(document.referrer)のように、自身のページ以外のURLもlocationオブジェクトのような情報を取得したいと思ったので、試しに作ってみました。正規表現使ったらもっと簡素にできるかもしれませんが、地道に検索して抜き出して……という感じで作っていきました。

引数にURLやパスを入れるとlocationオブジェクトのようなオブジェクトを返す関数です。ついでに、GETパラメータもオブジェクトとして取得するようにしています(参考:JavaScriptでGETパラメーターを取得する – Qiita)。

試しに、node.jsで”http://amyu.localhost:9000/hogehoge/index.html?a=a&b&c=c#zzz”というURLにアクセスできるようにし、locationオブジェクトと今回作った関数を比較してみました。開発者ツールのコンソールでcopyコマンドを利用し、それをペーストした結果です。

順番が異なっているのでわかりづらいですが、あってるようです。

ところで、今回この関数を作るにあたって、相対URLでも絶対URLに変換したうえで取得するようにしよう。そうすればaタグに記述している相対URLの情報も取得できるし。と思って調べたらaタグのhrefプロパティを取得すれば相対URLを入れていても絶対URLで取得できるということが分かりました(参考:JavaScript – 相対URLを絶対URL(URI)に変換する方法について – Qiita)。

なんだ、そんな簡単に相対URLを絶対URLに変換できるんだー! と驚きました。ただ、ここで試しておくべきだったんです……。a要素のプロパティに『hostname』や『pathname』があるのかどうかということを……。そうです。上記みたいな関数を作らなくても、a要素のプロパティにあるようなんです……。さっき知りました……。

参考:Location – Web API インターフェイス | MDN

まだまだJavaScriptは知らないことがあると実感しました。