如何簡單理解js中this的指向

前序

每一個人學js都會被this指向這個東西搞得很蒙,那就是this的指向問題。首先,咱們要明白 this的指向在函數定義的時候是肯定不了的,只有函數執行的時候才能肯定this到底指向誰實際上this的最終指向的是那個調用它的對象(網上大部分文章都是這樣寫的,可是我的以爲有點不全面吧,理解this指向起來容易讓人產生琢磨不透的感受,接下來一塊兒來學習下吧。javascript

例子1

function test(){
    var name = "翊楓";
    console.log(this.name); //undefined
    console.log(this); //Window
}
test();

 上面說過this最終指向的是調用它的對象,這裏的函數test實際是被Window對象所點出來的,咱們接着繼續看。html

function test(){
    var name = "翊楓"; 
  console.log(this.name); //undefined
  console.log(this);  //Window
}
window.test();

 這個事實兩個是同樣的意思。只是爲了證實是經過window對象點出來,讓你們明白爲啥this指向是Windowjava

例子2:

var o = {
    name:"翊楓",
    fn:function(){
        console.log(this.name);  //翊楓
    }
}
o.fn();

  首先咱們明白這個this指向了那裏,你們看到這裏應該也明白this指向了對象o,由於咱們調用fn這個函數是經過了o.fn()執行,那指向明顯就是對象o了。首先咱們仍是要強調一下,this指向函數定義時候是沒法肯定的,只有函數執行的時候才能肯定this到底指向誰,誰調用就指向誰,咱們必需要明白這個。數組

 上面例子可能你們都懂了,那接下來完全搞一下這個this,讓你們更加清晰明白this的指向哪裏app

例子3:

var o = {
    name:"翊楓",
    fn:function(){
        console.log(this.user); //翊楓
    }
}
window.o.fn();

  這段代碼跟離子二幾乎是同樣,咱們限制理解下爲啥這裏的this爲何不是指向window,若是按照上面的理論,最終this指向的是調用它的對象,這裏先說個而外話,window是js中的全局對象,咱們建立的變量其實是給window添加屬性,因此這裏能夠用window點o對象。函數

  咱們先不說爲啥會不指向window,接下來再看一段代碼。學習

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //12
        }
    }
}
o.b.fn();

  這裏一樣也是對象o點出來的,可是一樣this並無執行它,可能你會說爲啥不是指向對象o?其實也不是,只是一開始說的不許確,接下來我補充一句話,相信你就能夠完全的理解this的指向的問題。this

  1:若是一個函數中有this,可是它沒有被上一級的對象所調用,那麼this指向的就是window,這裏須要說明的是在js的嚴格版中this指向的不是window,可是咱們這裏不探討嚴格版的問題,你能夠上網查找。spa

  2:若是一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。code

  3:若是一個函數中有this,這個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象,例子3能夠證實,若是不相信,那麼接下來咱們繼續看幾個例子。

var o = {
    a:10,
    b:{
        // a:12,
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

儘管對象b中沒有屬性a,這個this指向的也是對象b,由於this只會指向它的上一級對象,無論這個對象中有沒有this要的東西。

還有一種比較特殊的狀況,例子4:

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();

這裏this指向的是window,是否是有些蒙了?實際上是由於你沒有理解一句話,這句話一樣相當重要。

  this永遠指向的是最後調用它的對象,也就是看它執行的時候是誰調用的,例子4中雖然函數fn是被對象b所引用,可是在將fn賦值給變量j的時候並無執行因此最終指向的是window,這和例子3是不同的,例子3是直接執行了fn。

  this講來說去其實就是那麼一回事,只不過在不一樣的狀況下指向的會有些不一樣,上面的總結每一個地方都有些小錯誤,也不能說是錯誤,而是在不一樣環境下狀況就會有不一樣,因此我也沒有辦法一次解釋清楚,只能你慢慢地的去體會。

 

構造函數版this:

function Fn(){
    this.user = "追夢子";
}
var a = new Fn();
console.log(a.user); //追夢子

  這裏之因此對象a能夠點出函數Fn裏面的user是由於new關鍵字能夠改變this的指向,將這個this指向對象a,爲何我說a是對象,由於用了new關鍵字就是建立一個對象實例,理解這句話能夠想一想咱們的例子3,咱們這裏用變量a建立了一個Fn的實例(至關於複製了一份Fn到對象a裏面),此時僅僅只是建立,並無執行,而調用這個函數Fn的是對象a,那麼this指向的天然是對象a,那麼爲何對象a中會有user,由於你已經複製了一份Fn函數到對象a中,用了new關鍵字就等同於複製了一份。

  除了上面的這些之外,咱們還能夠自行改變this的指向,關於自行改變this的指向請看JavaScript中call,apply,bind方法的總結這篇文章,詳細的說明了咱們如何手動更改this的指向。

 

更新一個小問題當this碰到return時

複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return {};  
}
var a = new fn;  
console.log(a.user); //undefined
複製代碼

再看一個

複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return function(){};
}
var a = new fn;  
console.log(a.user); //undefined
複製代碼

