入れ子関数内のJavascript「this」ポインタ


93

ネストされた関数のシナリオで「this」ポインターがどのように扱われるかについて質問があります。

次のサンプルコードをWebページに挿入するとします。ネストされた関数「doSomeEffects()」を呼び出すと、エラーが発生します。Firebugをチェックインしたところ、ネストされた関数にいるとき、「this」ポインターが実際にはグローバルな「ウィンドウ」オブジェクトを指していることがわかりました。オブジェクトの関数内でネストされた関数を宣言したので、関数に対して「ローカル」スコープを持つ必要があると考えたため、私は何かを正しく理解していないはずです(つまり、「this」ポインタは次のようにオブジェクト自体を参照しています。それが私の最初の「if」ステートメントでどのようになっているか)。

任意のポインタ(しゃれは意図されていません)をいただければ幸いです。

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();

あなたの質問は正確には何ですか?
Sarfraz

2
関数内で使用される場合this、関数が呼び出されるオブジェクトを指します。
Approxiblue 2012年

8
外側のスコープでできることは次のようなものでvar self = this;あり、selfクロージャーを介して内側の関数で参照します。
Kai

1
doSomeEffects特にobjに関連付けられていないためthis、すべての要素の母であるウィンドウであると見なされます。
およそ

3
@JoJoeDad外交的にこれをどのように言うのですか?しかし、下のchuckjによって与えられた答えはあなたの質問に対する答えです。何が起こっているのかを正確に理解するには、実行コンテキストスコープチェーン、およびこのキーワードをお読みください。そして、ここでいくつかの回答の外観から、他の人もそれらを読むべきです。私は良いjavascriptを伝道しようとします。それが私がこれらのリンクを与えるために時間をかけている理由です。
onefootswill 2013年

回答:


120

JavaScriptでは、thisオブジェクトは実際には関数呼び出しの方法に基づいています。

一般に、thisオブジェクトを設定するには3つの方法があります。

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

上記のすべての例で、thisオブジェクトはになりますsomeThing。先頭の親オブジェクトなしで関数を呼び出すと、通常、ほとんどのブラウザでオブジェクトを意味するグローバルオブジェクトが取得されますwindow


this.doSomeEffects();はあなたの答えに基づいてコードを変更しましたが、それでも機能しません。どうして?
Arashsoft 2018

1
@Arashsoft thisはをthis.doSomeEffects()指していstd_objます。上記の回答で説明したように、関数にオブジェクト参照がない場合はthis、ウィンドウオブジェクトである必要があります。
Shivam

38

これは、その種の最も支持されている質問の中にあるようですので、これらすべての年の後に、矢印関数を使用したES6ソリューションを追加しましょう:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

5
これ進捗です!
Joshua Pinter 2016年

優れたシンプルなソリューション。
Joshua Schlichting

32

thisはクロージャスコープの一部ではありません。これは、呼び出しサイトでバインドされる関数への追加パラメータと考えることができます。メソッドがメソッドとして呼び出されない場合、グローバルオブジェクトはとして渡されthisます。ブラウザでは、グローバルオブジェクトはと同じですwindow。たとえば、次の関数について考えてみましょう。

function someFunction() {
}

そして次のオブジェクト、

var obj = { someFunction: someFunction };

次のようなメソッド構文を使用して関数を呼び出す場合、

obj.someFunciton();

その後thisにバインドされていますobjます。

次のようなsomeFunction()を直接呼び出すと、

someFunction();

次にthis、グローバルオブジェクトにバインドされます。window

最も一般的な回避策は、これを次のようなクロージャに取り込むことです。

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      

that = this is perfect
Nather Webber

10

エンクロージャ変数と「これ」の間には違いがあります。「this」は実際には関数の呼び出し元によって定義されますが、明示的な変数は、エンクロージャーと呼ばれる関数宣言ブロック内にそのまま残ります。以下の例をご覧ください。

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

ここで試すことができます:http : //jsfiddle.net/kSTBy/

関数で発生しているのは「doSomeEffects()」であり、明示的に呼び出されています。これは、コンテキストまたは関数の「this」がウィンドウであることを意味します。「doSomeEffects」がプロトタイプメソッドである場合、たとえばthis.doSomeEffectsが「myObject」である場合、myObject.doSomeEffects()は「this」を「myObject」にします。


4
このデモログ、怠惰とせっかちのために:_this.name = myFirstObject this.name = mySecondObject
PTIM

9

この質問を理解するには、次のスニペットの出力を取得してみてください

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

上記のコードは、以下をコンソールに出力します。

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

外側の関数では、thisとselfの両方がmyObjectを参照しているため、fooを適切に参照してアクセスできます。

ただし、内部関数では、これはmyObjectを参照しなくなりました。その結果、this.fooは内部関数で定義されていませんが、ローカル変数selfへの参照はスコープ内に残り、そこでアクセスできます。(ECMA 5以前は、内部関数のこれはグローバルウィンドウオブジェクトを参照していましたが、ECMA 5以降は、内部関数のこれは未定義になります。)


1
内部関数でthisは、ウィンドウオブジェクト(ブラウザ環境の場合)またはGLOBALオブジェクト(node.js環境の場合)を
指し

2
なぜそれが起こるのですか?
setu

@raneshuは「厳密な使用」でありthis、ウィンドウを参照していません
rofrol

3

カイルで説明したように、関数内でcallまたはapplyを使用して指定できますthis

コードに適用される概念は次のとおりです。

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle


0

私はまた、「警告だ、この経由クラスのフィールドへの潜在的に無効な参照のアクセスを

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class

0

それが言及されなかったので、私は使用.bind()が解決策であることを言及します-


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

ここにもっと簡単な例があります-

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

確かに、アロー関数を使用する=>と見栄えがよくなり、プログラマーにとって簡単になります。ただし、レキシカルスコープは、単にthisを介して関数とポインターを関連付けるよりも、レキシカルスコープをセットアップして維持するために処理とメモリの面でより多くの作業を必要とする可能性があることに注意してください.bind()

JSでクラスを開発する利点の一部は、thisより確実に存在して利用できるようにする方法を提供し、関数型プログラミングと字句スコープから離れて、オーバーヘッドを削減することでした。

MDNから

パフォーマンスの考慮事項処理速度とメモリ消費の両方の点でスクリプトのパフォーマンスに悪影響を与えるため、特定のタスクにクロージャーが不要な場合、他の関数内に不要に関数を作成することは賢明ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.