1. 程式人生 > >關於JS類中this的指向問題

關於JS類中this的指向問題

JS是指令碼語言,相對於其他像是JAVA,C等語言還不夠成熟,作用域也就沒有其他語言那麼完善,所以this的問題在開發中,特別是面向物件開發時顯得尤為關鍵,而且極易弄混,下面來談談this在不同的位置有著什麼不同的指向

  • 全域性中的this

console.log(this);//window
        function abc() {
            console.log(this);//window
        }
        abc();

全域性中的this,都指向window

  • 事件中的this

var div=document.createElement("div");
        document.body.appendChild(div);
        div.addEventListener("click",clickHandler);
        function clickHandler(e) {
            console.log(this);//div  是e.currentTarget,被偵聽的物件
        }

什麼是事件,當前這個函式,在addEventListener中被傳入第二個引數,當前這個函式有且僅有一個引數,是e,並且這個e是基於event的物件在事件中,this永遠指向的是e.currentTarget,就是被偵聽的物件

  • 混入的this

function getsum(num) {
            this.a+=num;
        }
var obj={a:0};
var obj1={a:10};
var obj2={a:20};
getsum.call(obj,10);//a=10
getsum.apply(obj1,[10]);//a=20
getsum.bind(obj2)(10);//a=30;

混入的this,就是使用call、apply和bind方法,代替掉原有函式中的this,所以混入的this,就是代表這個混入的物件

  • 物件中的this

var obj3={
            a:10,
            c:function () {
//                this--->obj3
                console.log(this.a);
            }
        }

物件中的this也很明顯,就是指的這個物件

  • 類中的this

ES6:

class Box{
            constructor(){
                this.num=3;
            }
            play(){
//                this--->obj5 也就是通過new例項化的物件
//                console.log(this.num);
                console.log(this===obj5);
            }
        }
        let obj5=new Box();
        obj5.play();
        let obj6=new Box();
        obj6.play();

ES5:

function Box() {
            this.num=3;
        }
        Box.prototype={
            play:function () {
                console.log(this.num);
            }
        };
        var obj7=new Box();
        obj7.play();//this就是obj7

無論是在ES5還是ES6中,類中的this都是指向的這個類,也可以說是指向的通過類例項化的物件

  • 混合模式中的this(重點)

 

看下面的程式碼就知道,這是一個最簡單的混合模式的類了,類中含有點選事件,也就是說點選事件中的this和類中的this會有混淆。在類中,也就是在類的建構函式和原型物件的一般方法中,this都是指向Ball,在方法中呼叫類的屬性和方法時,都要在前面加上this.才能引用,比如你要使用別的方法,就要用this.clickHandler才行,這時的this就是指向Ball,但是下面就有一個點選函數了,在這個函式中是不能直接呼叫到Ball類的,只能呼叫到他偵聽的物件,這個區域性的div,這時候要想要呼叫到Ball,使用Ball的方法和屬性,就必須要曲線救國,下面提供兩種常見的方法

第一種:

      function Ball() {
    
      }
      Ball.prototype={
            num:5,
            clickBind:null,
            createBall:function () {
                var div=document.createElement("div");
                document.body.appendChild(div);
                div.style.width="50px";
                div.style.height="50px";
                div.style.backgroundColor="red";
                this.clickBind=this.clickHandler.bind(this);
                div.addEventListener("click",this.clickBind);
                return div;
            },
            clickHandler:function (e) {
               this.num++;
                console.log(this.num);
               if(this.num>=8){
                    e.currentTarget.removeEventListener("click",this.clickBind);
               }
            }
        };

第一種方法的原理就是混入,用bind混入,原因是bind不會自己執行函式,把原先的點選函式的this混入成這個類,那麼在點選函式中的this就不再指向原先的div了,就指向混入的物件,也就是這個Ball了。

第二種:

function Ball() {

} 
Ball.prototype={
            num:5,
            createBall:function () {
                var div=document.createElement("div");
                document.body.appendChild(div);
                div.style.width="50px";
                div.style.height="50px";
                div.style.backgroundColor="red";
                div.self=this;
                div.addEventListener("click",this.clickHandler);
                return div;
            },
            clickHandler:function (e) {
                this.self.num++;
                console.log(this.self.num);
                if(this.self.num>=8){
                    this.removeEventListener("click",this.self.clickHandler);
                }
                console.log("aaa");
            }
        };
        var ball=new Ball();
        ball.createBall();

第二種方法是最常使用的方法,原本點選事件中的this指向div,這個保持不變,給div新增一個新的屬性self(可隨意定),讓這個屬性的值為this,這個this是在類的方法中,所以這個this指的就是Ball,也就說div的屬性self就是Ball,這樣在點選事件函式中不能直接呼叫Ball,但是可以通過呼叫div.self也就是在事件函式中的this.self來呼叫到Ball了。