【JavaScript】 new演算子は何をやっている?
更新日:2020/10/22
一時期、オブジェクト作成はnewが必要ないという議論がありました。
確かに必要ありませんが、newの本質は単なるオブジェクト作成ではありません。
そこで今回は、new演算子が何をやっているのかについて解説します。
new演算子は継承の仕組みを構築している
new演算子は、コンストラクター関数のprotorypeプロパティを、新規オブジェクトのプロトタイプチェーンに組み込む処理をおこなっています。
プロトタイプチェーンは、JavaScript上での継承の仕組みです。
newが必要ないケース
コンストラクター関数のprototypeプロパティに関数独自のプロパティが用意されていない場合は、newを使用しなくても問題ありません。
例:
newを使用するパターン
function a( x ){
this.x = x;
}
const b = new a( 10 );
これを、newをしないパターンに置き換えてみます。
newを使用しないるパターン
function a( x ){
/* const obj = {}; obj.x = x; return obj; */
return { x: x }; // 上コメントの短縮パターン
}
const b = a( 10 );
newが必要なケース
コンストラクター関数のprototypeプロパティに関数独自のプロパティが用意されている場合は、newが使用です。
newでオブジェクトインスタンスを生成
function a( x ){
this.x = x;
}
a.prototype={
getX:function(){ return this.x }
};
const b = new a( 10 );
const c = b.getX(); // c = 10
上の関数aをnewを、newが必要ないケースで例示したパターンに書き換えてみます。
newを使用しないパターンに置き換え
function a( x ){
return { x: x };
}
a.prototype={
getX:function(){ return this.x }
};
const b = a( 10 );
const c = b.getX(); // TypeError: b.getX is not a function
a.prototypeが、関数aで返されたオブジェクトのプロタイプチェーンに組み込まれていないため、b.getX() を実行できません。
ただし、次のように Object.create() を使用すると、プロタイプチェーンを構築することができます。
newを使用しないで、プロタイプチェーンを構築する
function a( x ){
const obj = Object.create(a.prototype);
obj.x = x;
return obj;
}
a.prototype={
getX:function(){ return this.x }
};
const b = a( 10 );
const c = b.getX(); // c = 10
newを使用したほうが簡潔なので、この方法はおススメできません。
newがやっていること
new演算子は、次のことをおこなっています。
関数内コード実行前
- オブジェクトとして最低限の機能を持っている、新規オブジェクトを作成。
新規オブジェクト:{ }
- 新規オブジェクトのプロパティチェーンに、コンストラクター関数のprototypeプロパティへの参照をセット
新規オブジェクト:{
[[プロパティチェーン]] → コンストラクター関数のprototypeプロパティ
} - 新規オブジェクトを this として、関数内コードに渡す
関数内コード終了後
関数内コードにreturn式がない場合、this値をリターンします。
例えば、次のように最後にthis値をリターンするコードがあるとき、
this値をリターン
function a( x ){
this.x = x;
return this;
}
最後のreturn行を削除できます。
this値のリターンを省略
function a( x ){
this.x = x;
}
これは、new演算子の記述漏れ時に、不具合を発見するのに役立ちます。
newの付け忘れ
function a( x ){
this.x = x;
}
const b = a( 10 ); // b は undefined
const c = b.x; // TypeError: b is undefined
new演算子がない場合、関数aからの返り値はundefinedです。
そのため、後に続くコードでプロパティを参照しようとするとエラーとなり、処理がストップします。
this値をリターンした場合、そのまま処理が続く可能性があり、潜在的な不具合となるかもしれません。
newあるなしでの、関数内 thisの値
関数を new演算子で呼び出した場合、関数内のthisは新規オブジェクトです。
このオブジェクトは、自由にプロパティを追加しても問題ありません。
しかしnew演算子を使用しない場合、不具合が生じることがあります。
この場合のthis値は、strictモードかどうかで変わってきます。
strictモードの場合、this値は undefined です。
undefinedにプロパティを追加すると、エラーが発生します。
デバッグ時にエラーを捕捉できるようにしておけば、比較的容易に修正可能です。
非strictモードの場合、this値はブラウザならWindowsです。
つまり、this値に追加したつもりのプロパティが、グローバルオブジェクトに追加されたことになります。
これは、値の競合などがおき、気が付きにくい不具合の原因となります。
大きなプロジェクトなどでは、次のような方法で new呼び出しの判定をおこない、エラーをスローするなどの方策をするのもよいですね。
new呼び出し以外は、エラーをスロー
function a( x ){
if( new.target === undefined ) throw new Error("newで呼び出してください");
this.x = x;
}
まとめ
new演算子はオブジェクトを作成するだけではなく、継承の仕組みを構築しています。
とても重要な演算子なんです。
また、JavaScript初心者のうちはnew演算子を使用したらエラーがでる、ということも多いです。
そんなときは、次のページを参考にしてください。
■【JavaScript】 new したら コンストラクターではないと言われたなぜ?
そもそもコンストラクターって何?という人は、次の記事が参考になります。
■【JavaScript】 コンストラクターとは?関数とは違うのか?
更新日:2020/10/22
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。