Stories

Detail Return Return

理解JavaScript中的this - Stories Detail

前言

相信很多人在初學JavaScript的時候都對this的指向問題感覺到比較迷惑。它不像PHP,Java語言中的this有很明確的指向,也不會讓人容易搞混。
在JavaScript中this就是函數調用的上下文,在JavaScript中有四種函數調用:

  1. 函數調用,例如alert('hello world')
  2. 方法調用,例如console.log('hello world')
  3. 構造函數調用,例如new RegExp('\d')
  4. 間接調用,例如alert.call(undefined,'hello world')

每種調用方式都定義了自己的上下文,所以this的表現也是不盡相同的。
另外,strict mode嚴格模式也會影響可執行上下文。
理解this的關鍵在於,對於函數執行以及它是如何來影響上下文的有一個清晰的視角。
請熟悉一下幾個概念:

  1. 函數調用指的就是執行構成函數體的代碼,説白了,就是直接調用一個函數,比如調用parseInt函數,調用的方式就是parseInt('15'),一個普普通通的調用,簡單明瞭,沒有任何副作用。
  2. 執行上下文,就是函數體內this的值。
  3. 函數的作用域就是函數體內能夠訪問到的變量和函數。

函數調用

函數調用的一個簡單的例子:

function sayHello() {
  console.log('hello');
}

sayHello(); // 輸出:hello

沒有任何拖泥帶水的東西,簡單。

另外立即執行函數IIFE也是函數調用的一種,如:

(function (name) {
  console.log('hello ' + name);
})('tt'); // 輸出:hello tt

在函數調用中,this總是指向全局對象,瀏覽器中就是window對象。

function sayHello() {
  console.log('hello'); // this==window
}

sayHello(); // 輸出:hello
(function (name) {
  console.log('hello ' + name);
})('tt'); // 輸出:hello tt,  this==window
測試
const obj = {
  name: 'tt',
  sayHello() {
    console.log(this);
    // 這裏的this是什麼?
    function sayBye() {
      console.log(this);
      //這裏的this是什麼?
      console.log('Bye');
    }
    return sayBye();
  }
};


obj.sayHello();

根據上面講過的規則,思考下,this分別是什麼,然後在瀏覽器中查看,看看你是否已經掌握。

分析:這裏的sayHello是屬於對象obj的一個方法,所以在sayHello函數中,屬於方法調用的範疇而不是函數調用的範疇,所以this肯定當然是隻想obj了,再看看sayBye函數,sayBye函數就是一個普通的函數調用,所以this當然就是隻想window了,外層sayHello函數中的this指向絲毫沒有影響到sayBye函數的this指向,且也無法影響,毫無疑問。
進階:有人想問了,如果我非想讓sayBye函數中的this輸出的是obj對象呢?有辦法,代碼如下:
const obj = {
  name: 'tt',
  sayHello() {
    function sayBye() {
      console.log(this);
      console.log('Bye');
    }
    return sayBye.bind(obj); // 思考下,這裏能否用apply,call?
  }
};

obj.sayHello()()
靈魂深度拷問
const obj = {
  name: 'tt',
  sayHello() {
    function sayBye() {
      console.log(this);
      console.log('Bye');
    }
    sayBye.bind(obj);
    return sayBye();
  }
};


obj.sayHello()

上述代碼的this輸出什麼?
>.< what the fuck!
解答:毫無疑問,輸出的仍然是window,為啥?bind的用法,是什麼?bind方法會創建一個新函數,當這個新函數被調用時,它的this值是傳遞給bind方法的第一個參數。上述代碼是bind了,但是你沒返回bind後生成的新函數,所以,跟沒bind一個樣兒。
正確寫法相信小夥伴們已經知道如何修改了,上代碼:

const obj = {
  name: 'tt',
  sayHello() {
    function sayBye() {
      console.log(this);
      console.log('Bye');
    }
    return sayBye.bind(obj);
  }
};

obj.sayHello()();
const obj = {
  name: 'tt',
  sayHello() {
    function sayBye() {
      console.log(this);
      console.log('Bye');
    }
    return sayBye.apply(obj);
  }
};

obj.sayHello()

未完待續...

Add a new Comments

Some HTML is okay.