<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      深入理解JavaScript系列(20):《你真懂JavaScript嗎?》答案詳解

      2012-02-10 08:54  湯姆大叔  閱讀(46903)  評論(122)    收藏  舉報

      介紹

      昨天發(fā)的《大叔手記(19):你真懂JavaScript嗎?》里面的5個題目,有很多回答,發(fā)現(xiàn)強人還是很多的,很多人都全部答對了。

      今天我們來對這5個題目詳細分析一下,希望對大家有所幫助。

      注:

      問題來自大名鼎鼎的前端架構師Baranovskiy的帖子《So, you think you know JavaScript?》。
      答案也是來自大名鼎鼎的JS牛人Nicholas C. Zakas的帖子《Answering Baranovskiy’s JavaScript quiz》——《JavaScript高級程序設計》一書的原作者
      (但題目2的解釋貌似有點問題)

      OK,我們先看第一題

      題目1

      if (!("a" in window)) {
      var a = 1;
      }
      alert(a);

      代碼看起來是想說:如果window不包含屬性a,就聲明一個變量a,然后賦值為1。

      你可能認為alert出來的結果是1,然后實際結果是“undefined”。要了解為什么,我們需要知道JavaScript里的3個概念。

      首先,所有的全局變量都是window的屬性,語句 var a = 1;等價于window.a = 1; 你可以用如下方式來檢測全局變量是否聲明:

      "變量名稱" in window

      第二,所有的變量聲明都在范圍作用域的頂部,看一下相似的例子:

      alert("a" in window);
      var a;

      此時,盡管聲明是在alert之后,alert彈出的依然是true,這是因為JavaScript引擎首先會掃墓所有的變量聲明,然后將這些變量聲明移動到頂部,最終的代碼效果是這樣的:

      var a;
      alert("a" in window);

      這樣看起來就很容易解釋為什么alert結果是true了。

      第三,你需要理解該題目的意思是,變量聲明被提前了,但變量賦值沒有,因為這行代碼包括了變量聲明和變量賦值。

      你可以將語句拆分為如下代碼:

      var a;    //聲明
      a = 1; //初始化賦值

      當變量聲明和賦值在一起用的時候,JavaScript引擎會自動將它分為兩部以便將變量聲明提前,不將賦值的步驟提前是因為他有可能影響代碼執(zhí)行出不可預期的結果。

      所以,知道了這些概念以后,重新回頭看一下題目的代碼,其實就等價于:

      var a;
      if (!("a" in window)) {
      a = 1;
      }
      alert(a);

      這樣,題目的意思就非常清楚了:首先聲明a,然后判斷a是否在存在,如果不存在就賦值為1,很明顯a永遠在window里存在,這個賦值語句永遠不會執(zhí)行,所以結果是undefined。

      大叔注:提前這個詞語顯得有點迷惑了,其實就是執(zhí)行上下文的關系,因為執(zhí)行上下文分2個階段:進入執(zhí)行上下文和執(zhí)行代碼,在進入執(zhí)行上下文的時候,創(chuàng)建變量對象VO里已經(jīng)有了:函數(shù)的所有形參、所有的函數(shù)聲明、所有的變量聲明

      VO(global) = {
      a: undefined
      }

      這個時候a已經(jīng)有了;

      然后執(zhí)行代碼的時候才開始走if語句,詳細信息請查看《深入理解JavaScript系列(12):變量對象(Variable Object)》中的處理上下文代碼的2個階段小節(jié)。

      大叔注:相信很多人都是認為a在里面不可訪問,結果才是undefined的吧,其實是已經(jīng)有了,只不過初始值是undefined,而不是不可訪問。

      題目2

      var a = 1,
      b = function a(x) {
      x && a(--x);
      };
      alert(a);

      這個題目看起來比實際復雜,alert的結果是1;這里依然有3個重要的概念需要我們知道。

      首先,在題目1里我們知道了變量聲明在進入執(zhí)行上下文就完成了;第二個概念就是函數(shù)聲明也是提前的,所有的函數(shù)聲明都在執(zhí)行代碼之前都已經(jīng)完成了聲明,和變

      量聲明一樣。澄清一下,函數(shù)聲明是如下這樣的代碼:

      function functionName(arg1, arg2){
      //函數(shù)體
      }

      如下不是函數(shù),而是函數(shù)表達式,相當于變量賦值:

      var functionName = function(arg1, arg2){
      //函數(shù)體
      };

      澄清一下,函數(shù)表達式?jīng)]有提前,就相當于平時的變量賦值。

      第三需要知道的是,函數(shù)聲明會覆蓋變量聲明,但不會覆蓋變量賦值,為了解釋這個,我們來看一個例子:

      function value(){
      return 1;
      }
      var value;
      alert(typeof value); //"function"

      盡快變量聲明在下面定義,但是變量value依然是function,也就是說這種情況下,函數(shù)聲明的優(yōu)先級高于變量聲明的優(yōu)先級,但如果該變量value賦值了,那結果就完全不一樣了:

      function value(){
      return 1;
      }
      var value = 1;
      alert(typeof value); //"number"

      該value賦值以后,變量賦值初始化就覆蓋了函數(shù)聲明。

      重新回到題目,這個函數(shù)其實是一個有名函數(shù)表達式,函數(shù)表達式不像函數(shù)聲明一樣可以覆蓋變量聲明,但你可以注意到,變量b是包含了該函數(shù)表達式,而該函數(shù)表達式的名字是a;不同的瀏覽器對a這個名詞處理有點不一樣,在IE里,會將a認為函數(shù)聲明,所以它被變量初始化覆蓋了,就是說如果調(diào)用a(--x)的話就會出錯,而其它瀏覽器在允許在函數(shù)內(nèi)部調(diào)用a(--x),因為這時候a在函數(shù)外面依然是數(shù)字。基本上,IE里調(diào)用b(2)的時候會出錯,但其它瀏覽器則返回undefined。

      理解上述內(nèi)容之后,該題目換成一個更準確和更容易理解的代碼應該像這樣:

      var a = 1,
      b = function(x) {
      x && b(--x);
      };
      alert(a);

      這樣的話,就很清晰地知道為什么alert的總是1了,詳細內(nèi)容請參考《深入理解JavaScript系列(2):揭秘命名函數(shù)表達式》中的內(nèi)容。

      大叔注:安裝ECMAScript規(guī)范,作者對函數(shù)聲明覆蓋變量聲明的解釋其實不準確的,正確的理解應該是如下:

      進入執(zhí)行上下文: 這里出現(xiàn)了名字一樣的情況,一個是函數(shù)申明,一個是變量申明。那么,根據(jù)深入理解JavaScript系列(12):變量對象(Variable Object)介紹的,填充VO的順序是: 函數(shù)的形參 -> 函數(shù)申明 -> 變量申明。
      上述例子中,變量a在函數(shù)a后面,那么,變量a遇到函數(shù)a怎么辦呢?還是根據(jù)變量對象中介紹的,當變量申明遇到VO中已經(jīng)有同名的時候,不會影響已經(jīng)存在的屬性。而函數(shù)表達式不會影響VO的內(nèi)容,所以b只有在執(zhí)行的時候才會觸發(fā)里面的內(nèi)容。

      題目3

      function a(x) {
      return x * 2;
      }
      var a;
      alert(a);

      這個題目就是題目2里的大叔加的注釋了,也就是函數(shù)聲明和變量聲明的關系和影響,遇到同名的函數(shù)聲明,VO不會重新定義,所以這時候全局的VO應該是如下這樣的:

      VO(global) = {
      a: 引用了函數(shù)聲明“a”
      }

      而執(zhí)行a的時候,相應地就彈出了函數(shù)a的內(nèi)容了。

      題目4

      function b(x, y, a) {
      arguments[2] = 10;
      alert(a);
      }
      b(1, 2, 3);

      關于這個題目,NC搬出了262-3的規(guī)范出來解釋,其實從《深入理解JavaScript系列(12):變量對象(Variable Object)》中的函數(shù)上下文中的變量對象一節(jié)就可以清楚地知道,活動對象是在進入函數(shù)上下文時刻被創(chuàng)建的,它通過函數(shù)的arguments屬性初始化。arguments屬性的值是Arguments對象:

      AO = {
      arguments: <ArgO>
      };

      Arguments對象是活動對象的一個屬性,它包括如下屬性:

      1. callee — 指向當前函數(shù)的引用
      2. length — 真正傳遞的參數(shù)個數(shù)
      3. properties-indexes (字符串類型的整數(shù)) 屬性的值就是函數(shù)的參數(shù)值(按參數(shù)列表從左到右排列)。 properties-indexes內(nèi)部元素的個數(shù)等于arguments.length. properties-indexes 的值和實際傳遞進來的參數(shù)之間是共享的。

      這個共享其實不是真正的共享一個內(nèi)存地址,而是2個不同的內(nèi)存地址,使用JavaScript引擎來保證2個值是隨時一樣的,當然這也有一個前提,那就是這個索引值要小于你傳入的參數(shù)個數(shù),也就是說如果你只傳入2個參數(shù),而還繼續(xù)使用arguments[2]賦值的話,就會不一致,例如:

      function b(x, y, a) {
      arguments[2] = 10;
      alert(a);
      }
      b(1, 2);

      這時候因為沒傳遞第三個參數(shù)a,所以賦值10以后,alert(a)的結果依然是undefined,而不是10,但如下代碼彈出的結果依然是10,因為和a沒有關系。

      function b(x, y, a) {
      arguments[2] = 10;
      alert(arguments[2]);
      }
      b(1, 2);

      題目5

      function a() {
      alert(this);
      }
      a.call(null);

      這個題目可以說是最簡單的,也是最詭異的,因為如果沒學到它的定義的話,打死也不會知道結果的,關于這個題目,我們先來了解2個概念。

      首先,就是this值是如何定義的,當一個方法在對象上調(diào)用的時候,this就指向到了該對象上,例如:

      var object = {
      method: function() {
      alert(this === object); //true
      }
      }
      object.method();

      上面的代碼,調(diào)用method()的時候this被指向到調(diào)用它的object對象上,但在全局作用域里,this是等價于window(瀏覽器中,非瀏覽器里等價于global),在如果一個function的定義不是屬于一個對象屬性的時候(也就是單獨定義的函數(shù)),函數(shù)內(nèi)部的this也是等價于window的,例如:

      function method() {
      alert(this === window); //true
      }
      method();

      了解了上述概念之后,我們再來了解一下call()是做什么的,call方法作為一個function執(zhí)行代表該方法可以讓另外一個對象作為調(diào)用者來調(diào)用,call方法的第一個參數(shù)是對象調(diào)用者,隨后的其它參數(shù)是要傳給調(diào)用method的參數(shù)(如果聲明了的話),例如:

      function method() {
      alert(this === window);
      }
      method(); //true
      method.call(document); //false

      第一個依然是true沒什么好說的,第二個傳入的調(diào)用對象是document,自然不會等于window,所以彈出了false。

      另外,根據(jù)ECMAScript262規(guī)范規(guī)定:如果第一個參數(shù)傳入的對象調(diào)用者是null或者undefined的話,call方法將把全局對象(也就是window)作為this的值。所以,不管你什么時候傳入null,其this都是全局對象window,所以該題目可以理解成如下代碼:

      function a() {
      alert(this);
      }
      a.call(window);

      所以彈出的結果是[object Window]就很容易理解了。

      總結

      這5個題目雖然貌似有點偏,但實際上考察的依然是基本概念,只有熟知了這些基本概念才能寫出高質(zhì)量代碼。

      關于JavaScript的基本核心內(nèi)容和理解基本上在該系列就到此為止了,接下來的章節(jié)除了把五大原則剩余的2篇補全依然,會再加兩篇關于DOM的文章,然后就開始轉(zhuǎn)向整理關于JavaScript模式與設計模式相關的文章了(大概10篇左右),隨后再會花幾個章節(jié)來一個實戰(zhàn)系列。

      更多題目

      如果大家有興趣,可以繼續(xù)研究下面的一些題目,詳細通過這些題目也可以再次加深對JavaScript基礎核心特性的理解。

      大叔注:這些題目也是來自出這5個題目的人,當然如果你能答對4個及以上并且想拿高工資的話,請聯(lián)系我。

      1. 找出數(shù)字數(shù)組中最大的元素(使用Match.max函數(shù))
      2. 轉(zhuǎn)化一個數(shù)字數(shù)組為function數(shù)組(每個function都彈出相應的數(shù)字)
      3. 給object數(shù)組進行排序(排序條件是每個元素對象的屬性個數(shù))
      4. 利用JavaScript打印出Fibonacci數(shù)(不使用全局變量)
      5. 實現(xiàn)如下語法的功能:var a = (5).plus(3).minus(6); //2
      6. 實現(xiàn)如下語法的功能:var a = add(2)(3)(4); //9

      同步與推薦

      本文已同步至目錄索引:深入理解JavaScript系列

      深入理解JavaScript系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。

      主站蜘蛛池模板: 成人免费无码av| 男女性高爱潮免费网站| 依依成人精品视频在线观看| 美女一级毛片无遮挡内谢| 欧美激情一区二区三区成人| 国产成人a在线观看视频| 欧美日韩国产va在线观看免费 | 狠狠色噜噜狠狠狠狠777米奇| 国产亚洲999精品AA片在线爽| 亚洲中文字幕无码一区无广告 | 中文字幕第55页一区| 国产 另类 在线 欧美日韩| 亚洲成人四虎在线播放| 97一区二区国产好的精华液| 成人午夜污一区二区三区| 实拍女处破www免费看| 亚洲国产美女精品久久久| 久久这里只有精品好国产| 人妻丝袜无码专区视频网站| 水富县| 18禁黄网站禁片免费观看| 免费无码一区无码东京热| 上司的丰满人妻中文字幕| 中文国产不卡一区二区| 亚洲熟女精品一区二区| 337p日本欧洲亚洲大胆色噜噜| 国产一区二区黄色激情片| 日韩不卡一区二区在线观看| 江安县| 午夜福利日本一区二区无码| 大地资源中文第三页| 久久综合狠狠综合久久激情| 欧美成人午夜在线观看视频| 北京市| 丰满熟女人妻一区二区三| 99热精品国产三级在线观看| 日韩精品一区二区亚洲专区| 好吊视频一区二区三区在线| 国产精品视频一品二区三| 中文字幕国产精品二区| 日韩中文免费一区二区|