再來

複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return 1;
}
var a = new fn;  
console.log(a.user); //翊楓
複製代碼
複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return undefined;
}
var a = new fn;  
console.log(a.user); //翊楓
複製代碼

什麼意思呢?

  若是返回值是一個對象,那麼this指向的就是那個返回的對象,若是返回值不是一個對象那麼this仍是指向函數的實例。

複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return undefined;
}
var a = new fn;  
console.log(a); //fn {user: "翊楓"}
複製代碼

  還有一點就是雖然null也是對象,可是在這裏this仍是指向那個函數的實例,由於null比較特殊。

複製代碼
function fn()  
{  
    this.user = '翊楓';  
    return null;
}
var a = new fn;  
console.log(a.user); //翊楓
複製代碼

知識點補充:

  1.在嚴格版中的默認的this再也不是window,而是undefined。

  2.new操做符會改變函數this的指向問題,雖然咱們上面講解過了,可是並無深刻的討論這個問題,網上也不多說,因此在這裏有必要說一下。

function fn(){
    this.num = 1;
}
var a = new fn();
console.log(a.num); //1

  爲何this會指向a?首先new關鍵字會建立一個空的對象,而後會自動調用一個函數apply方法,將this指向這個空對象,這樣的話函數內部的this就會被這個空的對象替代。

  注意: 當你new一個空對象的時候,js內部的實現並不必定是用的apply方法來改變this指向的,這裏我只是打個比方而已.

  if (this === 動態的\可改變的) return true;

 

看完上面this指向的一些問題和例子,那接下來,請你們作幾道js題深入理解下this指向,如下代碼,結果是

問題1.

var foo = 1;
function bar() {
    foo = 10;
    return;
    function foo() {}
}
bar();
alert(foo);

問題2.

function bar() {
    return foo;
    foo = 10;
    function foo() {}
    var foo = 11;
}
alert(typeof bar());

問題3.

var x = 3;
var foo = {
    x: 2,
    baz: {
        x: 1,
        bar: function() {
            return this.x;
        }
    }
}
var go = foo.baz.bar;
alert(go()); 
alert(foo.baz.bar()); 

問題4.

x = 1;
function bar() {
    this.x = 2;
    return x;
}
var foo = new bar();
alert(foo.x);

問題5.

function foo(a) {
    alert(arguments.length);
}
foo(1, 2, 3);

問題6.

var foo = function bar() {}; 
alert(typeof bar);

問題7.

var arr = [];
arr[0]  = 'a';
arr[1]  = 'b';
arr.foo = 'c';
alert(arr.length);//備註:length返回的是array的數組索引長度,數組索引只能是數字,若是是字符串的至關於對象屬性了。

問題8.

function foo(a) {
    arguments[0] = 2;
    alert(a);
}
foo(1);

問題9.

function foo(){}
delete foo.length;
alert(typeof foo.length);

問題10.

var name="the window";
var object={
  name:"my object",
  getName: function(){
    return this.name;
  } }

經過如下調用

object.getName(); 
(object.getName)();
(object.getName = object.getName)()

參考答案:

1.輸出1

2.輸出function

3.輸出3和1

4.輸出2

5.輸出3

6.輸出function

7.輸出2,備註說了length返回的是array的數組索引長度,數組索引只能是數字,若是是字符串的至關於對象屬性了。

8.輸出2

9.輸出number,備註foo.length的值仍是0。delete沒法刪除foo.length

10.輸出

  my object

  my object

  the window

備註:

第一行的代碼,this指向的就是object,因此毫無疑問;

第二行代碼雖然加上括號,就好像只是在引用一個函數,但this的值獲得了維持。由於object.getName和(object.getName)的定義相同。

第三行代碼,先執行一條賦值語句,而後再調用賦值後的結果。由於這個賦值表達式的值是函數自己,因此this的值不能獲得維持,結果就返回the window.

相關文章
相關標籤/搜索