走進JavaScript之this篇

前言

在這篇文章中,你將會弄清this關鍵字指的是什麼,在爲你講解this同時,你還將學習到.call,.apply,.bindnew以及箭頭函數等知識。數組

this是什麼

this 是JavaScript中很特別的一個關鍵字,被自動定義在全部函數做用域中,可是它即不指向函數自身也不指向函數的詞法做用域,它指向的是調用函數的對象。bash

this到底指向哪裏

要判斷this指向哪裏,咱們必須理解每一個函數的this並非在聲明時就被綁定的,而是在調用時被綁定。
正如上文所說this指向的是調用函數的對象,因此要判斷this到底指向哪裏,首先咱們要知道 「是哪一個對象調用了函數?」app

舉個例子來幫助同窗們理解這一點:函數

例子1:學習

123function person(name) {
  console.log(name);
}
複製代碼

能夠看到例子裏聲明瞭一個person函數,接收一個name參數,想知道name會打印出什麼,必須得看person函數調用過程當中傳入參數是什麼。一樣的道理想判斷this指向哪裏,就得看函數調用方式是什麼。ui

根據函數調用方式不一樣,我將其分爲下面👇幾種狀況:

一、首先就是最經常使用的函數調用方法:函數名直接調用

例2:this

123456789101112function person() {
  console.log(this);
}

function personStrict() {
  'use strict'
  console.log(this);
}

person(); //  window

personStrict(); // undefined
複製代碼

能夠看到,函數被直接調用時,就至關於全局對象調用的;這樣它的 this就指向全局對象:windowspa

PS:可是在嚴格模式下,this不會指向全局對象,而是指向undefined,這是爲了儘可能減小不嚴謹的出錯行爲,你只須要在你所寫代碼做用域的最頂端加上use strict;建議將其放在一個當即執行函數中,避免污染全局。

例3:code

1234567(function(){
  //'use strict'
  function getDoc() {
    console.log(this.document);
  }
  getDoc();
})();
複製代碼


能夠看到,若是程序在非嚴格模式下運行,不會有錯誤拋出,那是由於在全局對象window中存在一個名爲document的屬性,而在嚴格模式下,因爲此時this指向undefined,因而拋出一個錯誤。cdn

二、做爲對象的方法調用

例4:

12345678let name = 'window';
let person = {
  name : 'Heternally',
  getName : function(){
    console.log(this.name);
  }
}
person.getName(); // 'Heternally'
複製代碼
123456789let name = 'window';
let person = {
  name : 'Heternally',
  getName
}
function getName(){
  console.log(this.name)
}
person.getName(); // 'Heternally'
複製代碼

能夠看到在上面👆例子中,getName函數是對象person調用的,因此打印出來的值是person中name的值

例5:

12345678910let name = 'window';
let person = {
  name : 'Heternally',
  getName : function () {
    console.log(this.name);
  }
}

let getName = person.getName;
getName(); // 'window'
複製代碼

例5對例4作了一點點小改動,能夠看到打印出的結果就是window了,這是由於getName函數最終仍是被window調用,因此這個this指向的是window

這又應了上文的話,this的指向不能在聲明的時候肯定,而是取決於誰調用了它

例6:

12345678910111213141516171819202122232425let person1 = {
  name : 'person1',
  getThis: function () {
    console.log(this);
  }
}

let person2 = {
  name : 'person2',
  getThis: function () {
    return person1.getThis();
  }
}

let person3 = {
  name : 'person3',
  getThis: function () {
    var getThis3 = person1.getThis;
    return getThis3();
  }
}

person1.getThis(); // person1
person2.getThis(); // person1
person3.getThis(); // window
複製代碼

看到這可能有同窗會問,不是說this的指向取決於誰調用了它嗎,那爲何person3.getThis()打印出來的是window呢,由於在person3.getThis裏,調用this的函數是getThis3,因此此時this指向了window

由上面👆幾個例子能夠得出,this指向最後調用它的那個對象。

經常使用改變this指向方法

一、call、apply、bind

這三個函數的做用都是改變函數執行時的上下文,即改變函數運行時的this指向。有了這我的生,接下來咱們經過幾個例子來學習如何使用這三個函數。
簡單說一下三個方法的用法:

1.1 call
1fun.call(thisArg[, arg1[, arg2[, ...]]])
複製代碼

它會當即執行函數,第一個參數時指定執行函數中this的上下文,若是不傳或者傳null、undefined,則表示this指向window,後面的參數是執行函數所需的參數;

1.2 apply
1fun.apply(thisArg[, [arg1, arg2, ...]])
複製代碼

它會當即執行函數,第一個參數時指定執行函數中this的上下文,若是不傳或者傳null、undefined,則表示this指向window,第二個參數接收一個數組(這是與call惟一的區別);

1.3 bind
1var foo = fun.bind(thisArg[, arg1[, arg2[, ...]]]);
複製代碼

它不會當即執行函數,而是返回一個心得函數,這個新的函數被指定了this上下文,接收的參數與call一致;

例7:

1234567891011let person = {
  name : 'Heternally'
}
var name = 'window';
function getName() {
  console.log(`Hello, my name is ${this.name}`);
}
getName(); // 'Hello, my name is window'
getName.call(person); // 'Hello, my name is Heternally'
getName.apply(person); // 'Hello, my name is Heternally'
getName.bind(person)(); // 'Hello, my name is Heternally'
複製代碼

能夠看到,使用call、apply、bind方法後,能夠動態改變函數執行上下文的this指向

二、new關鍵字

每當使用new關鍵字調用函數時,會自動把this綁定在新對象上,而後再調用這個函數

例8:

12345678function Person(name) {
  this.name = name;
  console.log(this);
}
var people = Person('Heternally'); // window
var people1 = new Person('Heternally'); // Person {name: "Heternally"}
people.name; //Cannot read property 'name' of undefined
people1.name; // 'Heternally'
複製代碼

三、ES6箭頭函數

在ES6中,新加入了箭頭函數,它和普通函數最不一樣的一點就是,對於箭頭函數的this指向,只須要看它是在哪裏建立便可

例9:

123456789101112131415var person = {
  name : 'Heternally',
  getName1 : function () {
    setTimeout(function(){
      console.log(this);
    },1000)
  },
  getName2 : function () {
    setTimeout(()=>{
      console.log(this);
    },1000)
  }
}
person.getName1(); //window
person.getName2(); // {name:'Heternally',getName1:f,getName2:f}
複製代碼

能夠看到,在getName2方法的setTimeout函數中,沒有跟getName1中setTimeout函數同樣打印window,而是打印出person對象。
簡單說:箭頭函數中的this只和定義它時做用域有關,並不受在哪裏及如何調用的影響;

優先級

瞭解了this指向及多種改變this指向的方法後,咱們還須要瞭解這多種方法的優先級,

1函數直接調用  < 對象方法調用 < call/apply < bind < new < 箭頭函數
複製代碼

總結

要判斷this指向,須要找到是這個函數的直接調用位置,找到以後能夠依據以下方法進行判斷this的指向:

一、 是否由箭頭函數調用 ?指向箭頭函數外層非箭頭函數的this指向 : ‘’

二、 是否由new調用 ?指向新建立的對象 : ‘’;

三、 是否由call || apply || bind 調用 ? 指向指定的對象 : ‘’;

四、 是否由對象調用 ? 指向這個對象 : ‘’;

五、 上面幾種狀況都不是:嚴格模式下指向undefined,非嚴格模式下指向全局對象

相關文章
相關標籤/搜索