作用域鏈和函數內部this指向問題以及bind、call、apply方法


作用域鏈和函數內部this指向問題以及bind、call、apply方法

作用域鏈

  • 作用域是相對於變量而言的, 其意義就在與查找變量(確定變量的來處, 變量是否可以訪問到, 確定變量在當前位置是否可以取到值)

    • JS分函數作用域和全局作用域
    • JS變量又遵循就近使用的原則
      1. 首先在使用該變量的當前作用域查找 , 如果當前作用域聲明了這個變量,就可以確定結果;如果沒有查找到,進入步驟2
      2. 查找當前作用域的上級作用域,也就是當前函數的上級函數,看看上級函數中有沒有聲明
      3. 再查找上級函數的上級函數,直到全局作用域為止
      4. 如果全局作用域中也沒有,我們就認為這個變量未聲明 xxx變量 is not defined
  • 如何確定函數作用域的上一級作用域

    • 函數的上一次作用域取決於函數聲明時的位置,而不是函數調用時的位置。

    • 函數就聲明在全局作用域內

      <script>
              function fn(callback){
                  var age=18;
                  callback()   
              }
              
              fn(function(){
                  console.log(age);  //age is not defined
                  //分析:age變量:
                  //1、查找當前作用域:並沒有
                  //2、查找上一級作用域:全局作用域 (此時函數就聲明在全局作用域內)
                  //3、此時age變量就獲取不到fn函數內部的age變量
                  //看上一級作用域,不是看函數在哪里調用,而是看函數在哪里編寫,函數聲明在全局,它的上一級作用域就是全局作用域。
                  //-->因為這種特別,我們通常會把作用域說成是:詞法作用域
              })
      </script>
      
    • 函數聲明在其它的函數內 (閉包就是個典型案例)

      <script>
          function fn(){
              var a=5;
              return function(){
                  a++;
                  console.log(a);     //a變量肯定是可以訪問的
              }
          }
          var f1=fn();        //f1指向匿名函數
          f1();       //6
          f1();       //7
          f1();       //8
      	 //分析:a變量:
                  //1、查找當前作用域:並沒有
                  //2、查找上一級作用域:fn函數作用域 (此時函數就聲明在fn函數內)
      
          //-->一般認為函數執行完畢,變量就會釋放,但是此時由於js引擎發現匿名函數要使用a變量,所以a變量並不能得到釋放,而是把a變量放在匿名函數可以訪問到的地方去了
          //-->a變量存在於f1函數可以訪問到的地方,當然此時a變量只能被f1函數訪問
      </script>
      

函數內部的this指向問題(箭頭函數不適用)

  • 函數的4種調用方式

    1. 普通函數調用的方式

      <script>
          var age = 18; //該age聲明在全局作用域下,其就會被掛載到window對象上,即window.age=18
          var p = {
              age: 15,
              say: function () {
                  console.log(this.age); //this指向window
              }
          }
          var s1 = p.say //s1 = function () {console.log(this.age);}
          s1(); //函數調用, 結果為 18
      </script>
      
    2. 方法的調用

      <script>
          var age = 18; //該age聲明在全局作用域下,其就會被掛載到window對象上,即window.age=18
          var p = {
              age: 15,
              say: function () {
                  console.log(this.age); //this指向p對象
              }
          }
          p.say() //結果為15  //p.say獲取的就是p對象內部的say方法,然后方法后+()就是方法的調用。
      </script>
      
    3. new調用(構造函數)

      <script>
          var age = 18;
          var p = {
              age: 15,
              say: function () {
                  console.log(this.age);
                   //此時this指向構造函數new出來的實例,而實例中並沒有初始化age屬性
              }
          }
          new p.say() //構造函數調用, 結果為undefined
      </script>
      
      <script>
          var age = 18;
          var p = {
              age: 15,
              say: function (age) {
                  this.age = age
                  console.log(this.age); 
              }
          }
          new p.say(22) //構造函數調用, 結果為22
      </script>
      
    4. 上下文方式(call、apply、bind)

      <script>
          var length = 21; //window.length = 21
          function f1() {
              console.log(this.length);
          }
          f1.call([1, 3, 5]) //結果為3,此時this指向[1,3,5]數組
          f1.apply(this) //結果為21,此時this指向window
          f1.call(5) //undefinde , 此時this指向 Number,其沒有length屬性
          f1.bind(this)() //結果為21 
      //bind方法只改變函數f1內部this指向,而不會讓函數執行,想讓其執行,需要在后面手動調用+()
      </script>
      

bind、call、apply共同點和區別

  • 三個方法都可以改變函數內部this指向
  • bind和call、apply的區別是,bind方法不會讓函數執行,而call、apply的調用會讓函數也跟着執行
  • call和apply的區別就在與給函數傳參的方式不同
    • call(this指向對象, 參數1,參數2,參數3) 參數以逗號分隔,依次排下去
    • apply(this指向對象, [參數1,參數2,參數3]) 參數包裹在一個數組中


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM