1. 程式人生 > >JavaScript中this指標指向的徹底理解

JavaScript中this指標指向的徹底理解

現在我們就來總結一下js中this的指向。首先需要了解一下幾個概念:

1、全域性變數預設掛載在window物件下

2、一般情況下this指向它的呼叫者

3、es6的箭頭函式中,this指向建立者,並非呼叫者

4、通過call、apply、bind可以改改變this的指向

在非箭頭函式下, this 指向呼叫其所在函式的物件,而且是離誰近就是指向誰(此對於常規物件,原型鏈, getter & setter等都適用);建構函式下,this與被建立的新物件繫結;DOM事件,this指向觸發事件的元素;內聯事件分兩種情況,bind繫結, call & apply 方法等。

下面我們具體分析一下

1、在函式呼叫時

//非嚴格模式下
const func = function () {
    console.log(this);
    const func2 = function () {
      console.log(this);
    };
    func2(); //Window
  };
  func(); //Window

//嚴格模式下

'use strict'
  const func = function () {
    console.log(this);
    const func2 = function () {
      console.log(
this); }; func2(); //undefined }; func(); //undefined

2、作為物件方法

const user = {
 
    userName: '小張',
    age: 18,
    selfIntroduction: function () {
      const str = '我的名字是:' + this.userName + ",年齡是:" + this.age;
    console.log(this);//user console.log(str); const loop
= function
() {
     console.log(this); //Window console.log(
'我的名字是:' + this.userName + ",年齡是:" + this.age); }; loop(); //我的名字是:undefined,年齡是:undefined } }; user.selfIntroduction(); //我的名字是:小張,年齡是:18

按照第二條規則,this指向他的呼叫者,selfIntroduction()方法的呼叫者是user,所以在selfIntroduction()方法內部this指向了他的父物件即user,而loop方法輸出的為undefined的原因就是我在上面所說的javascript的設計缺陷了,在這種情況下,我們通常選擇在selfIntroduction()方法裡將this快取下來。

const user = {
    userName: '小張',
    age: 18,
    selfIntroduction: function () {
      const str = '我的名字是:' + this.userName + ",年齡是:" + this.age;
      console.log(str);
 
      const that=this;
 
      const loop = function () {
        console.log('我的名字是:' + that.userName + ",年齡是:" + that.age);
      };
 
      loop();   //我的名字是:小張,年齡是:18
 
    }
  };
 
  user.selfIntroduction();  //我的名字是:小張,年齡是:18

此時loop的this指向就理想了。

const user={
 
    userName:'小張',
    age:18,
    selfIntroduction:function(){
      const str='我的名字是:'+this.userName+",年齡是:"+this.age;
      console.log(str); 
    }
  };
 
  const other =user.selfIntroduction;
  other(); //我的名字是:undefined,年齡是:undefined
 
  const data={
    userName:'小李',
    age:19,
  };
  data.selfIntroduction=user.selfIntroduction;
  data.selfIntroduction(); //我的名字是:小李,年齡是:19

在看這段程式碼,將selfIntroduction()賦值給了全域性變數other,呼叫other()方法,other掛載在全域性函式window物件下,window物件下沒有userName 和 age 這兩個屬性,所以輸出為undefined。

第二段程式碼,申明瞭data物件,包含了username和age屬性,記住我們的第二條規則一般情況下this指向它的呼叫者,大家就明白了,data是selfIntroduction()的函式的呼叫者,所以輸出了data的userName和age。

3:在html裡作為事件觸發

<body>
  <div id="btn">點選我</div>
</body>
     const btn=document.getElementById('btn');
    btn.addEventListener('click',function () {
      console.log(this); //<div id="btn">點選我</div>
    })

在種情況其實也是遵循了第二條規則一般情況下this指向它的呼叫者,this指向了事件的事件源即event。

4:new關鍵字(建構函式)

建構函式中的this與被建立的新物件繫結。

注意:當構造器返回的預設值是一個this引用的物件時,可以手動設定返回其他的物件,如果返回值不是一個物件,返回this。

const fun=function(userName){
    this.userName=userName;
  }
  const user=new fun('郭德綱');  
  console.log(user.userName); //郭德綱
function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

var b = new C2();
console.log(b.a); // logs 38

 

5:es6(箭頭函式)

const func1=()=>{
    console.log(this); 
  };
  func1(); //Window
const data={
    userName:'校長',
    selfIntroduction:function(){
      console.log(this); //Object {userName: "校長", selfIntroduction: function}
      const func2=()=>{
        console.log(this); //Object {userName: "校長", selfIntroduction: function}
      }
 
      func2();
    }
  }
  data.selfIntroduction();

大家在看看我開頭說的第三條準則:es6的箭頭函式中,this指向建立者,並非呼叫者,fun1 在全域性函式下建立,所以this指向全域性window,而fun2在物件data下建立,this指向data物件,所以在func2函式內部this指向data物件,個人認為es6的箭頭函式的this指向是對我上面所說的javascript設計缺陷的改進,(個人認知)。

6:改變this的指向

當函式通過Function物件的原型中繼承的方法 call() 和 apply() 方法呼叫時, 其函式內部的this值可繫結到 call() & apply() 方法指定的第一個物件上, 如果第一個引數不是物件,JavaScript內部會嘗試將其轉換成物件然後指向它。

function add(c, d){
  return this.a + this.b + c + d;
}

var o = {a:1, b:3};

add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

function tt() {
  console.log(this);
}
// 返回物件見下圖(圖1)
tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

而bind方法會返回一個可執行的函式。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty