整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          手把手教會你JavaScript引擎如何執行Java

          手把手教會你JavaScript引擎如何執行JavaScript代碼

          avaScript 在運行過程中與其他語言有所不一樣,如果不理解 JavaScript 的詞法環境、執行上下文等內容,很容易會在開發過程中產生 Bug,比如this指向和預期不一致、某個變量不知道為什么被改了,等等。所以今天我們就來聊一聊 JavaScript 代碼的運行過程。

          大家都知道,JavaScript 代碼是需要在 JavaScript 引擎中運行的。我們在說到 JavaScript 運行的時候,常常會提到執行環境、詞法環境、作用域、執行上下文、閉包等內容。這些概念看起來都差不多,卻好像又不大容易區分清楚,它們分別都在描述什么呢?

          這些詞語都是與 JavaScript 引擎執行代碼的過程有關,為了搞清楚這些概念之間的區別,我們可以回顧下 JavaScript 代碼運行過程中的各個階段。


          JavaScript 代碼運行的各個階段

          JavaScript 是弱類型語言,在運行時才能確定變量類型。JavaScript 引擎在執行 JavaScript 代碼時,也會從上到下進行詞法分析、語法分析、語義分析等處理,并在代碼解析完成后生成 AST(抽象語法樹),最終根據 AST 生成 CPU 可以執行的機器碼并執行。

          這個過程,我們稱之為語法分析階段。除了語法分析階段,JavaScript 引擎在執行代碼時還會進行其他的處理。以 V8 引擎為例,在 V8 引擎中 JavaScript 代碼的運行過程主要分成三個階段。

          1. 語法分析階段。該階段會對代碼進行語法分析,檢查是否有語法錯誤(SyntaxError),如果發現語法錯誤,會在控制臺拋出異常并終止執行。
          1. 編譯階段。該階段會進行執行上下文(Execution Context)的創建,包括創建變量對象、建立作用域鏈、確定 this 的指向等。每進入一個不同的運行環境時,V8 引擎都會創建一個新的執行上下文。
          2. 執行階段。將編譯階段中創建的執行上下文壓入調用棧,并成為正在運行的執行上下文,代碼執行結束后,將其彈出調用棧。

          其中,語法分析階段屬于編譯器通用內容,就不再贅述。前面提到的執行環境、詞法環境、作用域、執行上下文等內容都是在編譯和執行階段中產生的概念。


          執行上下文的創建

          執行上下文的創建離不開 JavaScript 的運行環境,JavaScript 運行環境包括全局環境、函數環境和eval,其中全局環境和函數環境的創建過程如下:

          1. 第一次載入 JavaScript 代碼時,首先會創建一個全局環境。全局環境位于最外層,直到應用程序退出后(例如關閉瀏覽器和網頁)才會被銷毀。
          2. 每個函數都有自己的運行環境,當函數被調用時,則會進入該函數的運行環境。當該環境中的代碼被全部執行完畢后,該環境會被銷毀。不同的函數運行環境不一樣,即使是同一個函數,在被多次調用時也會創建多個不同的函數環境。


          在不同的運行環境中,變量和函數可訪問的其他數據范圍不同,環境的行為(比如創建和銷毀)也有所區別。而每進入一個不同的運行環境時,JavaScript 都會創建一個新的執行上下文,該過程包括:

          • 建立作用域鏈(Scope Chain);
          • 創建變量對象(Variable Object,簡稱 VO);
          • 確定 this 的指向。

          由于建立作用域鏈過程中會涉及變量對象的概念,因此我們先來看看變量對象的創建,再看建立作用域鏈和確定 this 的指向。


          創建變量對象

          變量對象(VO)

          每個執行上下文都會有一個關聯的變量對象,該對象上會保存這個上下文中定義的所有變量和函數。

          在瀏覽器中,全局環境的變量對象是window對象,因此所有的全局變量和函數都是作為window對象的屬性和方法創建的。相應的,在 Node 中全局環境的變量對象則是global對象。


          創建VO的過程

          創建變量對象將會創建arguments對象(僅函數環境下),同時會檢查當前上下文的函數聲明和變量聲明。

          • 對于變量聲明:此時會給變量分配內存,并將其初始化為undefined(該過程只進行定義聲明,執行階段才執行賦值語句)。
          • 對于函數聲明:此時會在內存里創建函數對象,并且直接初始化為該函數對象。


          變量聲明和函數聲明的處理過程,便是我們常說的變量提升和函數提升,其中函數聲明提升會優先于變量聲明提升。因為變量提升容易帶來變量在預期外被覆蓋掉的問題,同時還可能導致本應該被銷毀的變量沒有被銷毀等情況。因此 ES6 中引入了let和const關鍵字,從而使 JavaScript 也擁有了塊級作用域。


          作用域

          在各類編程語言中,作用域分為靜態作用域和動態作用域。JavaScript 采用的是詞法作用域(Lexical Scoping),也就是靜態作用域。詞法作用域中的變量,在編譯過程中會產生一個確定的作用域。


          詞法作用域中的變量,在編譯過程中會產生一個確定的作用域,這個作用域即當前的執行上下文,在 ES5 后我們使用詞法環境(Lexical Environment)替代作用域來描述該執行上下文。因此,詞法環境可理解為我們常說的作用域,同樣也指當前的執行上下文(注意,是當前的執行上下文)。


          在 JavaScript 中,詞法環境又分為詞法環境(Lexical Environment)和變量環境(Variable Environment)兩種,其中:

          • 變量環境用來記錄var/function等變量聲明;
          • 詞法環境是用來記錄let/const/class等變量聲明。

          也就是說,創建變量過程中會進行函數提升和變量提升,JavaScript 會通過詞法環境來記錄函數和變量聲明。通過使用兩個詞法環境(而不是一個)分別記錄不同的變量聲明內容,JavaScript 實現了支持塊級作用域的同時,不影響原有的變量聲明和函數聲明。

          這就是創建變量的過程,它屬于執行上下文創建中的一環。創建變量的過程會產生作用域,作用域也被稱為詞法環境。


          建立作用域鏈

          作用域鏈,就是將各個作用域通過某種方式連接在一起。作用域就是詞法環境,而詞法環境由兩個成員組成。

          1. 環境記錄(Environment Record):用于記錄自身詞法環境中的變量對象。
          2. 外部詞法環境引用(Outer Lexical Environment):記錄外層詞法環境的引用。

          通過外部詞法環境的引用,作用域可以層層拓展,建立起從里到外延伸的一條作用域鏈。當某個變量無法在自身詞法環境記錄中找到時,可以根據外部詞法環境引用向外層進行尋找,直到最外層的詞法環境中外部詞法環境引用為null,這便是作用域鏈的變量查詢。

          JavaScript 代碼運行過程分為定義期和執行期,前面提到的編譯階段則屬于定義期,代碼示例如下:

          function foo() { // 定義全局函數foo
              console.dir(bar);
              var a=1;
              function bar() { // 在foo函數內部定義函數bar
                  a=2;
           }
          }
          console.dir(foo);
          foo();

          前面我們說到,JavaScript 使用的是靜態作用域,因此函數的作用域在定義期已經決定了。在上面的例子中,全局函數foo創建了一個foo[[scope]]屬性,包含了全局[[scope]]

          foo[[scope]]=[globalContext];

          而當我們執行foo()時,也會分別進入foo函數的定義期和執行期。

          在foo函數的定義期時,函數bar的[[scope]]將會包含全局[[scope]]和foo的[[scope]]:

          bar[[scope]]=[fooContext, globalContext];

          運行上述代碼,我們可以在控制臺看到符合預期的輸出:

          可以看到:

          • foo的[[scope]]屬性包含了全局[[scope]]
          • bar的[[scope]]將會包含全局[[scope]]和foo的[[scope]]

          也就是說,JavaScript 會通過外部詞法環境引用來創建變量對象的一個作用域鏈,從而保證對執行環境有權訪問的變量和函數的有序訪問。除了創建作用域鏈之外,在這個過程中還會對創建的變量對象做一些處理。

          在編譯階段會進行變量對象(VO)的創建,該過程會進行函數聲明和變量聲明,這時候變量的值被初始化為 undefined。在代碼進入執行階段之后,JavaScript 會對變量進行賦值,此時變量對象會轉為活動對象(Active Object,簡稱 AO),轉換后的活動對象才可被訪問,這就是 VO -> AO 的過程,示例如下:

          function foo(a) {
              var b=2; 
              function c() {}
              var d=function() {};
          }
          
          
          foo(1);

          在執行foo(1)時,首先進入定義期,此時:

          • 參數變量a的值為1
          • 變量b和d初始化為undefined
          • 函數c創建函數并初始化
          AO={
           arguments: {
            0: 1,
            length: 1
           },
           a: 1,
           b: undefined,
           c: reference to function() c() {}
           d:undefined
          }

          前面我們也有提到,進入執行期之后,會執行賦值語句進行賦值,此時變量b和d會被賦值為 2 和函數表達式:

          AO={
             arguments: {
              0: 1,
              length: 1
            },
            a: 1,
            b: 2,
            c: reference to function c(){},
            d: reference to FunctionExpression "d"
          }

          這就是 VO -> AO 過程。

          • 在定義期(編譯階段):該對象值仍為undefined,且處于不可訪問的狀態。
          • 進入執行期(執行階段):VO 被激活,其中變量屬性會進行賦值。

          實際上在執行的時候,除了 VO 被激活,活動對象還會添加函數執行時傳入的參數和arguments這個特殊對象,因此 AO 和 VO 的關系可以用以下關系來表達:

          AO=VO + function parameters + arguments

          現在,我們知道作用域鏈是在進入代碼的執行階段時,通過外部詞法環境引用來創建的。總結如下:

          • 在編譯階段,JavaScript 在創建執行上下文的時候會先創建變量對象(VO);
          • 在執行階段,變量對象(VO)被激活為活動對象( AO),函數內部的變量對象通過外部詞法環境的引用創建作用域鏈。

          通過作用域鏈,我們可以在函數內部可以直接讀取外部以及全局變量,但外部環境是無法訪問內部函數里的變量。示例如下:

          function foo() {
            var a=1;
          }
          foo();
          console.log(a); // undefined

          我們在全局環境下無法訪問函數foo中的變量a,這是因為全局函數的作用域鏈里,不含有函數foo內的作用域。

          如果我們想要訪問內部函數的變量,可以通過函數foo中的函數bar返回變量a,并將函數bar返回,這樣我們在全局環境中也可以通過調用函數foo返回的函數bar,來訪問變量a:

          function foo() {
            var a=1;
            function bar() {
              return a;
            }
            return bar;
          }
          var b=foo();
          console.log(b()); // 1

          當函數執行結束之后,執行期上下文將被銷毀,其中包括作用域鏈和激活對象。

          在上面的實例中;當b()執行時,foo函數上下文包括作用域都已經被銷毀了,但是foo作用域下的a依然可以被訪問到;這是因為bar函數引用了foo函數變量對象中的值,此時即使創建bar函數的foo函數執行上下文被銷毀了,但它的變量對象依然會保留在 JavaScript 內存中,bar函數依然可以通過bar函數的作用域鏈找到它,并進行訪問。這就是閉包;

          閉包使得我們可以從外部讀取局部變量,常見的用途包括:

          1. 用于從外部讀取其他函數內部變量的函數;
          2. 可以使用閉包來模擬私有方法;
          3. 讓這些變量的值始終保持在內存中。

          注意,在使用閉包的時候,需要及時清理不再使用到的變量,否則可能導致內存泄漏問題。


          確定 this 的指向

          在 JavaScript 中,this指向執行當前代碼對象的所有者,可簡單理解為this指向最后調用當前代碼的那個對象。

          根據 JavaScript 中函數的調用方式不同,this的指向分為以下情況。

          1. 在全局環境中,this指向全局對象(在瀏覽器中為window)
          2. 在函數內部,this的值取決于函數被調用的方式
          3. 函數作為對象的方法被調用,this指向調用這個方法的對象
          4. 函數用作構造函數時(使用new關鍵字),它的this被綁定到正在構造的新對象
          5. 在類的構造函數中,this是一個常規對象,類中所有非靜態的方法都會被添加到this的原型中
          6. 在箭頭函數中,this指向它被創建時的環境
          7. 使用apply、call、bind等方式調用:根據 API 不同,可切換函數執行的上下文環境,即this綁定的對象

          可以看到,this在不同的情況下會有不同的指向,在 ES6 箭頭函數還沒出現之前,為了能正確獲取某個運行環境下this對象,我們常常會使用以下代碼:

          var that=this;
          var self=this;

          這樣的代碼將變量分配給this,便于使用。但是降低了代碼可讀性,不推薦使用,通過正確使用箭頭函數,我們可以更好地管理作用域。


          總結

          今天我們了解了 JavaScript 代碼的運行過程,該過程分為語法分析階段、編譯階段、執行階段三個階段。

          在編譯階段,JavaScript會進行執行上下文的創建,在執行階段,變量對象(VO)會被激活為活動對象(AO),變量會進行賦值,此時活動對象才可被訪問。在執行結束之后,作用域鏈和活動對象均被銷毀,使用閉包可使活動對象依然被保留在內存中。這就是 JavaScript 代碼的運行過程。

          這里是云端源想IT,幫你輕松學IT”

          嗨~ 今天的你過得還好嗎?

          我們總是先揚起塵土

          然后抱怨自己看不見

          - 2024.04.17 -

          JavaScript是一種輕量級的編程語言,通常用于網頁開發,以增強用戶界面的交互性和動態性。然而在HTML中,有多種方法可以嵌入和使用JavaScript代碼。

          本文就帶大家深入了解如何在HTML中使用JavaScript。



          一、使用 script 標簽

          要在HTML中使用JavaScript,我們需要使用<script>標簽。這個標簽可以放在<head>或<body>部分,但通常我們會將其放在<body>部分的底部,以確保在執行JavaScript代碼時,HTML文檔已經完全加載。

          使用 <script> 標簽有兩種方式:直接在頁面中嵌入 JavaScript 代碼包含外部 JavaScript 文件。


          包含在 <script> 標簽內的 JavaScript 代碼在瀏覽器總按照從上至下的順序依次解釋。


          所有 <script> 標簽都會按照他們在 HTML 中出現的先后順序依次被解析。



          HTML 為 <script> 定義了幾個屬性:

          1)async:可選。表示應該立即下載腳本,但不妨礙頁面中其他操作。該功能只對外部 JavaScript 文件有效。


          如果給一個外部引入的js文件設置了這個屬性,那頁面在解析代碼的時候遇到這個<script>的時候,一邊下載該腳本文件,一邊異步加載頁面其他內容。


          2)defer:可選。表示腳本可以延遲到整個頁面完全被解析和顯示之后再執行。該屬性只對外部 JavaScript 文件有效。


          3)src:可選。表示包含要執行代碼的外部文件。


          4)type:可選。表示編寫代碼使用的腳本語言的內容類型,目前在客戶端,type 屬性值一般使用 text/javascript。不過這個屬性并不是必需的,如果沒有指定這個屬性,則其默認值仍為text/javascript。



          1.1 直接在頁面中嵌入JavaScript代碼

          內部JavaScript是將JavaScript代碼放在HTML文檔的<script>標簽中。這樣可以將JavaScript代碼與HTML代碼分離,使結構更清晰,易于維護。


          在使用<script>元素嵌入JavaScript代碼時,只須為<script>指定type屬性。然后,像下面這樣把JavaScript代碼直接放在元素內部即可:

          <script type="text/javascript">
          function sayHi(){
          alert("Hi!");
          }
          </script>


          如果沒有指定script屬性,則其默認值為text/javascript。


          包含在<script>元素內部的JavaScript代碼將被從上至下依次解釋。在解釋器對<script>元素內部的所有代碼求值完畢以前,頁面中的其余內容都不會被瀏覽器加載或顯示。


          在使用<script>嵌入JavaScript代碼的過程中,當代碼中出現"</script>"字符串時,由于解析嵌入式代碼的規則,瀏覽器會認為這是結束的</script>標簽。可以通過轉義字符“\”寫成<\/script>來解決這個問題。


          1.2 包含外部 JavaScript 文件

          外部JavaScript是將JavaScript代碼放在單獨的.js文件中,然后在HTML文檔中通過<script>標簽的src屬性引用這個文件。這種方法可以使代碼更加模塊化,便于重用和共享。


          如果要通過<script>元素來包含外部JavaScript文件,那么src屬性就是必需的。這個屬性的值是一個指向外部JavaScript文件的鏈接。

          <script type="text/javascript" src="example.js"></script>


          • 外部文件example.js將被加載到當前頁面中。
          • 外部文件只須包含通常要放在開始的<script>和結束的</script>之間的那些JavaScript代碼即可。



          與解析嵌入式JavaScript代碼一樣,在解析外部JavaScript文件(包括下載該文件)時,頁面的處理也會暫時停止。

          注意:帶有src屬性的<script>元素不應該在其<script>和</script>標簽之間再包含額外的JavaScript代碼。如果包含了嵌入的代碼,則只會下載并執行外部腳本文件,嵌入的代碼會被忽略。

          通過<script>元素的src屬性還可以包含來自外部域的JavaScript文件。它的src屬性可以是指向當前HTML頁面所在域之外的某個域中的完整URL。

          <script type="text/javascript" src="http://www.somewhere.com/afile.js"></script>

          于是,位于外部域中的代碼也會被加載和解析。


          1.3 標簽的位置

          在HTML中,所有的<script>標簽會按照它們出現的先后順序被解析。在不使用defer和async屬性的情況下,只有當前面的<script>標簽中的代碼解析完成后,才會開始解析后面的<script>標簽中的代碼。


          通常,所有的<script>標簽應該放在頁面的<head>標簽中,這樣可以將外部文件(包括CSS和JavaScript文件)的引用集中放置。



          然而,如果將所有的JavaScript文件都放在<head>標簽中,會導致瀏覽器在呈現頁面內容之前必須下載、解析并執行所有JavaScript代碼,這可能會造成明顯的延遲,導致瀏覽器窗口在加載過程中出現空白。


          為了避免這種延遲問題,現代Web應用程序通常會將所有的JavaScript引用放置在<body>標簽中的頁面內容的后面。這樣做可以確保在解析JavaScript代碼之前,頁面的內容已經完全呈現在瀏覽器中,從而加快了打開網頁的速度。


          二、執行JavaScript 程序

          JavaScript 解析過程包括兩個階段:預處理(也稱預編譯)執行

          • 在編譯期,JavaScript 解析器將完成對 JavaScript 代碼的預處理操作,把 JavaScript 代碼轉換成字節碼;
          • 在執行期,JavaScript 解析器把字節碼生成二進制機械碼,并按順序執行,完成程序設計的任務。


          1、執行過程

          HTML 文檔在瀏覽器中的解析過程是:按照文檔流從上到下逐步解析頁面結構和信息。

          JavaScript 代碼作為嵌入的腳本應該也算做 HTML 文檔的組成部分,所以 JavaScript 代碼在裝載時的執行順序也是根據 <script> 標簽出現的順序來確定。

          你是不是厭倦了一成不變的編程模式?想要突破自我,挑戰新技術想要突破自我,挑戰新技術?卻遲遲找不到可以練手的項目實戰?是不是夢想打造一個屬于自己的支付系統?那么,恭喜你,云端源想免費實戰直播——《微實戰-使用支付寶/微信支付服務,網站在線支付功能大揭秘》正在進行,點擊前往獲取源碼!云端源想

          2、預編譯

          當 JavaScript 引擎解析腳本時候,他會在與編譯期對所有聲明的變量和函數預先進行處理。當 JavaScript 解析器執行下面腳本時不會報錯。

          alert(a); //返回值 undefined
          var a=1;
          alert(a); //返回值 1


          由于變量聲明是在預編譯期被處理的,在執行期間對于所有的代碼來說,都是可見的,但是執行上面代碼,提示的值是 undefined 而不是 1。

          因為變量初始化過程發生在執行期,而不是預編譯期。在執行期,JavaScript 解析器是按照代碼先后順序進行解析的,如果在前面代碼行中沒有為變量賦值,則 JavaScript 解析器會使用默認值 undefined 。


          由于第二行中為變量 a 賦值了,所以在第三行代碼中會提示變量 a 的值為 1,而不是 undefined。

          fun(); //調用函數,返回值1
          function fun(){
          alert(1);
          }

          函數聲明前調用函數也是合法的,并能夠正確解析,所以返回值是 1。但如果是下面這種方式則 JavaScript 解釋器會報錯。

          fun(); //調用函數,返回語法錯誤
          var fun=function(){
          alert(1);
          }

          上面的這個例子中定義的函數僅作為值賦值給變量 fun 。在預編譯期,JavaScript 解釋器只能夠為聲明變量 fun 進行處理,而對于變量 fun 的值,只能等到執行期時按照順序進行賦值,自然就會出現語法錯誤,提示找不到對象 fun。

          總結:聲明變量和函數可以在文檔的任意位置,但是良好的習慣應該是在所有 JavaScript 代碼之前聲明全局變量和函數,并對變量進行初始化賦值。在函數內部也是先聲明變量,后引用。

          通過今天的分享,相信大家已經對JavaScript在HTML中的應用有了一定的了解。這只是冰山一角,JavaScript的潛力遠不止于此。希望這篇文章能激發大家對編程的熱情,讓我們一起在編程的世界里探索更多的可能性!



          我們下期再見!


          END

          文案編輯|云端學長

          文案配圖|云端學長

          內容由:云端源想分享

          了執行Javascript,需要在HTML文件內以特定的方式書寫JavaScript的代碼,JavaScript的書寫方法有多種,其執行的流程也各不相同:

          1 <script>標簽嵌入

          此種嵌入方法無法操作<script>之后的DOM元素。因為<script>之后的DOM元素還未構造,因此在<script>標簽內就無法取得位于其后的DOM元素。

          2 讀取外部JavaScript文件

          此種嵌入方法可以指定defer、async屬性。defer可以推遲執行,async可以異步執行。

          3 onload嵌入

          此種嵌入方法在頁面讀取完后再對其執行,所以可以對所有的DOM元素操作。

          <body onload="alert('hello')">
          window.onload=function(){alert('hello');};

          當window.onload事件觸發時,頁面上所有的DOM、樣式表、腳本、圖片、flash都已經加載完成了。

          //window.onload不能同時編寫多個。
          //以下代碼無法正確執行,結果只輸出第二個。
          window.onload=function(){
            alert("test1");
          };
          
          window.onload=function(){
            alert("test2");
          };
          
          //$(document).ready()能同時編寫多個
          //結果兩次都輸出
          $(document).ready(function(){ 
             alert("Hello World"); 
          }); 
          $(document).ready(function(){ 
             alert("Hello again"); 
          }); 

          window.onload和body中onload也有些許區別:

          <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
              <title></title>
              <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.0.js"></script>
              <script language="javascript">
                  window.onload=haha;
                  function haha(){console.log("window.onload");}
          
                  if(document.addEventListener){
                      function DOMContentLoaded(){
                          console.log("DOMContentLoaded");
                      }
                      document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                  }</script>
          </head>
          <body onload="console.log('bodyonload');">
                  <div id="div1">a</div>
          </body>
          </html>

          在IE10和FireFox下,結果為 :

          "DOMContentLoaded"
          "bodyonload"

          說明body中的onload會覆蓋window.onload

          在chrome下,結果為:

          DOMContentLoaded
          window.onload
          bodyonload

          然后,如果把javascript代碼移到最下面,結果又會是什么樣呢?

          chrome和IE10、FireFox的結果竟然是一樣的:

          DOMContentLoaded
          window.onload

          IE 10、Fire Fox可以理解,window.on load和body中的 on load 誰在下面就是誰覆蓋誰,只會執行后面的那個。

          4 DOM ContentLoaded嵌入

          onload方法可能需要等待時間,而本方法可以在完成HTML解析后發生的事件,減少等待時間。

          在chrome、IE10和FireFox中,執行結果是:DOMContentLoaded然后才是onload的輸出。所以說一般情況下,DOMContentLoaded事件要在window.onload之前執行,當DOM樹構建完成的時候就會執行DOMContentLoaded事件。

          <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
              <title></title>
              <script type="text/javascript" src="jquery2.js"></script>
              <script language="javascript">
                  window.onload=haha;
                  function haha(){console.log(document.getElementById("div1"));}
                  if(document.addEventListener){
                      function DOMContentLoaded(){
                          console.log("DOMContentLoaded");
                      }
                      document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                  }
              </script>
          </head>
          <body>
              <div id="div1">a</div>
          </body>
          </html>

          如果你是個jQuery使用者,你可能會經常使用$(document).ready();或者$(function(){}),這都是使用了DOMContentLoaded事件

          5 動態載入JavaScript文件

          5.1 使用原生js方法

          動態創建script標簽,并指定script的src屬性

          function loadJs(url, callback) {
              var script=document.createElement('script');
              script.type="text/javascript";
              if (typeof(callback) !="undefined") {
                  if (script.readyState) {
                      script.onreadystatechange=function() {
                          if (script.readyState=="loaded" || script.readyState=="complete") {
                              script.onreadystatechange=null;
                              callback();
                          }
                      }
                  } else {
                      script.onload=function() {
                          callback();
                      }
                  }
              }
              script.src=url;
              document.body.appendChild(script);
          }
          loadJs("test.js", function() {
              alert('done');
          });

          還可以使用同樣的原理動態加載css文件,只不過插入的的父節點是head標簽。

          5.2 使用document.write/writeln()方式

          該種方式可以實現js文件的動態加載,原理就是在重寫文檔流,這種方式會導致整個頁面重繪。

          document.writeln("<script src=\"http://lib.sinaapp.com/js/jquery/1.6/jquery.min.js\"></script>");

          需要注意的是特殊字符的轉義。

          5.3 使用jQuery

          使用getScript(url,callback)方法實現動態加載js文件

          $.getScript('test.js',function(){
              alert('done');
          });

          -End-


          主站蜘蛛池模板: 亚洲大尺度无码无码专线一区 | 中文字幕一区日韩在线视频 | 国产精品日韩欧美一区二区三区 | 亚洲AV无码一区二区乱孑伦AS| 精品午夜福利无人区乱码一区| 91久久精一区二区三区大全| 精品天海翼一区二区| 国产福利91精品一区二区三区 | 日本一区二区三区免费高清| 国产亚洲日韩一区二区三区 | 性色av无码免费一区二区三区| 少妇精品无码一区二区三区| 中文字幕无码一区二区三区本日| 国产日韩精品一区二区三区| 亚洲AV无码一区二区三区鸳鸯影院 | 色窝窝无码一区二区三区色欲| 合区精品久久久中文字幕一区| 精品一区二区三区电影| 亚洲av无码成人影院一区| 色狠狠色狠狠综合一区| 色狠狠色狠狠综合一区| 国产精品视频一区麻豆| 亚洲日韩中文字幕无码一区| 伊人久久大香线蕉AV一区二区| 伊人久久精品一区二区三区| 成人免费一区二区无码视频| 在线视频精品一区| 美女免费视频一区二区| 精品一区二区三区水蜜桃| 一区二区三区内射美女毛片 | 精品不卡一区二区| 性色AV一区二区三区天美传媒| 亚洲一区二区三区高清视频| 精品欧美一区二区在线观看 | 国产AV午夜精品一区二区三| 国产精品熟女一区二区| 精品欧洲av无码一区二区| 无码人妻精品一区二区三区99性 | 无码人妻精品一区二区三区在线| 亚洲国产精品第一区二区| 美女视频一区三区网站在线观看|