人気ブログランキング | 話題のタグを見る

var foo = function (x) { ... } の効用

関数を定義するのに funnciton foo(x) { ... } ではなく var foo = function (x) { ... } という記法を取るのは奇を衒っているようにも見えるが、コードの可読性を上げる意味もある気がする。次のようなコードを書いてみた。

var square = function (x) {return x*x };
var add = function (x,y) {return x+y };
var sumOfSquares = function (x) {return x.map(square).reduce(add) };

このコードで、function キーワードの後ろを読めば、引数と関数の内容が一目瞭然だ。また、var キーワードの後ろを見ると、関数名の一覧になっている。まるで、Haskell のタイプシグネチャーのような読み方ができる。= function を -> のような記号と考えればいいのだ。

単純な関数だけでなく、次のような手続き的なコードも同じ利点がある。

var sumUpTo = function (x) {
var total = 0;
for(i=1;i<=x;i++) {
total += i;
}
return total;
}

この場合も var キーワードの後ろは関数名。function の直後は引数リスト。return される値は、この処理の結果を表している。

この書き方の利点はもう一つある。} の後で改行すれば、あの邪魔な ; が省略できるのだ。例えば次のようなコードもきちんと動く。

var square = function (x) {return x*x }
var add = function (x,y) {return x+y }
var sumOfSquares = function (x) {return x.map(square).reduce(add) }

コードの記述を上の方式で統一すれば、; をかなり見ないで済む。

また、これらの関数の中に現れる変数の前には必ず var キーワードを置くようにすれば、上の関数は綺麗にカプセル化される。統一した記法で、安全なプログラムが書ければ気持ちがいい。

次のコードは ; がないが、きちんと動いた。

var total = function (x) {
var total = 0
for(i=1;i<=x;i++) {
total += i
}
return total
}

a_lert( total( 10 ))

これから、JavaScript のコードは意識的に var foo = function (x) { ... } で書いてみることにする。

追記

JavaScript の文末には必ず ; (セミコロン)を置くことが勧められているのでそのようにしていたが、JavaScript には ASI (Auto Semicolon Insertion) 機能があるので、たいていの場合セミコロンは省略できるらしい。しかし、JavaScript の仕様では return, throw, break, continue, ++, — という Restricted Production の前後では改行が禁止されているためそれらの前後で改行すると意図とは違う箇所にセミコロンが挿入される場合がある。そのため文末のセミコロンは省略しないことを勧められているらしい。

しかし、これもエキスパートの間でもセミコロンをつける派とつけない派があり大論争になっているようだ。


個人的には、Ruby や CoffeeScript などで行末にセミコロンをつけないのが喜ばれているし、つけない派でいいのではないかと思う。プログラムを一気に作ってデバッグすることはないので、おかしな動作はデパッグの中ではじき出されるだろう。セミコロン派の人にコードを渡す時は、エディタの正規表現置換で行末にセミコロンを追加すればいい。行末にセミコロンがあるのは汚く見えるし、余計なキー入力はしたくない。今日からセミコロンつけない派になることにする。

ASI のパーサーを改良すればいいだけの話のような気もするが。

追記2

Restricted Production 以外に 関数定義 var foo = function () {} の後に無名関数の即時実行 (function (){..}()) が続いたりするとそれが引数と解釈される場合があるので曖昧なコードを書くときは空行で区切るか、セミコロンを使用したほうがいいらしい。これは文法というより、表現の曖昧性の問題だろう。好みはあるかもしれないがクロージャーを記述するときは先頭にセミコロンを置いて

;(function() { ... })()

としたらそのコードがクロージャーを記述している目印にもなるのではないか。あまりきれいな表現ではないが。または、クロージャーを書くときはかならず空行を挟むようにしたほうがきれいかもしれない。

# by tnomura9 | 2016-01-29 05:22 | JavaScript | Comments(2)

The Revealing Module Pattern

The Revealing Module Patten という JavaScript のデザインパターンがあるらしい。前回の記事ではローカル関数を隠蔽するために。クロージャーを使ったが、関数の即時実行で無名オブジェクトを返す方法だ。このデザインパターンでは、戻り値のオブジェクトの属性の値を公開したい関数にする。利点としては、

1. 開発者にクリーンなライブラリを提供できる
2. 隠蔽したデータを利用できる
3. グローバルの名前空間を汚さない
4. 関数や変数をローカルにして隠蔽できる
5. スクリプトの書き方を統一できる(ユーザ側で)
6. 公開されるメソッドがはっきりしているので、コードが読みやすくなる

欠点は、

1. 隠蔽されたローカル関数にアクセスできない
2. ローカル関数のオーバーライドができない

などだ

https://carldanley.com/js-revealing-module-pattern/ のコード例には次のようなものが掲げてあった。

var MyModule = ( function( window ) {
function myMethod() {
a_lert( 'my method' );
}
function myOtherMethod() {
a_lert( 'my other method' );
}
// explicitly return public methods when this object is instantiated
return {
someMethod : myMethod,
someOtherMethod : myOtherMethod
};
} )( window );

// example usage
MyModule.myMethod(); // undefined
MyModule.myOtherMethod(); // undefined
MyModule.someMethod(); // alerts "my method"
MyModule.someOtherMethod(); // alerts "my other method"

