Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 免费观看日本,国内精品免费久久久久电影,国产嫩草影院精品免费网址

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          手把手教會(huì)你JavaScript引擎如何執(zhí)行Java

          手把手教會(huì)你JavaScript引擎如何執(zhí)行JavaScript代碼

          avaScript 在運(yùn)行過(guò)程中與其他語(yǔ)言有所不一樣,如果不理解 JavaScript 的詞法環(huán)境、執(zhí)行上下文等內(nèi)容,很容易會(huì)在開(kāi)發(fā)過(guò)程中產(chǎn)生 Bug,比如this指向和預(yù)期不一致、某個(gè)變量不知道為什么被改了,等等。所以今天我們就來(lái)聊一聊 JavaScript 代碼的運(yùn)行過(guò)程。

          大家都知道,JavaScript 代碼是需要在 JavaScript 引擎中運(yùn)行的。我們?cè)谡f(shuō)到 JavaScript 運(yùn)行的時(shí)候,常常會(huì)提到執(zhí)行環(huán)境、詞法環(huán)境、作用域、執(zhí)行上下文、閉包等內(nèi)容。這些概念看起來(lái)都差不多,卻好像又不大容易區(qū)分清楚,它們分別都在描述什么呢?

          這些詞語(yǔ)都是與 JavaScript 引擎執(zhí)行代碼的過(guò)程有關(guān),為了搞清楚這些概念之間的區(qū)別,我們可以回顧下 JavaScript 代碼運(yùn)行過(guò)程中的各個(gè)階段。


          JavaScript 代碼運(yùn)行的各個(gè)階段

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

          這個(gè)過(guò)程,我們稱之為語(yǔ)法分析階段。除了語(yǔ)法分析階段,JavaScript 引擎在執(zhí)行代碼時(shí)還會(huì)進(jìn)行其他的處理。以 V8 引擎為例,在 V8 引擎中 JavaScript 代碼的運(yùn)行過(guò)程主要分成三個(gè)階段。

          1. 語(yǔ)法分析階段。該階段會(huì)對(duì)代碼進(jìn)行語(yǔ)法分析,檢查是否有語(yǔ)法錯(cuò)誤(SyntaxError),如果發(fā)現(xiàn)語(yǔ)法錯(cuò)誤,會(huì)在控制臺(tái)拋出異常并終止執(zhí)行。
          1. 編譯階段。該階段會(huì)進(jìn)行執(zhí)行上下文(Execution Context)的創(chuàng)建,包括創(chuàng)建變量對(duì)象、建立作用域鏈、確定 this 的指向等。每進(jìn)入一個(gè)不同的運(yùn)行環(huán)境時(shí),V8 引擎都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。
          2. 執(zhí)行階段。將編譯階段中創(chuàng)建的執(zhí)行上下文壓入調(diào)用棧,并成為正在運(yùn)行的執(zhí)行上下文,代碼執(zhí)行結(jié)束后,將其彈出調(diào)用棧。

          其中,語(yǔ)法分析階段屬于編譯器通用內(nèi)容,就不再贅述。前面提到的執(zhí)行環(huán)境、詞法環(huán)境、作用域、執(zhí)行上下文等內(nèi)容都是在編譯和執(zhí)行階段中產(chǎn)生的概念。


          執(zhí)行上下文的創(chuàng)建

          執(zhí)行上下文的創(chuàng)建離不開(kāi) JavaScript 的運(yùn)行環(huán)境,JavaScript 運(yùn)行環(huán)境包括全局環(huán)境、函數(shù)環(huán)境和eval,其中全局環(huán)境和函數(shù)環(huán)境的創(chuàng)建過(guò)程如下:

          1. 第一次載入 JavaScript 代碼時(shí),首先會(huì)創(chuàng)建一個(gè)全局環(huán)境。全局環(huán)境位于最外層,直到應(yīng)用程序退出后(例如關(guān)閉瀏覽器和網(wǎng)頁(yè))才會(huì)被銷(xiāo)毀。
          2. 每個(gè)函數(shù)都有自己的運(yùn)行環(huán)境,當(dāng)函數(shù)被調(diào)用時(shí),則會(huì)進(jìn)入該函數(shù)的運(yùn)行環(huán)境。當(dāng)該環(huán)境中的代碼被全部執(zhí)行完畢后,該環(huán)境會(huì)被銷(xiāo)毀。不同的函數(shù)運(yùn)行環(huán)境不一樣,即使是同一個(gè)函數(shù),在被多次調(diào)用時(shí)也會(huì)創(chuàng)建多個(gè)不同的函數(shù)環(huán)境。


          在不同的運(yùn)行環(huán)境中,變量和函數(shù)可訪問(wèn)的其他數(shù)據(jù)范圍不同,環(huán)境的行為(比如創(chuàng)建和銷(xiāo)毀)也有所區(qū)別。而每進(jìn)入一個(gè)不同的運(yùn)行環(huán)境時(shí),JavaScript 都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文,該過(guò)程包括:

          • 建立作用域鏈(Scope Chain);
          • 創(chuàng)建變量對(duì)象(Variable Object,簡(jiǎn)稱 VO);
          • 確定 this 的指向。

          由于建立作用域鏈過(guò)程中會(huì)涉及變量對(duì)象的概念,因此我們先來(lái)看看變量對(duì)象的創(chuàng)建,再看建立作用域鏈和確定 this 的指向。


          創(chuàng)建變量對(duì)象

          變量對(duì)象(VO)

          每個(gè)執(zhí)行上下文都會(huì)有一個(gè)關(guān)聯(lián)的變量對(duì)象,該對(duì)象上會(huì)保存這個(gè)上下文中定義的所有變量和函數(shù)。

          在瀏覽器中,全局環(huán)境的變量對(duì)象是window對(duì)象,因此所有的全局變量和函數(shù)都是作為window對(duì)象的屬性和方法創(chuàng)建的。相應(yīng)的,在 Node 中全局環(huán)境的變量對(duì)象則是global對(duì)象。


          創(chuàng)建VO的過(guò)程

          創(chuàng)建變量對(duì)象將會(huì)創(chuàng)建arguments對(duì)象(僅函數(shù)環(huán)境下),同時(shí)會(huì)檢查當(dāng)前上下文的函數(shù)聲明和變量聲明。

          • 對(duì)于變量聲明:此時(shí)會(huì)給變量分配內(nèi)存,并將其初始化為undefined(該過(guò)程只進(jìn)行定義聲明,執(zhí)行階段才執(zhí)行賦值語(yǔ)句)。
          • 對(duì)于函數(shù)聲明:此時(shí)會(huì)在內(nèi)存里創(chuàng)建函數(shù)對(duì)象,并且直接初始化為該函數(shù)對(duì)象。


          變量聲明和函數(shù)聲明的處理過(guò)程,便是我們常說(shuō)的變量提升和函數(shù)提升,其中函數(shù)聲明提升會(huì)優(yōu)先于變量聲明提升。因?yàn)樽兞刻嵘菀讕?lái)變量在預(yù)期外被覆蓋掉的問(wèn)題,同時(shí)還可能導(dǎo)致本應(yīng)該被銷(xiāo)毀的變量沒(méi)有被銷(xiāo)毀等情況。因此 ES6 中引入了let和const關(guān)鍵字,從而使 JavaScript 也擁有了塊級(jí)作用域。


          作用域

          在各類(lèi)編程語(yǔ)言中,作用域分為靜態(tài)作用域和動(dòng)態(tài)作用域。JavaScript 采用的是詞法作用域(Lexical Scoping),也就是靜態(tài)作用域。詞法作用域中的變量,在編譯過(guò)程中會(huì)產(chǎn)生一個(gè)確定的作用域。


          詞法作用域中的變量,在編譯過(guò)程中會(huì)產(chǎn)生一個(gè)確定的作用域,這個(gè)作用域即當(dāng)前的執(zhí)行上下文,在 ES5 后我們使用詞法環(huán)境(Lexical Environment)替代作用域來(lái)描述該執(zhí)行上下文。因此,詞法環(huán)境可理解為我們常說(shuō)的作用域,同樣也指當(dāng)前的執(zhí)行上下文(注意,是當(dāng)前的執(zhí)行上下文)。


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

          • 變量環(huán)境用來(lái)記錄var/function等變量聲明;
          • 詞法環(huán)境是用來(lái)記錄let/const/class等變量聲明。

          也就是說(shuō),創(chuàng)建變量過(guò)程中會(huì)進(jìn)行函數(shù)提升和變量提升,JavaScript 會(huì)通過(guò)詞法環(huán)境來(lái)記錄函數(shù)和變量聲明。通過(guò)使用兩個(gè)詞法環(huán)境(而不是一個(gè))分別記錄不同的變量聲明內(nèi)容,JavaScript 實(shí)現(xiàn)了支持塊級(jí)作用域的同時(shí),不影響原有的變量聲明和函數(shù)聲明。

          這就是創(chuàng)建變量的過(guò)程,它屬于執(zhí)行上下文創(chuàng)建中的一環(huán)。創(chuàng)建變量的過(guò)程會(huì)產(chǎn)生作用域,作用域也被稱為詞法環(huán)境。


          建立作用域鏈

          作用域鏈,就是將各個(gè)作用域通過(guò)某種方式連接在一起。作用域就是詞法環(huán)境,而詞法環(huán)境由兩個(gè)成員組成。

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

          通過(guò)外部詞法環(huán)境的引用,作用域可以層層拓展,建立起從里到外延伸的一條作用域鏈。當(dāng)某個(gè)變量無(wú)法在自身詞法環(huán)境記錄中找到時(shí),可以根據(jù)外部詞法環(huán)境引用向外層進(jìn)行尋找,直到最外層的詞法環(huán)境中外部詞法環(huán)境引用為null,這便是作用域鏈的變量查詢。

          JavaScript 代碼運(yùn)行過(guò)程分為定義期和執(zhí)行期,前面提到的編譯階段則屬于定義期,代碼示例如下:

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

          前面我們說(shuō)到,JavaScript 使用的是靜態(tài)作用域,因此函數(shù)的作用域在定義期已經(jīng)決定了。在上面的例子中,全局函數(shù)foo創(chuàng)建了一個(gè)foo[[scope]]屬性,包含了全局[[scope]]

          foo[[scope]]=[globalContext];

          而當(dāng)我們執(zhí)行foo()時(shí),也會(huì)分別進(jìn)入foo函數(shù)的定義期和執(zhí)行期。

          在foo函數(shù)的定義期時(shí),函數(shù)bar的[[scope]]將會(huì)包含全局[[scope]]和foo的[[scope]]:

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

          運(yùn)行上述代碼,我們可以在控制臺(tái)看到符合預(yù)期的輸出:

          可以看到:

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

          也就是說(shuō),JavaScript 會(huì)通過(guò)外部詞法環(huán)境引用來(lái)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈,從而保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的變量和函數(shù)的有序訪問(wèn)。除了創(chuàng)建作用域鏈之外,在這個(gè)過(guò)程中還會(huì)對(duì)創(chuàng)建的變量對(duì)象做一些處理。

          在編譯階段會(huì)進(jìn)行變量對(duì)象(VO)的創(chuàng)建,該過(guò)程會(huì)進(jìn)行函數(shù)聲明和變量聲明,這時(shí)候變量的值被初始化為 undefined。在代碼進(jìn)入執(zhí)行階段之后,JavaScript 會(huì)對(duì)變量進(jìn)行賦值,此時(shí)變量對(duì)象會(huì)轉(zhuǎn)為活動(dòng)對(duì)象(Active Object,簡(jiǎn)稱 AO),轉(zhuǎn)換后的活動(dòng)對(duì)象才可被訪問(wèn),這就是 VO -> AO 的過(guò)程,示例如下:

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

          在執(zhí)行foo(1)時(shí),首先進(jìn)入定義期,此時(shí):

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

          前面我們也有提到,進(jìn)入執(zhí)行期之后,會(huì)執(zhí)行賦值語(yǔ)句進(jìn)行賦值,此時(shí)變量b和d會(huì)被賦值為 2 和函數(shù)表達(dá)式:

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

          這就是 VO -> AO 過(guò)程。

          • 在定義期(編譯階段):該對(duì)象值仍為undefined,且處于不可訪問(wèn)的狀態(tài)。
          • 進(jìn)入執(zhí)行期(執(zhí)行階段):VO 被激活,其中變量屬性會(huì)進(jìn)行賦值。

          實(shí)際上在執(zhí)行的時(shí)候,除了 VO 被激活,活動(dòng)對(duì)象還會(huì)添加函數(shù)執(zhí)行時(shí)傳入的參數(shù)和arguments這個(gè)特殊對(duì)象,因此 AO 和 VO 的關(guān)系可以用以下關(guān)系來(lái)表達(dá):

          AO=VO + function parameters + arguments

          現(xiàn)在,我們知道作用域鏈?zhǔn)窃谶M(jìn)入代碼的執(zhí)行階段時(shí),通過(guò)外部詞法環(huán)境引用來(lái)創(chuàng)建的。總結(jié)如下:

          • 在編譯階段,JavaScript 在創(chuàng)建執(zhí)行上下文的時(shí)候會(huì)先創(chuàng)建變量對(duì)象(VO);
          • 在執(zhí)行階段,變量對(duì)象(VO)被激活為活動(dòng)對(duì)象( AO),函數(shù)內(nèi)部的變量對(duì)象通過(guò)外部詞法環(huán)境的引用創(chuàng)建作用域鏈。

          通過(guò)作用域鏈,我們可以在函數(shù)內(nèi)部可以直接讀取外部以及全局變量,但外部環(huán)境是無(wú)法訪問(wèn)內(nèi)部函數(shù)里的變量。示例如下:

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

          我們?cè)谌汁h(huán)境下無(wú)法訪問(wèn)函數(shù)foo中的變量a,這是因?yàn)槿趾瘮?shù)的作用域鏈里,不含有函數(shù)foo內(nèi)的作用域。

          如果我們想要訪問(wèn)內(nèi)部函數(shù)的變量,可以通過(guò)函數(shù)foo中的函數(shù)bar返回變量a,并將函數(shù)bar返回,這樣我們?cè)谌汁h(huán)境中也可以通過(guò)調(diào)用函數(shù)foo返回的函數(shù)bar,來(lái)訪問(wèn)變量a:

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

          當(dāng)函數(shù)執(zhí)行結(jié)束之后,執(zhí)行期上下文將被銷(xiāo)毀,其中包括作用域鏈和激活對(duì)象。

          在上面的實(shí)例中;當(dāng)b()執(zhí)行時(shí),foo函數(shù)上下文包括作用域都已經(jīng)被銷(xiāo)毀了,但是foo作用域下的a依然可以被訪問(wèn)到;這是因?yàn)閎ar函數(shù)引用了foo函數(shù)變量對(duì)象中的值,此時(shí)即使創(chuàng)建bar函數(shù)的foo函數(shù)執(zhí)行上下文被銷(xiāo)毀了,但它的變量對(duì)象依然會(huì)保留在 JavaScript 內(nèi)存中,bar函數(shù)依然可以通過(guò)bar函數(shù)的作用域鏈找到它,并進(jìn)行訪問(wèn)。這就是閉包;

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

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

          注意,在使用閉包的時(shí)候,需要及時(shí)清理不再使用到的變量,否則可能導(dǎo)致內(nèi)存泄漏問(wèn)題。


          確定 this 的指向

          在 JavaScript 中,this指向執(zhí)行當(dāng)前代碼對(duì)象的所有者,可簡(jiǎn)單理解為this指向最后調(diào)用當(dāng)前代碼的那個(gè)對(duì)象。

          根據(jù) JavaScript 中函數(shù)的調(diào)用方式不同,this的指向分為以下情況。

          1. 在全局環(huán)境中,this指向全局對(duì)象(在瀏覽器中為window)
          2. 在函數(shù)內(nèi)部,this的值取決于函數(shù)被調(diào)用的方式
          3. 函數(shù)作為對(duì)象的方法被調(diào)用,this指向調(diào)用這個(gè)方法的對(duì)象
          4. 函數(shù)用作構(gòu)造函數(shù)時(shí)(使用new關(guān)鍵字),它的this被綁定到正在構(gòu)造的新對(duì)象
          5. 在類(lèi)的構(gòu)造函數(shù)中,this是一個(gè)常規(guī)對(duì)象,類(lèi)中所有非靜態(tài)的方法都會(huì)被添加到this的原型中
          6. 在箭頭函數(shù)中,this指向它被創(chuàng)建時(shí)的環(huán)境
          7. 使用apply、call、bind等方式調(diào)用:根據(jù) API 不同,可切換函數(shù)執(zhí)行的上下文環(huán)境,即this綁定的對(duì)象

          可以看到,this在不同的情況下會(huì)有不同的指向,在 ES6 箭頭函數(shù)還沒(méi)出現(xiàn)之前,為了能正確獲取某個(gè)運(yùn)行環(huán)境下this對(duì)象,我們常常會(huì)使用以下代碼:

          var that=this;
          var self=this;

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


          總結(jié)

          今天我們了解了 JavaScript 代碼的運(yùn)行過(guò)程,該過(guò)程分為語(yǔ)法分析階段、編譯階段、執(zhí)行階段三個(gè)階段。

          在編譯階段,JavaScript會(huì)進(jìn)行執(zhí)行上下文的創(chuàng)建,在執(zhí)行階段,變量對(duì)象(VO)會(huì)被激活為活動(dòng)對(duì)象(AO),變量會(huì)進(jìn)行賦值,此時(shí)活動(dòng)對(duì)象才可被訪問(wèn)。在執(zhí)行結(jié)束之后,作用域鏈和活動(dòng)對(duì)象均被銷(xiāo)毀,使用閉包可使活動(dòng)對(duì)象依然被保留在內(nèi)存中。這就是 JavaScript 代碼的運(yùn)行過(guò)程。

          這里是云端源想IT,幫你輕松學(xué)IT”

          嗨~ 今天的你過(guò)得還好嗎?

          我們總是先揚(yáng)起塵土

          然后抱怨自己看不見(jiàn)

          - 2024.04.17 -

          JavaScript是一種輕量級(jí)的編程語(yǔ)言,通常用于網(wǎng)頁(yè)開(kāi)發(fā),以增強(qiáng)用戶界面的交互性和動(dòng)態(tài)性。然而在HTML中,有多種方法可以嵌入和使用JavaScript代碼。

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



          一、使用 script 標(biāo)簽

          要在HTML中使用JavaScript,我們需要使用<script>標(biāo)簽。這個(gè)標(biāo)簽可以放在<head>或<body>部分,但通常我們會(huì)將其放在<body>部分的底部,以確保在執(zhí)行JavaScript代碼時(shí),HTML文檔已經(jīng)完全加載。

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


          包含在 <script> 標(biāo)簽內(nèi)的 JavaScript 代碼在瀏覽器總按照從上至下的順序依次解釋。


          所有 <script> 標(biāo)簽都會(huì)按照他們?cè)?HTML 中出現(xiàn)的先后順序依次被解析。



          HTML 為 <script> 定義了幾個(gè)屬性:

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


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


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


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


          4)type:可選。表示編寫(xiě)代碼使用的腳本語(yǔ)言的內(nèi)容類(lèi)型,目前在客戶端,type 屬性值一般使用 text/javascript。不過(guò)這個(gè)屬性并不是必需的,如果沒(méi)有指定這個(gè)屬性,則其默認(rèn)值仍為text/javascript。



          1.1 直接在頁(yè)面中嵌入JavaScript代碼

          內(nèi)部JavaScript是將JavaScript代碼放在HTML文檔的<script>標(biāo)簽中。這樣可以將JavaScript代碼與HTML代碼分離,使結(jié)構(gòu)更清晰,易于維護(hù)。


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

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


          如果沒(méi)有指定script屬性,則其默認(rèn)值為text/javascript。


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


          在使用<script>嵌入JavaScript代碼的過(guò)程中,當(dāng)代碼中出現(xiàn)"</script>"字符串時(shí),由于解析嵌入式代碼的規(guī)則,瀏覽器會(huì)認(rèn)為這是結(jié)束的</script>標(biāo)簽。可以通過(guò)轉(zhuǎn)義字符“\”寫(xiě)成<\/script>來(lái)解決這個(gè)問(wèn)題。


          1.2 包含外部 JavaScript 文件

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


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

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


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



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

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

          通過(guò)<script>元素的src屬性還可以包含來(lái)自外部域的JavaScript文件。它的src屬性可以是指向當(dāng)前HTML頁(yè)面所在域之外的某個(gè)域中的完整URL。

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

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


          1.3 標(biāo)簽的位置

          在HTML中,所有的<script>標(biāo)簽會(huì)按照它們出現(xiàn)的先后順序被解析。在不使用defer和async屬性的情況下,只有當(dāng)前面的<script>標(biāo)簽中的代碼解析完成后,才會(huì)開(kāi)始解析后面的<script>標(biāo)簽中的代碼。


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



          然而,如果將所有的JavaScript文件都放在<head>標(biāo)簽中,會(huì)導(dǎo)致瀏覽器在呈現(xiàn)頁(yè)面內(nèi)容之前必須下載、解析并執(zhí)行所有JavaScript代碼,這可能會(huì)造成明顯的延遲,導(dǎo)致瀏覽器窗口在加載過(guò)程中出現(xiàn)空白。


          為了避免這種延遲問(wèn)題,現(xiàn)代Web應(yīng)用程序通常會(huì)將所有的JavaScript引用放置在<body>標(biāo)簽中的頁(yè)面內(nèi)容的后面。這樣做可以確保在解析JavaScript代碼之前,頁(yè)面的內(nèi)容已經(jīng)完全呈現(xiàn)在瀏覽器中,從而加快了打開(kāi)網(wǎng)頁(yè)的速度。


          二、執(zhí)行JavaScript 程序

          JavaScript 解析過(guò)程包括兩個(gè)階段:預(yù)處理(也稱預(yù)編譯)執(zhí)行

          • 在編譯期,JavaScript 解析器將完成對(duì) JavaScript 代碼的預(yù)處理操作,把 JavaScript 代碼轉(zhuǎn)換成字節(jié)碼;
          • 在執(zhí)行期,JavaScript 解析器把字節(jié)碼生成二進(jìn)制機(jī)械碼,并按順序執(zhí)行,完成程序設(shè)計(jì)的任務(wù)。


          1、執(zhí)行過(guò)程

          HTML 文檔在瀏覽器中的解析過(guò)程是:按照文檔流從上到下逐步解析頁(yè)面結(jié)構(gòu)和信息。

          JavaScript 代碼作為嵌入的腳本應(yīng)該也算做 HTML 文檔的組成部分,所以 JavaScript 代碼在裝載時(shí)的執(zhí)行順序也是根據(jù) <script> 標(biāo)簽出現(xiàn)的順序來(lái)確定。

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

          2、預(yù)編譯

          當(dāng) JavaScript 引擎解析腳本時(shí)候,他會(huì)在與編譯期對(duì)所有聲明的變量和函數(shù)預(yù)先進(jìn)行處理。當(dāng) JavaScript 解析器執(zhí)行下面腳本時(shí)不會(huì)報(bào)錯(cuò)。

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


          由于變量聲明是在預(yù)編譯期被處理的,在執(zhí)行期間對(duì)于所有的代碼來(lái)說(shuō),都是可見(jiàn)的,但是執(zhí)行上面代碼,提示的值是 undefined 而不是 1。

          因?yàn)樽兞砍跏蓟^(guò)程發(fā)生在執(zhí)行期,而不是預(yù)編譯期。在執(zhí)行期,JavaScript 解析器是按照代碼先后順序進(jìn)行解析的,如果在前面代碼行中沒(méi)有為變量賦值,則 JavaScript 解析器會(huì)使用默認(rèn)值 undefined 。


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

          fun(); //調(diào)用函數(shù),返回值1
          function fun(){
          alert(1);
          }

          函數(shù)聲明前調(diào)用函數(shù)也是合法的,并能夠正確解析,所以返回值是 1。但如果是下面這種方式則 JavaScript 解釋器會(huì)報(bào)錯(cuò)。

          fun(); //調(diào)用函數(shù),返回語(yǔ)法錯(cuò)誤
          var fun=function(){
          alert(1);
          }

          上面的這個(gè)例子中定義的函數(shù)僅作為值賦值給變量 fun 。在預(yù)編譯期,JavaScript 解釋器只能夠?yàn)槁暶髯兞?fun 進(jìn)行處理,而對(duì)于變量 fun 的值,只能等到執(zhí)行期時(shí)按照順序進(jìn)行賦值,自然就會(huì)出現(xiàn)語(yǔ)法錯(cuò)誤,提示找不到對(duì)象 fun。

          總結(jié):聲明變量和函數(shù)可以在文檔的任意位置,但是良好的習(xí)慣應(yīng)該是在所有 JavaScript 代碼之前聲明全局變量和函數(shù),并對(duì)變量進(jìn)行初始化賦值。在函數(shù)內(nèi)部也是先聲明變量,后引用。

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



          我們下期再見(jiàn)!


          END

          文案編輯|云端學(xué)長(zhǎng)

          文案配圖|云端學(xué)長(zhǎng)

          內(nèi)容由:云端源想分享

          了執(zhí)行Javascript,需要在HTML文件內(nèi)以特定的方式書(shū)寫(xiě)JavaScript的代碼,JavaScript的書(shū)寫(xiě)方法有多種,其執(zhí)行的流程也各不相同:

          1 <script>標(biāo)簽嵌入

          此種嵌入方法無(wú)法操作<script>之后的DOM元素。因?yàn)?lt;script>之后的DOM元素還未構(gòu)造,因此在<script>標(biāo)簽內(nèi)就無(wú)法取得位于其后的DOM元素。

          2 讀取外部JavaScript文件

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

          3 onload嵌入

          此種嵌入方法在頁(yè)面讀取完后再對(duì)其執(zhí)行,所以可以對(duì)所有的DOM元素操作。

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

          當(dāng)window.onload事件觸發(fā)時(shí),頁(yè)面上所有的DOM、樣式表、腳本、圖片、flash都已經(jīng)加載完成了。

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

          window.onload和body中onload也有些許區(qū)別:

          <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下,結(jié)果為 :

          "DOMContentLoaded"
          "bodyonload"

          說(shuō)明body中的onload會(huì)覆蓋window.onload

          在chrome下,結(jié)果為:

          DOMContentLoaded
          window.onload
          bodyonload

          然后,如果把javascript代碼移到最下面,結(jié)果又會(huì)是什么樣呢?

          chrome和IE10、FireFox的結(jié)果竟然是一樣的:

          DOMContentLoaded
          window.onload

          IE 10、Fire Fox可以理解,window.on load和body中的 on load 誰(shuí)在下面就是誰(shuí)覆蓋誰(shuí),只會(huì)執(zhí)行后面的那個(gè)。

          4 DOM ContentLoaded嵌入

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

          在chrome、IE10和FireFox中,執(zhí)行結(jié)果是:DOMContentLoaded然后才是onload的輸出。所以說(shuō)一般情況下,DOMContentLoaded事件要在window.onload之前執(zhí)行,當(dāng)DOM樹(shù)構(gòu)建完成的時(shí)候就會(huì)執(zhí)行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>

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

          5 動(dòng)態(tài)載入JavaScript文件

          5.1 使用原生js方法

          動(dòng)態(tài)創(chuàng)建script標(biāo)簽,并指定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');
          });

          還可以使用同樣的原理動(dòng)態(tài)加載css文件,只不過(guò)插入的的父節(jié)點(diǎn)是head標(biāo)簽。

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

          該種方式可以實(shí)現(xiàn)js文件的動(dòng)態(tài)加載,原理就是在重寫(xiě)文檔流,這種方式會(huì)導(dǎo)致整個(gè)頁(yè)面重繪。

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

          需要注意的是特殊字符的轉(zhuǎn)義。

          5.3 使用jQuery

          使用getScript(url,callback)方法實(shí)現(xiàn)動(dòng)態(tài)加載js文件

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

          -End-


          主站蜘蛛池模板: 久久久99精品一区二区| 亚洲国产一区二区三区青草影视 | 国产福利电影一区二区三区,日韩伦理电影在线福 | 亚洲无圣光一区二区| 一区二区三区免费电影| 日本内射精品一区二区视频| 精品国产毛片一区二区无码| 国产成人一区二区三区精品久久| 精品人妻码一区二区三区| 奇米精品一区二区三区在线观看| 蜜桃视频一区二区| 免费在线视频一区| 日韩AV无码一区二区三区不卡| 色噜噜狠狠一区二区三区| 一区二区三区四区视频| 久久一区二区三区精品| 无码少妇一区二区| 日本免费一区二区三区四区五六区| 国产在线观看一区二区三区精品| 一区二区三区四区在线观看视频| 国产成人免费一区二区三区| 精品国产免费观看一区| 国产精品美女一区二区| 国产主播一区二区三区在线观看| 午夜一区二区免费视频| 国产一区二区视频免费| 日韩毛片一区视频免费| 国产天堂一区二区综合| 日本亚洲国产一区二区三区| 国产成人无码一区二区在线播放| 国产爆乳无码一区二区麻豆| 无码精品一区二区三区免费视频| 无码成人一区二区| 中文乱码人妻系列一区二区| 精品国产免费观看一区| 国产精品 一区 在线| 久久久无码精品国产一区| 久久精品黄AA片一区二区三区| 麻豆AV无码精品一区二区| 国产视频一区在线播放| 国产精品毛片一区二区 |