分享

【原】理解javascript中的this

 ipilipala 2016-11-21

  最近的文章基本都是總結javascript基礎內容的,因為我覺得這些東西很重要。而且很多時候你覺得你理解了,其實並沒有你自認為的那麼理解。十月份沒怎麼寫文章,因為國慶出去玩的比較久,心變野了,現在是時候重新回到學習的軌跡了,今天要總結的是javascript中的this.

 

介紹this之前,先來兩句話,這兩句話比較重要,可以說貫穿全文。

1、this的最終指向的是那個調用它的對象

2、如果一個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象

 

下面通過一些代碼示例來講解this

1、window中的this

 

demo1

複製代碼
function test1(){
    this.x = 1;
    console.log(this.x);
  }
test1();  // 1

function test2(){
    var y = 2;
    console.log(this.y); //undefined
    console.log(this);  //Window
}
test2();
複製代碼

 上面的兩個例子中,都是屬於屬於全局性調用,因此this就代表全局對象。也就是為什麼test2打印出來的this.y會是undefined,因為this指向了 window.

 

2、在對象中使用this 

 

demo2

複製代碼
var x=0;
var obj={
    x:1,
    fn:function(){
        console.log(this.x);
    }
}
obj.fn();  // 1
複製代碼

這裡的this指向對象obj,因為調用的fn是通過obj.fn()執行的。

不過這裡有個注意點,就是this的指向在函數創建的時候是決定不了的,在調用的時候才能決定,誰調用的就指向誰,這個是比較重要的一點。為了驗證這句話,稍微改造一下上面的demo2

 

demo3

複製代碼
var x=0;
var obj={
    x:1,
    fn:function(){
        console.log(this.x);
    }
}
obj.fn();  // 1  跟上面的代碼一樣

// 修改了這裡
var obj2=obj.fn;
obj2();  //0
複製代碼

demo3中和demo2的不同之處在於,沒有直接調用obj裡面的fn函數,而是將其賦值給obj2,最終通過obj2來調用fn函數。

而obj2屬於全局變量,因為fn裡面的this指向了 window。這個也驗證了上面那句話: this的指向在函數創建的時候是決定不了的,在調用的時候才能決定,誰調用的就指向誰

 

demo4

複製代碼
var obj = {
    x:1,
    y:{
        fn:function(){
            console.log(this.x); //undefined
        }
    }
}
obj.y.fn();
複製代碼

如果一個對象中又包含有其他對象,this僅僅會指向它的上一級對象。
就像demo4中,fn裡面調用this.x,這個this只會指向fn的上一級對象y。y中沒有x這個變量,所以返回的是undefined。

 

稍微改造一下demo4,再次來驗證一下:this的指向在函數創建的時候是決定不了的,在調用的時候才能決定,誰調用的就指向誰

demo5

複製代碼
var obj = {
    x:1,
    y:{
        x:2,
        fn:function(){
            console.log(this.x); //undefined
        }
    }
}
var obj2=obj.y.fn;
obj2();  // 因為此時fn中的this指向了window
複製代碼

 

3、在構造函數中使用this

demo6

function Fn(){
    this.x=1;
}
var myFn=new Fn();
console.log(myFn.x); //1

如果函數傾向於和 new 關鍵詞一塊使用,則我們稱這個函數是構造函數, 在函數內部,this 指向新創建的對象。也就是說,這個this不是指向函數Fn,而是指向它的實例 myFn。

 

4、call和apply,改變this指向(這裡順便把call和apply給介紹了

 

在 javascript 中,call 和 apply 都是為了改變某個函數運行時的上下文(context)而存在的,也就是說,是為了改變函數體內部 this 的指向。

call和apply的區別就在於傳遞方式的不同,call在接收指定參數的形式是 someMethod.call(obj, arg1, arg2);

而apply在接收指定參數時的形式是 someMethod.apply(obj, [arg1, arg2]).或者someMethod.apply(obj, arg1),但是這個arg1必須是一個類數組對象

 

demo7

複製代碼
var personA = {
    name: 'kobe',
    sayName: function (hobby){
        console.log(this.name + ' likes ' + hobby);
    } 
};

personA.sayName('basketball');   // 'kobe likes basketball'

var personB = {

    name: 'James'
}

personA.sayName.call(personB, 'basketball');  // 'James likes basketball'
personA.sayName.apply(personB, ['basketball']); // 'James likes basketball'
複製代碼

personA.sayName('basketball'); 這段代碼中,調用sayName()這個方法的對象是personA,因此sayName()內部的this指向就是personA對象。
personA.sayName.call(personB, 'basketball'); 本來sayName方法的this指向是personA對象,但是調用call/apply後,this對象指向了personB對象。


也可以這麼理解,personA.sayName.call(personB, 'basketball')其實就是 personB.sayName(『basketball');



demo8
複製代碼
function PersonA(name,hobby) {
   this.name = name,
   this.hobby = hobby,
   this.say = function() {
        console.log(name +" likes " + this.hobby + '.');
   }
}
function PersonB(name,hobby) {
   PersonA.call(this,name,hobby);
}

var Fn=new PersonB('James','basketball');
Fn.say(); // James likes basketball.
複製代碼

雖然我們沒有在PersonB對象裡添加任何屬性和方法,但是我們使用call()繼承了原本屬於PersonA 的屬性和方法。就可以做到 PersonA 函數所有能做到的事情。

這裡額外再穿插一下appy的使用一些場景

 

apply應用場景1:數組合併

複製代碼
var list1 = [0,1,2];
var list2 = [3,4,5];
[].push.apply(list1,list2);

//或者 Array.prototype.push.apply(list1, list2); 

console.log(list1);//  [0,1,2,3,4,5]
複製代碼

 

apply應用場景2:獲取數組中的最大值或者最小值

var arr = [0,1,2,15,6];
var getMax=Math.max.apply(this,arr);
console.log(getMax); //15

 

5、使用this的注意點

 

這裡主要介紹 setTimeout或者setInterval中使用this的注意點

demo9

複製代碼
var name='james';
var person={
  name:'koBe',
  sayName:function(){
     setTimeout(function(){
        console.log(this.name);
     },0);
  }
}
person.sayName();  // james
複製代碼

因為setTimeout()這個異步函數調用的時候,內部的回調函數this的指向是window.剛好window中有一個 name為 james的標量。所以輸出james.

 

那如何將setTimeout中的this指向 person呢。兩種方式:

方法一:將this保存在一個變量中

demo10

複製代碼
var name='james';
var person={
  name:'koBe',
  sayName:function(){
    var that=this; //將this存儲在that中。
     setTimeout(function(){
        console.log(that.name);
     },0);
  }
}
person.sayName();  // koBe
複製代碼

將this保存在that中,這樣,setTimeout調用this.name就變成了that.name了。

 

方法二:使用bind

demo11 

複製代碼
var name='james';
var person={
  name:'koBe',
  sayName:function(){
     setTimeout(function(){
        console.log(this.name);
     }.bind(this),0);
  }
}
person.sayName();  // koBe
複製代碼

bind方法,起的作用和call,apply一樣,都是改變函數/方法執行時,this的指向,確保這個函數/方法運行時this指向保持一致。

demo11中,通過bind方法將this對象綁定為person。那麼回調函數在執行的時候,this指向還是person。

 

有誤之處,歡迎指出

如果您覺得文章有用,可以打賞個咖啡錢

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多