MENU

JavaScript変数

【JavaScript】 undefinedの判定方法とその正体

更新日:2021/07/15

 

undefinedとは

 

undefinedは英語で『未定義』を表す言葉です。

 

JavaScriptでも同じ意味で使用され、プロパティが定義されいるかどうかの判定に使用されます。
さらに、変数が初期化されているかどうかの判定にも使用されることがあります。

 

プロパティの存在確認

 

例えば次の例では、オブジェクトobjに存在しないprop2プロパティを参照しています。
その結果、undefinedという値を返しているのがわかります。

 

存在しないプロパティを参照

 


const obj = {
    prop1:100
};

console.log( obj.prop1); // 100
console.log( obj.prop2); // undefined

 

変数の初期化確認

 

次の例は、定義をしているが値をセットしていない、つまり初期化していない変数を参照しています。
こちらの結果も、undefinedという値を返します。

 

初期化していない変数を参照

 


let value;

console.log( value ); // undefined

 

なお変数参照でundefinedを返すのは、事前に定義されている必要があります。
定義されていなかったり、文脈上後方で定義している場合は『ReferenceError』がスローされます。

 

定義されていない場合

 


console.log( value ); // ReferenceError: value is not defined

 

後方で定義

 


console.log( value ); // ReferenceError: can't access lexical declaration 'value' before initialization

let value;

 

 

補足:関数・メソッドの戻り値

 

returnの記述がない関数およびメソッドは、戻り値としてundefinedを返します。

 

つまり戻り値が『未定義』という意味です。

 

returnなし関数の戻り値

 


function func(){
    // 何らかの処理
}
console.log( func() ); // undefined

 

ただしnewを使用して実行すると関数およびメソッドはコンストラクターとして機能します。
その結果、オブジェクトを返します。

 

コンストラクターの戻り値

 


function func(){
    // 何らかの処理
}
console.log( new func() ); // Object {  }

 

コンストラクターについては、次の記事を読んでみてください。
【JavaScript】 コンストラクターとは?関数とは違うのか?

undefinedの判定方法

 

前項でも書いたように、undefinedはプロパティ定義状況および変数の初期化状況を確認するために使用することが多いです。

 

ここでは、その判定方法をお伝えします。

 

undefined値との比較(厳密等価演算)で判定

 

確認するには、単純にundefined値と比較すれば大丈夫です。
ただし比較は厳密等価演算子( === )でおこないます。

 

プロパティの存在確認

 


const obj = {
    prop1:1
};

console.log( `存在${obj.prop1 === undefined ? "しない" : "する"}`); // 存在する
console.log( `存在${obj.prop2 === undefined ? "しない" : "する"}`); // 存在しない

 

変数の初期化確認:再考の余地あり

 


let value;

console.log( `初期化${value === undefined ? "していない" : "済み"}`); // 初期化していない

 

変数の初期化確認の例は問題がありますが、後で説明します。

 

ただし、undefinedは予約語ではないので、変数名として使用できます。

そのため意図的に変更した場合、上記のようなundefinedとの比較で判定できません。

 

undefined変数の変更

 

let value;

const func = ()=>{
    const undefined = 100;
    console.log( undefined ); // 100
    console.log( `初期化${ value === undefined ? "していない" : "済み"}`); // 初期化済み
};
func();
console.log( undefined ); // undefined
console.log( `初期化${ value === undefined ? "していない" : "済み"}`); // 初期化していない

 

この場合は、グローバルプロパティのundefined、つまりglobalThis.undefinedと比較してください。

 

グローバルプロパティで比較


console.log( `初期化${ value === globalThis.undefined ? "していない" : "済み"}`);

 

なお、globalThisは古い処理系では使用できません。
古いブラウザなどでも対応させるときは、typeof演算子との比較で判定を参考にしてください。

 

変数が後方で定義されている場合

 

変数が後方で定義されている場合は、上記の方法ではエラーとなってしまいます。
そこで、try...catchで例外を補足します。

 

後方で定義

 


try{
    console.log( value );
}catch(e){
    console.log( "未定義です" ); // 未定義です
}
let value;
console.log( value ); // undefined

 

ただし後方での定義は、コード上の欠陥であることがほとんどです。
そのため定義を前方に移動することが、最適解です。
つまり、『変数の初期化確認』のコードになります。

 

しかし、そのコードもおススメできません。

変数は定義時に初期化しておき、初期化しているかどうかの確認をおこなわないコードを作成するべきです。

 

変数の初期化は確実におこなう

 


let value = null;

console.log( `初期化${value === undefined ? "していない" : "済み"}`); // 初期化済み(無意味な判定)

 

 

typeof演算子との比較で判定

 

typeof演算子での判定は古いJavaScriptコードでよく見られる手法です。

 

undefined値に対してtypeof演算子を適用すると、文字列"undefined"を返します。
その値をチェックすることで、判定をおこないます。

 

