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

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

做用域鏈

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

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

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

    • 函數就聲明在全局做用域內閉包

      <script>
              function fn(callback){
                  var age=18;
                  callback()   
              }
      
              fn(function(){
                  console.log(age);  //age is not defined
                  //分析:age變量:
                  //一、查找當前做用域:並無
                  //二、查找上一級做用域:全局做用域 (此時函數就聲明在全局做用域內)
                  //三、此時age變量就獲取不到fn函數內部的age變量
                  //看上一級做用域,不是看函數在哪裏調用,而是看函數在哪裏編寫,函數聲明在全局,它的上一級做用域就是全局做用域。
                  //-->由於這種特別,咱們一般會把做用域說成是:詞法做用域
              })
      </script>
    • 函數聲明在其它的函數內 (閉包就是個典型案例)app

      <script>
          function fn(){
              var a=5;
              return function(){
                  a++;
                  console.log(a);     //a變量確定是能夠訪問的
              }
          }
          var f1=fn();        //f1指向匿名函數
          f1();       //6
          f1();       //7
          f1();       //8
           //分析:a變量:
                  //一、查找當前做用域:並無
                  //二、查找上一級做用域:fn函數做用域 (此時函數就聲明在fn函數內)
      
          //-->通常認爲函數執行完畢,變量就會釋放,可是此時因爲js引擎發現匿名函數要使用a變量,因此a變量並不能獲得釋放,而是把a變量放在匿名函數能夠訪問到的地方去了
          //-->a變量存在於f1函數能夠訪問到的地方,固然此時a變量只能被f1函數訪問
      </script>

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

  • 函數的4種調用方式函數

    1. 普通函數調用的方式this

      <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. 方法的調用code

      <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)ip

      <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]) 參數包裹在一個數組中
相關文章
相關標籤/搜索