函數做用域中的this問題

首先一塊兒回顧下預解析做用域吧:es6

  預解析:
    瀏覽器每讀到一個script標籤或function,先不執行任何代碼,會先把整個代碼快速的瀏覽一遍,而後從中 挑出 var 和 function兩個關鍵字 .
    var: 預解析遇到 var 就把 var 連同它後邊的名字一塊 提到script(或function) 的最前邊,預解析完成以後,在從上向下一行一行執行代碼,若是碰到了 = 就賦值;
    function:預解析遇到function,就把整個函數提到提到script(或function) 的最前邊(跟在var的後邊預解析先解析var 在解析 function)瀏覽器

    全局做用域 --- 在任何地方都能訪問
                             函數外定義的變量擁有全局做用域
                            不使用var定義的變量擁有全局做用域
                            全部window對象上的屬性擁有全局做用域
                            沒有聲明在任何函數內部的函數擁有全局做用域
      局部做用域 --- 只能在函數內部訪問
                            使用var在函數內部定義的變量,和使用function在函數內部聲明的函數,擁有局部做用域函數

     [[Scopes]] : 做用域
                            當咱們聲明一個函數的時候,同時該函數就會建立一個屬性這個屬性是[[Scopes]](做用域),咱們在這個函數中 聲明的變量都會被存入這個函數的[[Scopes]]屬性中
     變量與函數的查找規則:
                            當咱們調用一條數據的時候,js首先會在當前做用域中進行查找,若是找不到,就向上找到父級的做用域,若是在父級的做用域中也找不到,就繼續向上查找,直                               到window的做用域。若是在window中也找不到,就報錯了this

小例子中有涉及到箭頭函數,因此咱們先認識一下ES6中的箭頭函數吧。spa

// 箭頭函數
    var fn3 = (a, b) => {
        console.log(a, b);
    }

    // 幾種有條件的簡化寫法
    // 當參數只有一個的時候
    var fn4 = a => {
        console.log(a);
    }
    // 當參數有多個的時候
    var fn5 = (a, b) => {
        console.log(a, b);
    }
    // 當沒有參數的時候
    var fn6 = () => {
        console.log(1);
    }
    // 有且僅有一個形參的時候,能夠省略參數的小括號


//    var fn7 = r => {
//        return r * r * Math.PI;
//    }
    // 只有一條語句的時候,能夠省略{},同時該條語句的結果將做爲該函數的返回值
    var fn7 = r => r * r * Math.PI;

下面是箭頭函數的特性,主要是this須要多加註意code

    在es6中,提供了一種新的函數格式:箭頭函數
    注意:
      1. 箭頭函數不能做爲構造函數,也就是箭頭函數不能使用new運算符
      2. 箭頭函數的this永遠指向當前申明做用域對象     帶大括號的範圍
           1. 普通函數this指向取決於調用
           2. 箭頭函數this指向取決於申明
      3. 箭頭函數沒有arguments對象
      也會由於call的改變指向而改變this。即便在構造函數中。所謂的聲明時候的this,也多是被變化掉了。
     
 

接着,咱們一塊兒來看一個小例子吧!對象

var name='window';
    var person1 = {
        name:'person1',
        show1: function() {
            console.log(this.name)
        },
        show2: () => console.log(this.name),
        show3: function() {
            return function() {
                console.log(this.name)//這裏的this跟外層函數的this不要緊
            }
        },
        show4: function(){
            return () => console.log(this.name)
        }
    };
    //call是更改this指向的。
    
    //對象屬性中的函數,寫法至關於 person1.show=function(){}
    //函數是全局做用域,掛載到window下,方便理解後面的this指向window
    
    var person2 = {name:'person2'};
    person1.show1()//person1
    //person1是個對象因此這裏指向的是對象的this person1
    
    person1.show1.call(person2)//person2
    //函數執行前先改了this指向
    
    person1.show2()//window
    //函數聲明是掛載到window下的,箭頭函數與聲明做用域對象有關
    //即便這裏調用該函數執行的是對象,但this仍是指的window
    
    person1.show2.call(person2)//window
    //同上,雖然執行函數時更改了this指向,可是這一套對箭頭很差使。因此仍然是window
    
    person1.show3()()//window
    //person1.show3()獲得的實際上是function(){console.log(this.name)}
    //函數執行,this指向的就是window
    
    person1.show3().call(person2)//person2
    //person1.show3()獲得的實際上是function(){console.log(this.name)}
    //然而接着在執行function前更改了指向
    
    person1.show3.call(person2)()//window
    //person1.show3.call(person2)更改了function() {
    //        return function() {
    //            console.log(this.name)
    //        }
    //    }
    //但再次調用括號執行本質仍是跟上面同樣,執行的是return出來的內部函數
    //先更改的指向是障眼法而已
    
    person1.show4()()//person1
    //person1.show4()先執行獲得的是() => console.log(this.name)
    //再指向該箭頭函數,它的聲明做用域this指向的是person1
    
    person1.show4().call(person2)//person1
    //箭頭函數this在聲明時候指向的是person1。
    //person1.show4()先執行獲得的是() => console.log(this.name)
    //這裏的call是更改箭頭函數自己的指向,是改不了的。
    
    person1.show4.call(person2)()//person2
    //person1.show4.call(person2)第一步執行這個的時候就先更改了指向
    //在執行() => console.log(this.name)前先更改了person1的指向

 

對比: 上面第一個是一個對象而已,因此show2的箭頭函數聲明時直接指向的window, 下面的是構造函數,可以爲s1的箭頭函數提供執行環境,因此這裏指向的是TImer對象自己。blog

       以上兩種狀況是須要注意區別的。ip

有錯誤或者不足之處請你們多多指正,謝謝啦!!!作用域

相關文章
相關標籤/搜索