プロパティの存在確認

 


const obj = {
    prop1:1
};
console.log( `存在${typeof obj.prop2 === "undefined" ? "しない" : "する"}`); // 存在しない

 

変数の初期化確認

 


let value;
console.log( `初期化${typeof value === "undefined" ? "していない" : "済み"}`); // 存在しない

 

 

前項のundefined値との厳密等価演算比較と比べると、迂遠に感じますね。
この手法が有効とされた背景には、グローバルとしてのundefinedを他の値に変更できたという理由がありました。
現在は変更できませんので、気にせず厳密等価演算比較できます。

 

なお前項でもふれていますが、undefined予約語でないため変数として定義できます。

 

前項では対応策としてglobalThis.undefinedと比較していますが、globalThis未対応の処理系では使用できない方法でした。

 

仕方がないので、typeof演算子を使用します。

 

globalThis未実装でのundefined判定

 


let value;

const func = ()=>{
    const undefined = 100;
    console.log( undefined ); // 100
    console.log( `初期化${ typeof value === "undefined" ? "していない" : "済み"}`); // 初期化済み
};
func();

undefinedの二つの意味

 

ここまで何回か書いていますが、undefinedはプロパティが未定義がということと、変数が初期化されていないということをあらわすことが多いです。

 

この二つの違いについて、考えてみます。

 

プロパティ参照とundefined

 

次のようなオブジェクトがあるとします。

 


const obj = {
    prop1 : 1000,
    prop2 : "abc",
    prop3: undefined,
};

 

そして次のようなコードを実行します。

 

const value = obj.prop1;

 

すると内部で、objのプロパティ名が順番に検索されます。
そして一致したprop1の値、1000が変数valueに代入されます。

 

では、次のコードを考えてみます。

 

const value = obj.prop4;

 

こちらも実行すると、objのプロパティ名が順番に検索されます。
しかしprop4と一致するプロパティがありません。
そこで結果として”その値は存在していない”という意味のundefinedが返り、valueに代入されます。

 

では、次のコードを考えてみます。

 

const value = obj.prop3;

 

prop3は存在していて値はundefinedです。
そのままvalueに代入されます。

 

つまりプロパティにアクセスすると、次の二つのパターンでundefinedが返ってきます。

 

  1. プロパティが存在しない
  2. プロパティ値がundefined

 

実はここまで紹介していたのは値がundefinedかどうか判定する方法であり、プロパティが存在するかどうかではありませんでした。

 

プロパティの存在確認は、次のhasOwnPropertyメソッドを使用します。

 

プロパティの存在確認

 


console.log( `存在${!obj.hasOwnProperty("prop3") ? "しない" : "する"}`); // 存在する
console.log( `存在${!obj.hasOwnProperty("prop4") ? "しない" : "する"}`); // 存在しない

 

 

変数初期化とundefined

 

変数はブロック毎にまとめて用意され、定義コード処理時に初期化されます。

 

次のようなコードで解説します。

 


function func(){

      // 何らかの処理

    let value1;
    let value2 = 1000;
    const value3 = "abc";
}

 

関数funcは、JavaScriptエンジンがコードを読み込んだ時に解析され、変数の一覧が作成されています。

 

関数funcが実行されると変数一覧から変数が実体化されます。
この時点では変数に値が結びつけられていません。
そしてこの状態で変数を参照すると、『ReferenceError』がスローされます。

 

関数の処理が進み、let value1;のコードに到達すると、変数value1にundefined値がセットされます。
つまり、undefined値で初期化されたわけです。

 

これまで変数とundefined値を比較することで、未初期化かどうか判断していました。
しかし実際は、『アルゴリズム上で初期化されていない』という意味だったのです。

 

なおプロパティと同様に、コード上で変数にundefined値をセットできます。
これとJavaScriptエンジンがセットしたundefined値を見分ける方法はありません。

undefined値とグローバルプロパティ

 

JavaScriptには複数のデータ値が定義されています。

 

例えば、1,2,3などの数値は数値データです。
"abc","あいうえお"などの文字列は、文字列データです。

 

これらと同じように、undefinedという値を表すundefined値があります。

 

では次のコードのundefinedは、何でしょうか。

 

undefinedは何?

 


let value;
if( value === 

undefined

) { }

 

これは、undefined値ではなくて、グローバルオブジェクトのプロパティです。

 

リテラルとデータ値

 

プログラムコード上でデータを表す文字をリテラルと呼びます。

 

リテラル

 


const value1 = 123;
const value2 = "あいうえお";

 

上の例で123は数値リテラルです。
この行が評価されると、メモリ上に123という数値が格納されます。

 

"あいうえお"は文字列リテラルです。
同様に、メモリ上に"あいうえお"という文字列が格納されます。

 

しかし、次のコードはundefinedリテラルではありません。

 

