QA@IT

JavaScriptのコンストラクタでthisをつけて初期化された変数のスコープ

4629 PV

JavaScript初心者です。

これまでJavaやC++、Objective-Cには触れたことがあったので、その感覚からGoogleChromeで以下のようなコードを試しました。

function A(){
    this.value = 100;
}
A.prototype = {
    getValue:
        function (){
            return this.value;
        },
    getGetValue:
        function (){
            return this.getValue;
        }
}

var a = new A();
console.log(a.getValue());
console.log(a.getGetValue()());
console.log(a.getValue === a.getGetValue());

私の予想では、このコードを実行すると

100
100
true

と出力されると考えていたのですが、実際には

100
undefined
true

と出力されました。
まだa.getValue === a.getGetValue()の結果がfalseなのであれば、undefinedとなる理由の検討のつけようもあるのですが、
同じオブジェクトを参照しているにもかかわらず、this.valueの値が異なるというのは、感覚的に不思議でなりません。

return this.getValue;で返されるオブジェクトがオブジェクトaのメンバではなく、孤立したオブジェクトであるという可能性も考えましたが、その考えだと、そもそもなぜ新たに領域を確保してオブジェクトをコピーしたわけでもないのに、thisの参照先が変更されているのかが不明です。

JavaScriptはほとんど触れたことがないために、てんで的はずれなことを言っているだけなのかもしれませんが、なぜこのような結果になるのか、ご教授いただければ幸いです。
どうぞ、よろしくお願いいたします。

回答

thisについて理解するにはなかなか良い問題ですね。
ただ、getGetValueはちょっとわかりにくいので、もう少し簡単なコードにしてみましょう。

function A(){
    this.value = 100;
}
A.prototype = {
    getValue:
        function (){
            return this.value;
        }
}

var a = new A();
var f = a.getValue;
console.log(a.getValue());
console.log(f());
console.log(a.getValue === f);

これは、質問のコードと全く同じ結果になります。
質問を整理すると、なぜあるオブジェクトのメソッドを適当な変数に入れてから呼び出すと、thisがかわってしまうのか、ということだと思います。
実は、JavaScriptのthisというのは(原則的には)関数が呼び出された時に決定します(一部例外もありますがここでは無視します)。
JavaScriptのthisというのは、引数の一種であると考えてしまっても問題ありません。
なので、JavaScriptにおいては関数の呼び出し方が変われば、thisが変わるのはごく当たり前なことです。
手前ですが、もう少し詳しい解説を以前書いたことがあるので、参考になれば。 http://gihyo.jp/dev/serial/01/crossbrowser-javascript/0016

編集 履歴 (0)
  • os0xさん、ご回答ありがとうございます。
    なるほど、関数が呼び出された時にthisが決定するとは、思っても見ませんでした。URLを貼ってくださった記事でthisの指定方法もわかりましたので、非常に助かりました。JavaScriptはこの、慣習よりも全体に貫かれたパラダイムを徹底する感じが、なかなか楽しい言語ですね。もっとコードを書いていこうと思います。 本当にありがとうございました。
    -

console.log(a.getGetValue()());
の部分の関数呼び出しで、すでに回答がある通り、
a.getGetValue()
が関数オブジェクトになるので、それに単に()を付けて呼び出すと、windowをthisとして関数が呼ばれます。

意図通りにするには、this==aを明示して呼び出せばいいです。
console.log(a.getGetValue().apply(a));

編集 履歴 (0)

a.getValueでのthisはaを参照しているという感覚でいいですよ
よってa.getGetValue()でreturnされたgetValueではその結びつきがないのです
わかりにくいのであればbindを使ってthisを固定することもできます

編集 履歴 (0)
  • なるほど。callやapply以外にも、bindで固定するという方法があるのですね。それは知りませんでした。 ご回答、ありがとうございます。 -

これ ↓ がなんか変じゃないですか?

console.log(a.getGetValue()());

編集 履歴 (0)
  • SurferOnWwwさん、ご回答ありがとうございます。
    文法の話でしたら、
    return this.value;

    return 200;
    とすれば、コンソールには
    200
    200
    true
    と表示されましたので、問題ないと思うのですが、そういう意味でしょうか。
    -

なんでカッコが二つあるのですかと言っているのですが。

編集 履歴 (0)
  • 1つめのカッコはa.getGetValueの引数で、2つめのカッコはa.getGetValueによってreturnされる関数の引数です。(a.getGetValue())()と書いた方がわかりやすいかもしれません。 先ほどのご回答へのコメントは、確かにその通りに動作していることが確認できる、という意味でした。 わかりにくい投稿、失礼いたしました。 -
  • 意図がわかりました。勘違いして変な質問をしてしまいました。すみません。

    a.getGetValue() は function () { return this.value; } になるが、a.getGetValue()() で呼び出すと this に代入されるのは window オブジェクトになって、this.value が undefined になるということですね。
    -
  • そうですね。みなさんのご回答のお陰で、理解することができました。ありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。