僕が感じる、JavaScriptの文法の特徴

2019年5月6日月曜日

プログラミング

JavaScriptの文法の特徴

ゴールデンウィーク最終日、やることがなくなってしまったのでブログを書きます。

また、休み中にキーボードを叩きすぎて右手が腱鞘炎気味なので、左手だけで書きます。
ミスタイプがあったらすみません。


JavaScriptについてです。

最近JavaScriptの面白さに目覚めて、趣味でもJavaScriptをやってるのですが、ちゃんと勉強を始めてみると「今まで仕事で主に使ってきた言語と、ずいぶん違うなぁ」と思う部分が多かったので、記事にまとめておこうと思います。

もし少しでも、JavaScript勉強中の方のお役に立てれば幸いです。


前提

JavaScriptの文法の特徴 前提

一口に「JavaScriptの特徴」と言っても、何の言語と比べるかによるかと思うのですが、自分が仕事で使ってきた言語は以下のようなものです。

Java、VB.NET、Ruby、PHP、C++、ShellScript、PL/SQL、VBA

最近は仕事でC#を使うことになったので勉強中です。


基本的にはJavaVBを使っていたので、何だか静的型の言語の方が使いやすい時期が長かったです。
静的型:変数に入れる値の型を、あらかじめカッチリ決めて書く言語。)

PHPC++は、あんまり長くは使ってなかったので、理解度は浅めです。


そんな僕が感じる、JavaScriptの文法で「変わってるなぁ」「面白いなぁ」と思うところを書いていきたいと思います。


また、「ブラウザ上で動かせる」とか、「サーバーサイドではノンブロッキング」とかいう全体的なこと?ではなく、プログラムを書く際の文法で、気になった点を書いていければと思います。

おかしな所があったら、コメントでご指摘頂けるとうれしいです。


JavaScriptの文法の特徴


僕が一番強く感じるJavaScriptの文法の特徴は「自由」ということです。
たまに「何でもアリか」と感じるぐらい、自由さを感じます。

個別の内容を、以下で見ていきたいと思います。

引数が足りなくても、多過ぎてもOK

引数が足りなくても、多過ぎてもOK

まずは関数を呼び出す時についてなのですが、
たとえば以下のような関数を見てみます。

function sayHello(firstName, lastName){

  console.log(`Hello, ${firstName} ${lastName}.`);

}

sayHello("Kosuke", "Zaizen");

// 結果:"Hello, Kosuke Zaizen."

関数sayHelloでは、引数で文字列を2つ受け取り、コンソールに挨拶文を表示しています。

引数が足りない時


以下では、引数を一つ減らして、この関数を呼び出してみます。


function sayHello(firstName, lastName){

  console.log(`Hello, ${firstName} ${lastName}.`);

}

sayHello("Kosuke");

//結果:"Hello, Kosuke undefined."

関数sayHelloは2つの引数をとる関数として定義されていますが、上記のように、引数を一つしか渡さなくても、正常に呼び出すことができます。

Javaなどのコンパイラ型言語でこんな呼び出し方をしたら、コンパイルの時点で
「引数足りてまへんで~」
「そんな引数のとり方する関数、存在してまへんで~」
って、IDEから怒られてしまいそうです。

(IDE:統合開発環境 - EclipseとかVisual Studioとか)


JavaScriptでは、受け取れなかった引数には一旦undefinedを入れて、関数が正常に実行されてしまうのですね。


引数が多過ぎるとき


個人的にもっと違和感があったのが、JavaScriptでは
「渡す引数が多すぎても問題なく実行できる」
ってところです。

以下のソースコードでは、2つの引数をとるべき関数sayHelloに対して、引数を4つも渡しています。

function sayHello(firstName, lastName){

  console.log(`Hello, ${firstName} ${lastName}.`);

}

sayHello("Kosuke", "Tanaka", "Yoshida",  "Zaizen");

//結果:”Hello, Kosuke Tanaka.”

引数を多く渡しても、何の問題もなく実行できています。

また、以下のように記述すれば、多く渡し過ぎた引数を、処理の中で用いることもできます。

function sayHello(firstName, lastName){

  console.log(`Hello, ${arguments[0]} ${arguments[1]} ${arguments[2]} ${arguments[3]}.`);

}

sayHello("Kosuke", "Tanaka", "Yoshida",  "Zaizen");

//結果:”Hello, Kosuke Tanaka Yoshida Zaizen.”

上記の形になってくると、
「関数を定義する時に(firstName, lastName)って、引数の個数を指定してる意味は??」
って思えてきます 笑

とても自由に書けちゃいます。


関数もオブジェクト

関数もオブジェクト

JavaScriptでは関数も「オブジェクトの一つ」とみなされるので、変数に代入できてしまいます。

function sayHello(firstName, lastName){

  console.log(`Hello, ${firstName} ${lastName}.`);

}

let saySomething;

saySomething = sayHello; //代入

saySomething("Kosuke", "Zaizen");

//結果:"Hello, Kosuke Zaizen."

上記では関数sayHelloを、変数saySomethingに代入していますね。

代入した後は、saySomethingを呼び出すことで、同様の処理が実行できていることが分かります。