undefinedリテラル?

 


const value3 = undefined;

 

最初に書きましたが、グローバルオブジェクトのプロパティです。

 

グローバルオブジェクトとは

 

JavaScriptにはグローバルオブジェクトという最上位のオブジェクトが存在します。
このオブジェクトのプロパティは、オブジェクト名なしで呼び出すことができます。

 

例えば組み込みオブジェクトのMathは、グローバルオブジェクトのプロパティです。
そのため、Mathのメソッドを直接呼び出すことができます。

 

グローバルオブジェクトと組み込みオブジェクト

 


console.log( Math === globalThis.Math ); // グローバルオブジェクトと同値

console.log( Math.floor( 123.456 ) ); // 組み込みオブジェクトのメソッドを呼び出す

 

■globalThisとは?

 

グローバルオブジェクトの名前は、処理系によって異なります。
例えばブラウザではwindowで、 Node.jsではglobalです。
globalThisは処理系によって異なるグローバルオブジェクトに、同一の名前でアクセスできるようにしたショートカットです。

 

グローバルオブジェクトのundefinedプロパティ

 

グローバルオブジェクトには、undefinedという名前のプロパティが用意されていて、参照されるとundefined値を返します。

 

このプロパティも他のグローバルオブジェクトのプロパティと同様に、直接呼び出すことができます。

 

同じundefinedプロパティを参照

 


console.log( globalThis.undefined ); // undefined
console.log( undefined ); // undefined

 

つまり、プログラムコード上のundefinedはデータ値ではなくて、グローバルオブジェクトのプロパティが参照されるのです。

 

undefinedプロパティは上書き不可

 

グローバルオブジェクトのundefinedプロパティは上書き禁止属性がセットされています。
そのため上書きできません。

 

非strictモードで上書きすると、無視されます。

 

非strictモードでのundefined上書き

 


undefined = 1000;
console.log( undefined ); // undefined

 

strictモードでは、TypeErrorが発生します。

 

strictモードでのundefined上書き

 

"use strict";
undefined = 1000; //  TypeError: "undefined" is read-only
console.log( undefined ); // ←到達しない

 

古いJavaScriptでは、グローバルオブジェクトのundefinedプロパティを上書きすることができました。
そのため、undefinedプロパティがundefined値を持つことが保証されていませんでした。

 

このことから、undefinedプロパティを比較対象に使用することが推奨されていませんでした。

 

古いJavaScript

 


undefined = 1000;
console.log( undefined ); // 1000←上書きできた

var obj = { prop1:100 };

if( obj.prop2 === undefined ) { // undefined値ではなくて、1000と比較される
}

 

現在は上書き不可となっているので、気にする必要はありません。

undefinedイコール未定義ではない

 

JavaScriptのundefinedは、『未定義』を表現することを目的として用意されています。

 

しかしundefinedはあくまでもデータ値であり、必ずしも未定義を意味するものではありません。

 

仕様上、オブジェクトに存在しないプロパティを参照するとundefinedを返します。
関数で戻り値を指定しないと、undefinedを返します。

 

これらはあくまで、そのような取り決めになっているだけです。

 

例えば次のような文字列判定の関数があるとします。

 

文字列判定関数

 


function isString( str ){
    return ( typeof str === "string" ) ? 0 : 1;
}

 

この関数は引数が文字列なら0を、文字列でないなら1を返します。
普通ならtrueかfalseですが、ここではそのように取り決めてあります。

 

trueかfalseなら、3項演算子を使用する必要がありません。
return typeof str === "string";

 

ここで、0や1の本来の意味に、『結果が文字列かどうか』の意味があるでしょうか。
ありませんね。0はゼロ、1はイチの意味しかありません。

 

undefinedも同じです。
プロパティが存在しないときにundefinedを返すと取り決めてあるだけで、undefinedに未定義の意味があるわけではありません。

 

重要なのは、undefinedの意味を議論することではありません。
操作の結果どのような値が返ってくるかを把握することです。

 

最後にもう一度言います。
undefinedは、プロパティが存在しないときなどに返ってくる値です。
undefinedそのものには意味などありません。

 

と思うのだけど、どうだろうか・・・

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    期間限定情報:
    7/16から7/18は63時間のビッグセール!
    欲しかったアレが安く手に入るチャンスです
    忘れずにチェックしてください!
    僕は以前のタイムセール祭りで4Kモニタが買ったけど、それより安かったらどうしよう・・・

    さらにお得なポイントアップキャンペーンも同時開催!

    記事の内容について

     

    こんにちはけーちゃんです。
    説明するのって難しいですね。


    「なんか言ってることおかしくない?」
    たぶん、こんなご意見あると思います。

    裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

    そんなときは、ご意見もらえたら嬉しいです。

    ご意見はこちら。
    https://note.affi-sapo-sv.com/info.php

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。