Javascriptの基礎でちょっと気になる部分をまとめてみた

家でJavascriptの本を見かけたから、パラパラと見直してみた。確か自分はプログラミングを最初rubyで学んで、その後Javascriptって面白いんじゃない?みたいな感じで勉強しようと思ってこの本を買った気がする。 本はJavascript本格入門。

ここでは、基礎的でもちょっと気になるだろう部分を書いていこうと思う。

変数にはvarをつけよう

varをつけなくても使うことができるけど、それはグローバル変数として扱われてしまう。グローバル変数ばかりだとわけがわからなくなるのでやめよう。

アンカータグにスクリプトを埋め込む

何か作る時にアンカータグに直接埋め込むようなことはしないと思うけど、こんな書き方もJavasriptはできる。

<!DOCTYPE html>
<html>
    <head>
        <title></title>
    </head>
    <body>
        <a href="Javascript:window.alert('Hello World');">ダイアログを表示</a>
    </body>
</html>

<a href="Javascript:window.alert('Hello World');">ダイアログを表示</a> こんな記述があるけど、上記のコードはクリックすると実行することができるんです。

f:id:utr066:20180430011846j:plain

あくまでちょっとスクリプトを埋め込むための記法だから、実際のプロジェクトではまあ見ないよね。

no script

Javascriptは、今ではどのブラウザでも使えるようなもんだしなかなか見ないけど、<noscript>~</noscript>の中に記述された内容は、 ユーザーの使用しているブラウザがスクリプトに対応していない場合や、 スクリプトを実行しない設定にしている場合などに表示させることができる。

<script type="text/javascript">
<!--
today = new Date();
document.write(today);
//-->
</script>
<noscript>
<p>このページではJavaScriptを使用しています。</p>
</noscript>

小数点を含む計算には注意

小数点の計算を行う時になんか期待する答えと違うなあ、難しい計算じゃあないんだけどなあ・・・と思ったことはないでしょうか。これはJavascriptは内部的に数値演算を10進数ではなく2進数でおこなっているため誤差が出ちゃってる状態ですね。

f:id:utr066:20180430012656j:plain

試しにブラウザのコンソールから小数点の計算をしてみるとちょっとおかしいですよね。これを正しく計算するのはちょっと面倒で以下のように計算しなくてはいけない。

  1. 値を整数に直して計算する
  2. 1の結果を小数点に戻す

f:id:utr066:20180430012931j:plain

面倒ですが、期待する結果が出力されていますね。

厳密に比較したい

等価演算子(==)で左辺と右辺を比較する場合以下のような現象が起きます。

f:id:utr066:20180430182052j:plain

型の違いがあるのにも関わらず、trueとなってしまいます。これでは厳密に比較する場合に困るので厳密等価演算子(===)を使う。

f:id:utr066:20180430182355j:plain

今度はfalseになりましたね。

functionが読み込まれるタイミング

functionの宣言前に関数を使っている以下のコードは実行できるだろうか?

console.log('三角形の面積:' + triangle(5,2))

function triangle(base, height) {
    return base * height / 2;
}

上記のように書いたファイルをブラウザで実行してみると、ちゃんと結果がでます。

f:id:utr066:20180430183306j:plain

関数の宣言前にその関数を使うことができるなんて不思議だなあ・・・と思うけど、function命令はコードを解析/コンパイルするタイミングで関数を登録しているんです。だから後ろに書いてもその関数を使うことはできるんですね。何気なく書いているけど、まあ知っておくとしっくりくるよね。

なので、関数を変数として扱っているこんなスクリプトは実行不可能。

console.log('三角形の面積:' + triangle(5,2))

function triangle(base, height) {
    return base * height / 2;
}

エラーが起きるはずです。

引数の数をチェックしない

これって実行するとどうなると思いますか?引数の数が違うから実行できないですかね?

function print(text) {
    console.log(text);
}

print('1番目のテキスト', '2番目のテキスト');

これは実行すると1番目のテキストという文字だけが出力されます。引数の数にあっていなくても最初に渡したものは出力されるんですよ。で、これが内部的にはどうなっているかというと引数として渡したテキストはargumentsオブジェクトというものに保存されるんですね。 だからこういう書き方もできる。

function print(text) {
    console.log(arguments[0]);
    console.log(arguments[1])
}

print('1番目のテキスト', '2番目のテキスト');

これを実行すると2つのテキストが出力されます。引数情報を管理するargumentsオブジェクトなんてものがあるなんてコードには書いてないからなかなか分かんない。

引数の数のチェックをしたい

さっき書いた通り、javascriptでは引数の数をチェックしてくれないので、数を仮引数と本引数で合わせたいときはargumentsオブジェクトを使ってチェックします。

function print(text) {
    if (arguments.length != 1) {
        console.log('引数の数が違うよ!(例外処理とかここに入れるといいでしょう。)');
        return;
    }
    console.log(text);
}

print('1番目のテキスト', '2番目のテキスト');

こんな風にarguments.lengthで引数の数をチェックすることができます。でも、引数の数があやふやなんてなんだかアプリケーションの設計ミスのような気がしなくもない。場合によっては使えそう。

calleeプロパティ

あまり見かけることはないけど、これも面白い。関数などの処理の中で自分自身を再帰的に呼び出すことができる。

function factorial(n) {
    if (n != 0) { return (n * arguments.callee(n - 1))};
    return 1;
}

console.log(factorial(5));
// => 120

arguments.calleefactorial関数を何度も呼び出して54321の計算をしている。ちょっとややこしいけど、使いこなせたらなんかかっこいい。

関数も関数の引数として渡せる

function arrBridge(data, f) {
    for (var key in data) {
        f(data[key]);
    }
}

function printElement(v) {
    console.log(v + 1);
}

arr = [1,2,3];
arrBridge(arr, printElement);

arrBridgeprintElementという関数を渡して、arrBridgeの中でその関数を使っています。こんな風に関数を引数に渡して使うことができてしまうんです。

クロージャ

そこまで聞くような言葉ではないかもしれないけど、場合によってはクロージャを使ってスマートに書くことができたりする。

function closure(init) {
    var counter = init;

    return function() {
        return ++counter
    }
}

var myClosure = closure(1);
console.log(myClosure());
console.log(myClosure());
console.log(myClosure());

これは、引数に与えた数字に1をプラスするプログラムだけど、結果として2,3,4という数字が出力される。同じ記述なのに、値が保持されてincrementされているのが不思議ですよね。これは、変数myClosureが返り値を関数として受け取り、そしてその関数が変数counterを保持しているから。counterの値は保持されるから何回呼び出してもそれに++された値が返るんです。使いこなすのは難しそうだよねー。

まとめ

Javascriptの基礎的な部分でもちょっと気になるような部分をまとめておいた。何もかも忘れてしまった未来の自分に役立ったらいいかな。