js做用域和this的理解

JavaScript 的做用域和上下文對象this

  • 從幾個有意思的js問題開始

    1. 爲何輸出的不是f2?html

      `var scope = 'top';
        var f1 = function() { 
            console.log(scope);
        };
        f1(); // 輸出 top
        var f2 = function() { 
            var scope = 'f2'; 
            f1();
        };
         f2(); // 輸出 top`
    2. 我還能訪問closure,但this值改變前端

      `function closure(arg){
           var a ="closure";
           console.log(a);//(1) closure
           console.log(this);//(2) obj對象
           return function(){
                  console.log(a);//(4) closure
                  console.log(arg); // (5) arg
                  console.log(this);//(6) windowd對象
                }
           }
       var obj ={
          func:closure
          }
       function func () {
          setTimeout(obj.func("arg"),100);
          console.log("funcEnd");// (3)funcEnd
          }
       func()`
    3. this會被綁定在某個對象上c++

      `var b = "window.b"
       var funcFactory = function(){
          var b ="func.b";
          var obj ={
              a:"obj.a",
              func:function(){
                  console.log(b)
                  console.log(this.a);
              }
          }
          return obj;
       }
       var temp =funcFactory()
          var func = temp.func;
          temp.func();// 先出現 func.b 後出現obj.a
          func();// 先出現func.b 後出現 undefined`

      4.函數執行完了,this綁定在window上es6

      `function func(){
          var a=1;
          setTimeout(function(){
              console.log(a)
          },100);
        }
        func();// 1`

  • 深度理解js靜態做用域

    1. js靜態做用域面試

      • 在第一段代碼中不少人會理所固然的認爲會打印出f2來,會說f2是個function,js是個以function爲做用域劃分,然而事情每每事與願違---最後打印出的是出乎意料的top。人人都知道js在執行以前會進行預處理(V8處理js還會先把它編程字節碼),不少人只知道在預處理的時候會出現var變量的提高(es6的let就不會啦)和function一等公民的預先處理。然而卻忽略了做用域的處理--- 函數做用域的嵌套關係是定義時決定的,而不是調用時決定的,也就 是說,JavaScript 的做用域是靜態做用域,又叫詞法做用域,這是由於做用域的嵌套關係可 以在語法分析時肯定,而沒必要等到運行時肯定。編程

    2. this與做用域的誰在變化app

      • 首先讓咱們來理解下scope的概念---一段程序代碼中所用到的名字並不老是有效/可用的,而限定這個名字的可用性的代碼範圍就是這個名字的做用域。在網上老是有什麼前端面試題問setTimeout或者setInterval裏面的變量爲何會改變,給出的答案永遠是千篇一概的做用域發生了變化,然而咱們從第一段代碼中咱們就知道js是一個在語法分析時做用域就已經肯定了的。那究竟是什麼發生了變化了呢?是this(學名上下文對象),this這個值在js中是很詭異的,等我有時間專門要拿出來說一講,this這個值是會在運行時動態的發生變化的,好比call,apply,bind。至於setTimeout和setInterval一對兄弟,我想再分享一段代碼來解釋下dom

        function func(){
            var a=1;
            setTimeout(function(){
                console.log(a)
            },100);
            while(true){
                a=2;
            }
        }
        func();

        四段代碼中你能夠看見1,但在這段代碼中你永遠也不會再控制檯上發現1 的影子,由於只要沒執行完func這個函數,js永遠不會去事件隊列裏面查詢是否有事件發生。同時js的變量回收也是在執行完一個函數後才執行的。函數

    3. this和做用域的彙總介紹this

      • 第三段代碼的彙總使用啦,temp會從function中發揮obj對象,在咱們調用temp.func的時候,this值會被綁定在obj這個對象上,顯示console.log(b),由於b是從做用域中拿到的,因此在語法分析時就已經給設定好啦,因此在控制檯上打印出「func.b」,在調用this.a時,由於this被運行時綁定在obj對象上,因此會直接在obj上找(若是對象沒有能夠繼續向上找原形鏈),當變量func去獲得obj.func時,他只獲得的是函數(不帶包含它的對象),this自動會綁在window上,因此只能打印出b來(在語法分析時就已經註定了訪問哪一個變量),因此在window上找不到a這個屬性,就致使了undefined的出現。


  • 總結

    1. 總而言之,做用域在語法分析時就已經處理完啦,JavaScript 的做用域是靜態做用域,在運行時只是this(上下文對象)在一直髮生變化。也就是一個在運行前就完成劃分(詞法做用域Lexical Scope),一個是在運行中改變(相似於動態做用域Dynamic Scope),嚴格意義上說JavaScript只是詞法做用域。

    2. js只有function來劃分做用域。this的改變方式就大概有三種,第一種call,apply,bind方法(有點相似於c++的組合型配接器)this會被綁定在第一個參數對象,第二種事件方法,setTimeout和setInterval會被綁定在全局對象上,像點擊事件之類的會被綁定在dom對象上,第三種obj.func訪問對象裏的function會被綁定在最裏面的對象上,例如obj1.obj2.obj3.func會綁定在obj3上(有人會說function裏面也能夠寫this的屬性,對呀function也是繼承自Object的呀,它本身自己就帶上下文對象的)

    3. 做用域能夠訪問嵌套它的做用域值,而this是按着原形鏈去訪問它父級對象,注意啦就如上例obj1.obj2.obj3.func,obj3雖然是obj2的屬性,但不是繼承於obj2的,它繼承是經過它prototype屬性來繼承的,因此在obj3.func中this並不會去訪問obj2的屬性


  • 後記

    不但願之後有人會拿以上代碼來面試或以以上代碼去面試企業,這些知識知道就行了,實戰能力和對業務的把控纔是程序猿技術的關鍵。

[1](http://www.cnblogs.com/bennman/archive/2013/09/08/3309024.html

相關文章
相關標籤/搜索