【ITニュース解説】Day 6 of Complete JavaScript in 17 days | Visual Series📚✨
2025年09月24日に「Dev.to」が公開したITニュース「Day 6 of Complete JavaScript in 17 days | Visual Series📚✨」について初心者にもわかりやすく解説しています。
ITニュース概要
JavaScriptのNormal FunctionとArrow Functionでは、`this`の参照先が異なる。Normal Functionの`this`は呼び出し元のオブジェクトを指すが、Arrow Functionの`this`は親スコープを指す。この違いは面接でよく問われ、MongooseモデルのメソッドでArrow Functionを使うと`this`が正しく参照できず、不具合の原因となるため注意が必要だ。
ITニュース解説
JavaScriptを学ぶ上で、関数の種類とそれぞれの挙動の違いを理解することは非常に重要だ。特に「アロー関数」と「通常の関数」の区別は、単なる書き方の違いにとどまらず、プログラムの動作に大きな影響を与える。この違い、特にthisという特別なキーワードの振る舞いは、多くの初心者が混乱しやすいポイントであり、同時にプログラミングスキルの理解度を測る面接でも頻繁に問われるため、しっかりと把握しておく必要がある。
まず、JavaScriptにおけるthisが何を指すのかを理解しよう。thisは、関数がどのように呼び出されたかによって、参照するオブジェクトが変わる特性を持つ。これは多くのプログラミング言語とは異なるJavaScript特有の挙動であり、その柔軟性ゆえに注意が必要だ。
通常の関数、つまりfunctionキーワードを使って定義する関数では、thisは基本的にその関数を「呼び出したオブジェクト」を参照する。例えば、objという名前を持つオブジェクトがあり、その中にsayHiという通常の関数で定義されたメソッドがあると仮定する。このsayHiメソッドの中でconsole.log("Hi", this.name);と書くと、this.nameは正しくobjオブジェクトのnameプロパティを参照し、「Hi Azaan」と表示される。これは、obj.sayHi()という呼び出し方によって、sayHi関数がobjオブジェクトのコンテキストで実行されたためだ。
一方で、アロー関数(=>を使って定義される関数で、「ファットアロー関数」とも呼ばれる)の場合、thisの振る舞いは通常の関数と大きく異なる。アロー関数は、それ自身では独自のthisを「持たない」。代わりに、アロー関数内のthisは、そのアロー関数が定義された「親スコープ」(レキシカルスコープ)のthisを参照する。言い換えれば、アロー関数が書かれている場所の外側のthisが何を参照しているかをそのまま引き継ぐのだ。同じobjオブジェクトにgreetというアロー関数で定義されたメソッドを考えてみよう。このgreetメソッドの中でconsole.log("Hello", this.name);と書くと、期待に反してthis.nameはundefinedと表示されてしまう。これは、greetがアロー関数であるため、独自のthisを持たず、objオブジェクトのスコープではなく、そのさらに外側、つまりグローバルスコープのthisを参照しようとするからだ。グローバルオブジェクトにはnameプロパティが存在しないため、結果はundefinedとなる。
このthisの振る舞いの違いは、実際のアプリケーション開発において予期せぬバグの原因となることがある。特に、オブジェクトのメソッドとして関数を定義する際に、どちらの関数形式を選択するかは慎重に判断する必要がある。この理解は、面接で技術的な深さを問われる際にも重要となるだろう。
具体的な開発現場での例として、ユーザー認証機能におけるパスワード比較メソッドの実装を見てみよう。MongooseというNode.jsでMongoDBを扱うためのライブラリでは、モデルのスキーマにメソッドを追加して、特定のドキュメントに関連する操作を定義できる。パスワードを比較するcomparePasswordメソッドをスキーマに追加する場合、このメソッド内でユーザーのハッシュ化されたパスワード(this.password)と入力されたパスワードを比較する必要がある。
もしここで誤ってアロー関数を使ってcomparePasswordメソッドを定義してしまうと、問題が発生する。
1// ❌ 間違い: 'this'がMongooseドキュメントを参照しない 2userSchema.methods.comparePassword = (inputPassword) => { 3 return bcrypt.compare(inputPassword, this.password); 4};
このコードでは、comparePasswordがアロー関数であるため、そのthisはユーザーのMongooseドキュメントを参照しない。アロー関数は親スコープのthisを引き継ぐため、このコンテキストではthisがMongooseドキュメントとは異なる何か(例えばグローバルオブジェクトなど)を指してしまい、this.passwordはundefinedとなる。結果として、正しいパスワードが入力されても「Invalid Password」と表示され続ける、というデバッグが困難なバグを引き起こすことになる。
この問題を解決するには、comparePasswordメソッドを通常の関数として定義する必要がある。
1// ✅ 正しい: 'this'がMongooseドキュメントを参照する 2userSchema.methods.comparePassword = function (inputPassword) { 3 return bcrypt.compare(inputPassword, this.password); 4};
このように通常の関数で定義することで、comparePasswordメソッドがMongooseドキュメントのコンテキストで呼び出されたときに、そのthisは正しくそのMongooseドキュメント自身を参照するようになる。これにより、this.passwordはユーザーのハッシュ化されたパスワードにアクセスでき、入力されたパスワードとの比較が正しく行われるようになるのだ。
アロー関数はコードを簡潔に書けるというメリットがあり、独自のthisを必要としないコールバック関数などでは非常に便利だ。しかし、オブジェクトのメソッドとしてthisを使用する場合や、特定のコンテキストでthisが特定のオブジェクトを指す必要がある場合には、通常の関数を選ぶことが重要だ。
JavaScriptにおけるthisの概念、そしてアロー関数と通常の関数におけるその振る舞いの違いを正確に理解することは、システムエンジニアとして堅牢で予測可能なアプリケーションを開発する上で不可欠な知識である。この違いを意識して関数を使い分けることで、意図しないバグを防ぎ、より効率的なデバッグと信頼性の高いコード作成に繋がるだろう。