公開されるメソッドは MyModule オブジェクトの MyModule.someMethod と Mymodule.someOtherMethod で プライベートな myMethod や myOtherMethod は外部からは利用できない。

このような特定の目的を持ったコードの書き方を JavaScript デザインパターン というそうだが、それらをたくさん知っていると、随分コードの開発が楽になるのだろう。どうやら、JavaScript の文法を理解しただけでは JavaScript 使いにはなれないようだ。

どの分野もそうだが、JavaScript も奥が深そうだ。大変だ。

追記

このデザインパターンを使ってコードを書いてみたが、ローカル変数にアクセスできないのでデパッグがやりにくかった。開発の時は普通にグローバル空間でコードを書いて、最後にクロージャーでカプセル化するのが良いようだ。

# by tnomura9 | 2016-01-29 02:44 | JavaScript | Comments(0)

ローカル関数

JavaScript で無名関数を使いたいことが多いが、無名関数を記述するのに function() { ... } と書くとやたらと fundtion キーワードが増えて煩わしい。しかし、いちいち関数に名前をつけていると、名前空間を不必要に汚してしまう。そういう時は、ローカル関数を使うといい。関数の中で定義されたローカル関数は関数の実行が済むと消滅してしまう。

var squareAndSum = function(x) {
var square = function (x) {return x*x};
var add = function (x,y) {return x+y};
return x.map(square).reduce(add);
}

a_lert(squareAndSum([1,2,3]));

この方法では squareAndSum 関数を呼び出すたびに squre 関数と add 関数が再定義されるので squareAndSum 関数のオーバーヘッドが気になる場合は、次のようにクロージャーを使うといい。クロージャーは関数を戻り値として返す関数だが、クロージャーの戻り値の関数は、クロージャーの中のローカル関数を利用できる。

var squareAndSum = (function() {
var square = function (x) {return x*x};
var add = function (x,y) {return x+y};
return function(x){return x.map(square).reduce(add)}
})();

a_lert(squareAndSum([1,2,3]));


# by tnomura9 | 2016-01-28 05:23 | JavaScript | Comments(0)

function foo() { ... } と foo = function() { ... }

最近 var foo = function() { ... } という記述をよく見かけるようになった。function foo() { ... } と同じ意味になるのだが、どちらかというと前者の方が好みだ。無名関数を変数に代入することによって、関数も他の値と同じように他の関数の引数にできるのだというイメージが湧いてくる。

たとえば Array.prototype.map 関数を使う時に、次のようなコードが自然にかけるのがいい。map の引数の中に function() { ... } と入らないのがすっきりする。

> var square = function(x) {return x*x};

[Function]

> [1,2,3].map(square)

[ 1, 4, 9 ]


JavaScript の表現力の秘密が、関数が first order object であるということがよくわかる。


ふと思いついたのだけれど、無名関数の省略形を (..) { ... } にしたら、function キーワードが要らなくなるのではないだろうか。これだと関数は全て、


var foo = (x) { ... }


で定義できる。


# by tnomura9 | 2016-01-27 06:24 | JavaScript | Comments(0)

CoffeeScript の気になるところ

CoffeeScript の日本語の解説を見つけた。


これを読んで CoffeeScript の仕様で気になる点が2、3あったので書いてみる。

1. CoffeeScript は JavaScript の単なる略記法ではなく、制御文などに CoffeeScript 独特なものがあるということ。たとえば CoffeeScript には for 文がない。制御文に微妙な違いがあると、JavaScript と CoffeeScript の間で混乱が起きやすいのではないかと思う。制御文の機能については、表現は様々でも、言語間でそれほど違いが発生しないのだから、わざわざ新しい制御文を作ることは混乱を引き起こすのではないかと思う。

2. 関数の引数のカッコを省略したこと。これは Haskell の方法を取り入れたのかもしれないが、Haskell の場合は関数の取り扱い方が JavaScript とは異なっている。Haskell では関数の出力をパイプラインのようにつないでいくというプログラミングスタイルになるので、手続き型言語のスタイルを取る JavaScript の関数の取り扱い方とは異なっている。カッコをスベースに置き換えることで、入力の手間はそう増えないのだから、CoffeeScript でコーディングする場合も、関数の引数はカッコで囲んだ方がいいと思う。

3. グローバル変数の名前は、ローカル変数には使えなくなったこと。これは関数の内容の隠蔽にあまり良くない影響を与えるのではないだろうか。関数の内部を記述するのに、いちいち外部のグローバル変数の名前を気にしなくてはならないのは気持ちが悪い。

4. 関数の最後の文の戻り値が必ずその関数の戻り値として戻されている。これは、余分な工夫のように思える。戻り値を指定したい時は、明白に return 文を記述すれば問題はないのだけれど、自分の意図しないところで、コードが作られるのは気持ちが悪い気がする。

やはり、CoffeeScript を単なる JavaScript の略記法と考えるのは、無理なようだ。JavaScript でコードを記述する時よりは、明らかにバグの発生が少なく、可読性が向上すると思われるし、プロジェクトの開発に CoffeeScript が指定されている場合あるようだ。しかし、その場合も全く新しい言語を学習するくらいのつもりで取り組んだ方がいいのではないかと思う。


# by tnomura9 | 2016-01-27 06:07 | JavaScript | Comments(0)