Javascript基礎——this指向

前幾天發佈的Javavscript基礎——原型和原型鏈 收藏轉化率還挺高,看來你們對於JS基礎知識仍是很看重的,因爲JS語言設計的關係,不少語言特性不是那麼清晰。好比經典的this在哪的問題。javascript

本文研究一下Javascript的this指向。相信學完以後應該沒啥問題。java

Javascript的this指向問題,有些人可能以爲很簡單,有些人卻以爲撲朔迷離,看完本文以後相應會對this的掌握有一個直觀的判斷,而不是"開局全靠猜"。面試

敲黑板

  1. function函數this指向由調用方式肯定,跟定義環境無關。
  2. 箭頭函數this指向由定義環境決定,與調用方式無關,也不能夠bind(this)

嚴格模式

  1. 非嚴格模式下,全局做用域下的this指向window
  2. 嚴格模式下,全局做用域下的this指向undefined

如下討論均爲非嚴格模式,這個不影響今天的討論。segmentfault

實踐

說結論每每是讓人難以理解的,下面經過不一樣的調用場景對this作一個說明。app

1. 直接調用

function test() {
  console.log(this);
}
test(); // 輸出undefined

直接調用是最簡單的, 大部分人在這裏都能回答的很好。函數

總結

直接調用時this指向全局做用域this

  • 非嚴格模式this指向window
  • 嚴格模式this指向undefined

2. 對象調用

'use strict'
var n = 1;
var a = {
  n: 2,
  b: function() {
      console.log(this.n);
  }
};

a.b(); // 輸出2
var b = a.b;
b(); // 輸出1

面試題:請問上述例子輸出什麼?

非嚴格模式下,輸出2和1,嚴格模式下輸出2和一個報錯(this指向undefined,訪問undefined的n屬性確定報錯)

那若是你這麼回答,滿分!設計

分析

知其然還要知其因此然,咱們分析一下:code

爲何輸出2?對象

由於 a.b()是對象調用方式,因此b()中的this指向a

爲何輸出1?

這個很是有意思,並且也頗有迷惑性,面試的時候常常問到,也常常有人被問倒。

var b = a.b

a.b賦值給變量b,b就是一個函數,請注意: 這裏只是賦值,沒有調用,因此b中的this指向還不肯定

b();

調用函數b,這是什麼調用方式? 普通調用,因此this指向全局做用域。

總結

對象調用方式下this指向調用對象。

是否GET? 若是沒有GET,請關注公衆號NodeJs之路,我在線給你答疑。

開胃菜已經吃了,下面來點"有難度的(實際上也沒啥難度)"。

3. 嵌套對象調用

var n = 1;
var a = {
  n: 2,
  b: {
    n: 3,
    c: function() {
      console.log(this.n)
    }
  }
};

面試題:請問上述例子中function中的this指向哪裏?

正確答案: 未肯定調用環境的狀況下,this的指向 不肯定

錯誤答案:指向a.b對象,Too young too simple!

var n = 1;
var a = {
  n: 2,
  b: {
    n: 3,
    c: function() {
      console.log(this.n)
    }
  }
};

a.b.c(); // 輸出3

a.c = a.b.c;
a.c(); // 輸出2

var c = a.b.c; 
c(); // 輸出1

這道題跟以前那道對象調用很像。

爲何輸出3?

對象調用方式下指向調用對象,a.b.c()中c()是經過 a.b對象調用,指向對象 a.b

爲何輸出?

a.c = a.b.c 給a對象定義一個函數c,注意,此時沒有調用!this指向不肯定

a.c() 經過a對象來調用c(),因此this指向對象a

爲何輸出1?

var c = a.b.c 函數賦值給普通變量,注意,此時沒有調用!

c(); 普通方式調用,指向window

總結

嵌套對象調用方式下,this指向最終調用函數的對象。

a.b.c.d.e.f.g.h() h函數中的this指向a.b.c.d.e.f.g

