javascriptのthisを深く理解する

4322 ワード

javascriptの中のthisの意味はとても豊富で、それは全体の対象であることができて、現在のオブジェクトあるいは任意のオブジェクト、これはすべて関数の呼び出しの方式に依存します.関数には、オブジェクトメソッドとしての呼び出し、関数としての呼び出し、構造関数としての呼び出し、appyまたはcallの呼び出しがあります.
 
オブジェクトメソッドの呼び出し
 
オブジェクトメソッドとして呼び出すと、thisはオブジェクトに紐付けされます.
var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
     this.x = this.x + x; 
     this.y = this.y + y; 
     } 
 }; 

 point.moveTo(1, 1)//this        ,  point   

ここで強調したいのは、関数定義ではなく、関数実行時に対応する値を取得することです.オブジェクト方法の呼び出しであっても、この方法の関数属性が関数名として他の作用領域に入ってくると、thisの指向が変化します.例を挙げます.
var a = {
	aa : 0,
	bb : 0,
	fun : function(x,y){
		this.aa = this.aa + x;
		this.bb = this.bb + y;
	}
};
var aa = 1;
var b = {
	aa:0,
	bb:0,
	fun : function(){return this.aa;}
}	
a.fun(3,2);
document.write(a.aa);//3,this      
document.write(b.fun());//0,this      
(function(aa){//        ,          
	var c = aa();
	document.write(c);//1 ,   fun     ,  this        ,     window
})(b.fun);	
これで分かりますよね.これは紛らわしいところです.
関数の呼び出し
関数を直接呼び出すこともできます.このときには、thisはグローバルオブジェクトに結合されます.
 var x = 1;
  function test(){
    this.x = 0;
  }
  test();
  alert(x); //0
しかし、このようにいくつかの問題が発生します.つまり、関数の内部で定義された関数は、そのthisも全体を指します.私たちが望むのとは正反対です.コードは以下の通りです
var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
     //     
     var moveX = function(x) { 
     this.x = x;//this       
    }; 
    //     
    var moveY = function(y) { 
    this.y = y;//this       
    }; 

    moveX(x); 
    moveY(y); 
    } 
 }; 
 point.moveTo(1, 1); 
 point.x; //==>0 
 point.y; //==>0 
 x; //==>1 
 y; //==>1
私たちは私たちが望んでいる移動だけではなく、効果が達成されていないことを発見します.逆に二つのグローバル変数が多くなります.どう解決しますか?関数の関数に入るときにthisを変数に保存し、その変数を使うだけでいいです.コードは以下の通りです
var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
      var that = this; 
     //     
     var moveX = function(x) { 
     that.x = x; 
     }; 
     //     
     var moveY = function(y) { 
     that.y = y; 
     } 
     moveX(x); 
     moveY(y); 
     } 
 }; 
 point.moveTo(1, 1); 
 point.x; //==>1 
 point.y; //==>1
コンストラクタ呼び出し
 
javascriptで自分でコンストラクションを作成する時に、thisを利用して新しく作成したオブジェクトを指すことができます.これにより、関数のthisが全体を指すことを回避することができます.
 var x = 2;
  function test(){
    this.x = 1;
  }
  var o = new test();
  alert(x); //2
applyまたはコールコールコール
この2つの方法は、関数が実行するコンテキスト環境、すなわち、thisバインディングを変更するオブジェクトを切り替えることができる.applyとcallは似ています.パラメータが入る時の要求は配列で、一つの要求は別々に入ってきます.だから私達はappyを例にします.
<pre name="code" class="html">var name = "window";
    
var someone = {
    name: "Bob",
    showName: function(){
        alert(this.name);
    }
};

var other = {
    name: "Tom"
};   
 
someone.showName();		//Bob
someone.showName.apply();    //window
someone.showName.apply(other);    //Tom
オブジェクト中のメソッドに正常にアクセスすると、オブジェクトにthisが向けられます.applyを使用した後、applyにパラメータがない場合、thisの現在のオブジェクトはグローバルであり、applyにパラメータがある場合、thisの現在のオブジェクトはこのパラメータです.
矢印関数呼び出し
ここでは、次世代javascript標準ES 6の矢印関数のthisは常に関数定義時のthisを指し、実行時ではない.一例を通して理解します.
var o = {
    x : 1,
    func : function() { console.log(this.x) },
    test : function() {
        setTimeout(function() {
            this.func();
        }, 100);
    }
};

o.test(); // TypeError : this.func is not a function
上のコードはエラーが発生します.上記のコードを修正したいです.
var o = {
    x : 1,
    func : function() { console.log(this.x) },
    test : function() {
        var _this = this;
        setTimeout(function() {
            _this.func(); 
        }, 100);
    }
};

o.test();
外部に保存されているthisを使ってください.ここでは矢印関数を利用できます.矢印関数のthisは、実行時ではなく関数定義時のthisを常に指しています.上のコードを下記のように修正します.
var o = {
    x : 1,
    func : function() { console.log(this.x) },
    test : function() {
        setTimeout(() => { this.func() }, 100);
    }
};

o.test();
今回はthisがoを指しています.このthisは対象を変えないので、callとapplyはthisの方向を変えることができると知っていますが、矢印関数では無効です.
var x = 1,
    o = {
        x : 10,
        test : () => this.x
    };

o.test(); // 1
o.test.call(o); //    1
このようにして、様々な場合のthisバインディングオブジェクトの違いが分かります.