(C#とかでもラムダ式を使えば、関数を変数に入れられますが… Javaでも8からはラムダ式が導入されてますね…)


メソッドもプロパティのうち

メソッドもプロパティのうち

上記の「関数もオブジェクト」という点に関連するのですが、メソッドの扱いが特徴的だと思います。

普通の?オブジェクト指向言語では、
「オブジェクトとは、メソッドとプロパティを持っているものである」
って言われることが多いと思います。

ただ、JavaScriptの場合は、いわば「プロパティしかない」ような状態です。

そのプロパティに対して関数が代入された場合、特にそれをメソッドと呼んでいるに過ぎません。

つまりJavaScriptでは、メソッドはプロパティの一種であって、別の物ではありません。


JavaScriptでは「関数もオブジェクト」で、変数等に代入できるがために、「オブジェクトのプロパティに関数を代入する」なんてことができて、こんな仕様になっているのですね。


つまりオブジェクトが、ただの連想配列っぽい

つまりオブジェクトが、ただの連想配列っぽい

結局、上記のような背景から、JavaScriptではオブジェクトというものが、すごくシンプルであるように感じます。

プロパティとメソッドが別々に分かれていて、プロパティをPrivateにしてカプセル化して……
なんてことを考えなくても、配列ぐらいのノリでオブジェクト作って、使えます。


なんだか、ただの連想配列みたいです。

連想配列とは、「キー」と「値」をセットで格納できる配列みたいなやつですね。

JavaだったらMapRubyだったらHashVBだったらDictionaryなどと呼ばれているやつです。

その連想配列が、JavaScriptだったら「オブジェクト」って呼ばれているだけな気がします。

JavaScriptではオブジェクトとは「キーをセットで持てるもの」に過ぎず、たまたま値として関数が代入されると、それをメソッドと呼ぶ。
ぐらいに考えております…


thisがコロコロ変わる

thisがコロコロ変わる

Javaなどでthisというと、
「このインスタンスの」
という言葉に言い換えられるものだと覚えていました。

クラスのプログラムを書く際に、そのクラスのメソッドの中でthisと記述することで、自分自身のインスタンスを呼び出せたのですね。


ただ、JavaScriptのオブジェクトが持っているメソッドは、変数にも入る「値」としてホイホイ受け渡せるので、
「関数が定義された時のthisと、その関数が実行されるときのthisが違う」
ということも普通にあります。

具体的なソースを見てみると…

let Taro = {name: "太郎"};
let Hanako = {name: "花子"};

Taro.sayName = function(){
  console.log("I'm " + this.name);
}

Hanako.sayName = Taro.sayName; //ここでthisが変わる

Hanako.sayName();

//結果:"I'm 花子"

上記では、sayNameという関数を、初めはTaroオブジェクトのメソッドとして定義しています。

このsayNameというメソッドの中でthisというキーワードが使われていますが、TaroのメソッドとしてsayNameが定義された時点では、このthisはもちろん、Taro自身を指しています。


しかしその後、sayNameメソッドは、Hanakoオブジェクトの同名のプロパティに対して、Taroオブジェクトから受け渡されています。

この時点で、HanakoオブジェクトのsayNameメソッド内のthisは、Hanakoオブジェクト自身を指すようになります。

その為、実行結果は「I'm 花子」となっています。


このようにJavaScriptではメソッドをオブジェクト間で受け渡せるため、「関数が定義された時点と、実行される時点でのthisが指す内容が異なる」ということが、よく起こります。

thisの値をコロコロ変えたくない場合は、bind関数と呼ばれるものを用いるなどの対策を行っておく必要があります。


この程度のプログラムではまだ処理が追えますが、プログラムが大きくなってくると、「thisって誰?」という状態に、僕はよくなります 笑


オブジェクトのプロパティを、後からホイホイ付け足せる

オブジェクトのプロパティを、後からホイホイ付け足せる

JavaScriptを自由と感じるもう一つのポイントに、
「オブジェクトのプロパティを、後からホイホイ付け足せる」
というものがあります。

(ただの「連想配列」だと考えれば、当たり前なのかも知れませんが…)


上記のTaroHanakoのプログラムの例で言うと、以下のようにオブジェクトをリテラルで定義した時点では、sayNameなんてプロパティやメソッドは存在していません。
(リテラル:ソースコード中に定義をベタ書きすること。)
この時点ではプロパティとして、nameがあるだけです。

let Taro = {name: "太郎"};
let Hanako = {name: "花子"};

そこに後から、
Taro.sayName = …
という記述を行うことで、オブジェクトに対して、行き当たりばったりでプロパティやメソッドを追加できちゃっています。


クラスとしてオブジェクトの設計図をあらかじめカッチリ決めておいて、それに従ったプロパティしか、オブジェクトは持つことができない…
なんていう堅苦しいことは、JavaScriptではないのですね…


個人的には、この性質はとても重宝しています。

if(Taro.sayName){
  //何か処理
}
という風に書けば、TarosayNameというプロパティを持っているかを判定できるので、様々な状態を表すプロパティを、好き勝手オブジェクトに持たせて、判定することができます。

ただ、それはあくまで個人でプログラムを書く場合であって、大人数で開発するときは、色んな人が色んな所から、好き勝手プロパティを追加していったら、収拾がつかなくなる気もします…


結論

JavaScriptの文法の特徴 結論

結論としては、JavaScriptは
自由で楽しい
です。

この記事ではプログラムそのものの文法についてばかり書きましたが、何といってもJavaScriptの特徴は、
「ブラウザ側でWebの画面に動きを出せる」
ということでしょう。

もっと深くJavaScriptを理解して、ワクワクするようなUIを作っていきたいものです。


Node.jsを使えばバックエンドでもJavaScriptを書けますが、個人的にはノンブロッキングなところが扱いにくかったので、「う~ん…」という感じです…

まだNode.jsを使ったことがなくて、興味がある方は、是非サーバーサイドでのJavaScriptも試してみてください。


この記事が、JavaScriptを勉強中の方の、学習のヒントになれば幸いです。
最後までお付き合いくださり、ありがとうございました。


不備などあれば、コメントでご指摘もらえると嬉しいです。