4. 構造函數方式調用

var name = 1;
function Person() {
  this.name = 2;
}

var p1= Person(); // p1爲undefined
console.log(p1.name); // 報錯

var p2 = new Person();
console.log(p2.name); // 輸出2

p1爲何是undefined?

這道題比較坑,跟調用方式和this指向無關,由於Person函數沒有返回值,js中,默認會返回undefined.

p2.name爲何是2?

使用new操做符時,構造函數的返回值 默認指向對象實例,因此p2.name就是Person()中的this.name

若是在Person()函數中加上return this的話,Person()返回值仍是this,由於這是普通調用。

5. 構造函數中指明返回值

原則上構造函數不該該有返回值,可是若是真的寫了會怎麼樣?咱們來探討一下。

返回複雜對象

function Person() {
  this.name = 2;
  return {};
}
var p1 = new Person();
console.log(p1.name)// 輸出undefined

返回簡單對象

雖然Js只有對象,可是有一些如string,number這種通常叫作簡單對象,date,regex,array,object等等叫複雜對象。

function Person() {
  this.name = 2;
  return 1;
}
var p1 = new Person();
console.log(p1.name)// 輸出2

返回null

使用typeof null返回的是[object],證實null是個對象,不過我們來看看構造函數返回null的表現。

function Person() {
  this.name = 2;
  return null;
}
var p1 = new Person();
console.log(p1.name)// 輸出2

總結

構造函數中this指向對象實例自己,若是構造函數指明瞭返回值,那麼表現以下:

  • 返回普通值,this指向不變,仍是對象實例自己
  • 返回複雜對象,this指向新對象,也就是你new Person()返回的是那個新對象

6. bind

var a = {
    n: 1
  };
  var b = {
    n: 2
  }
  function f() {
    console.log(this.n);
  }
  var fa = f.bind(a);
  var fb = fa.bind(b);

  fa(); // 輸出1
  fb(); // 輸出1

第1個輸出1應該不難理解,bind能夠更改function內部的this指向。屢次bind已經bind過的函數,this指向不變。

bind的實現原理有點複雜,將在下一篇文章進行詳細解讀。

總結

bind能夠手動綁定function的this,this指向第1次bind時的this。

7. apply/call

這兩個函數在this指向上表現一致,放到一塊兒講

var a = {
    n: 1
  };
  var b = {
    n: 2
  }
  function f() {
    console.log(this.n);
  }
  f.call(a); // 輸出1
  f.apply(b); // 輸出2
call和apply的第1個參數爲function執行時的this,這個this是肯定的,對未使用過bind的函數進行屢次apply/call,this指向都會改變。

8. 箭頭函數

var n = 1;
var b = {
  n: 2,
  a: () => {
    console.log(this.n);
  }
}
b.a(); // 輸出1
b.a.call({n:3}); // 輸出1

b.a定義時的this和n,b所在的this一致,默認狀況下爲全局做用域

箭頭函數的this指向定義時所在的this,這個是明確的,可是若是定義時所在的是1個function,那麼this指向同上面7點。

說下我以前學JS遇到過的問題: ES5下function纔會有做用域隔離, {}這種玩意不會隔離做用域。

結尾

  1. 直接調用this指向全局做用域window,嚴格模式指向undefined
  2. 對象調用方式指向調用對象
  3. 嵌套對象調用方式指向最終調用對象(離function最近的那個)
  4. 構造函數方式調用指向對象實例

    1. 構造函數返回String/Number等簡單類型時this指向不變,返回null指向也不變
    2. 構造函數返回Object/Array等複雜對象時,new Person()的返回值爲return的對象
  5. bind能夠更改function的this,一經綁定,永不改變。可是並不執行函數
  6. apply/call能夠更改沒有被bind過的this
  7. 箭頭函數的this指向爲定義箭頭函數的this
相關文章
相關標籤/搜索