uhyohyo.net

JavaScript初級者から中級者になろう

二章第十四回 nullとundefined

今回はDOMから少し離れて、nullundefinedについて解説します。これらはオブジェクトではなくプリミティブ値で、文字列、数値、真偽値に続く、第四・第五の値です。

「true」「false」と書くとその名の変数ではなくプリミティブである真偽値を表すのと同様に、「null」「undefined」と書くとそれぞれnull,undefinedという個別の値を表します(実は厳密には少し異なり、undefinedだけは書き換えできない変数という扱いになっていますが)。

null

意味的には、nullは「何もない」ということを意味します。「何もないということを教えるための値」ともいうことができます。

例えば、nullは次のような時に出現します。

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>test</p>

    <script type="text/javascript">
      var nodelist = document.getElementsByTagName('p');

      console.log(nodelist.item(100));
    </script>
  </body>
</html>
        

「null」と表示されます。

このサンプルでやっていることは、まずp要素のNodeListを変数nodelistに代入し、その「100番目」を表示しようとしています。

しかし、このサンプルにはp要素はひとつしかありません。当然、100番目なんてあるわけがありません。つまり、「存在しない」ということになります。

こういうときにnullが活躍します。このように存在しない場合、itemメソッドはnullを返します。これは、NodeListでもHTMLCollectionでも同じです。

他にもさまざまな場面で出てきます。例えば、getElementByIdで該当するidを持つ要素が無かったときもnullを返しますし、各種フォームコントロールが持つformプロパティで、そのコントロールがform要素に所属していなかった場合もnullです。

他に、次のような場合もあります。

var newelement = document.createElement('p');

console.log(newelement.parentNode);
          

これは、createElementで新しくp要素を作り、それのparentNode、つまり親要素を表示しています。

しかし、createElementで作った要素は、まだ木構造には加わっていないのでした。つまり、親が無いということになります。こういう場合にもnullが返るのです。

さらに、firstChildやlastChild(二章第四回)も、子ノードが1つもない場合は存在しないのでnullです。previousSiblingやnextSibling(二章第7回)も、存在しない場合があり、そういう場合nullです。

また、自分からnullを使う場合もあります。例えば、insertBefore(二章第六回)では、第二引数にノードの代わりにnullを渡すことができます。こうすると、そのノードは一番最後に追加されます。普通にappendChildでいいような気もしますが、この仕様は次のような場合に役に立ちます。

<!doctype html>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>test</p>
    <script type="text/javascript">
      var newp = document.createElement('p');

      document.body.insertBefore(newp, document.body.firstChild);
    </script>
  </body>
</html>
        

p要素を作り、それをinsertBeforeでbody要素に追加しているだけです。

第二引数がdocument.bodyのfirstChild、つまり最初の子ノードです。それよりも前にだから、つまりは先頭に追加しようとしていることがわかります。

ここで、もしbodyに子ノードが無かったらどうでしょう。firstChildは上で解説した通りnullです。ここで、insertBeforeの第二引数が、「nullだったらどこに追加したら分からないから追加しないよ」のような仕様だった場合、結局追加されずに終わってしまいます。

ところが、この仕様があることで一番最後に追加され(もともと空っぽだったのでそれは一番最初でもあります)、もし子ノードがなくても思い通りに追加されることが保証されるわけです。

undefined

undefinedは、直訳すると「定義されていない」という意味です。これは、定義する必要がない、つまり、普通触れられない、触れることを想定されていないというものです。

例えば、

var a = {};
console.log(a.aaa);

という場合があります。aという空のオブジェクトをつくり、そのaaaというプロパティをいきなり表示しています。しかし何も入っていませんね。このような場合、undefinedが入っていることになります。「undefined」と表示されるのが分かると思います。

また、次のような場合もundefinedが出てきます。

function a(){
}
var b = a();
console.log(b);
          

何もしないaという関数をつくり、bにその戻り値を代入しています。しかし、関数aはreturnで何か値を返しているわけではありません。このように、何も返さない関数は、実はundefinedを返しています。return;のように、return文で返す値を省略した場合も同じです。

if文

さて、今までif文には、真偽値を渡してきました。例えば、

if(a == 3){
〜
}

という場合、a == 3はtrueかfalseを返します。それがifに渡され、trueなら真、falseなら偽というものでした。

では、ifに真偽値以外の値を渡したらどうなるのでしょう。

if(10){
    console.log("真");
}else{
    console.log("偽");
}

この場合、「真」が表示されるので、10という数値は真であることが分かります。他にもいろいろ試してみましょう。

実は、ifに渡すと偽になる値は、限られています。0と、空文字列(つまり"")、falsenullundefinedなどです。

これ以外の値は、全て真です。また、オブジェクトも全て真です。

これらは、次のような場合に役立ちます。

if(node.firstChild){
    処理
}

(ここで、変数nodeには、何かノードのオブジェクトが代入されているものとします。)このコードでは、nodeが子ノードを持っているかどうかを判定しています。しかし、このような判定にはhasChildNodes(二章第四回)を使うはずでした。もちろんそれでもいいですが、この方法でも問題なく判定できます。

なぜなら、ノードが1つでも子ノードを持っていれば、「最初の子ノード」が必ずあるため、node.firstChildにはノードのオブジェクトが代入されています。つまり、真となります。

対して、子ノードがひとつもない場合はnullです。nullは偽だから、子ノードが1つもない場合は偽ということになります。