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
大家都知道,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 是弱類(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è)階段。
其中,語(yǔ)法分析階段屬于編譯器通用內(nèi)容,就不再贅述。前面提到的執(zhí)行環(huán)境、詞法環(huán)境、作用域、執(zhí)行上下文等內(nèi)容都是在編譯和執(zhí)行階段中產(chǎn)生的概念。
執(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ò)程如下:
在不同的運(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ò)程包括:
由于建立作用域鏈過(guò)程中會(huì)涉及變量對(duì)象的概念,因此我們先來(lái)看看變量對(duì)象的創(chuàng)建,再看建立作用域鏈和確定 this 的指向。
變量對(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ù)聲明和變量聲明。
變量聲明和函數(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)兩種,其中:
也就是說(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è)成員組成。
通過(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ù)期的輸出:
可以看到:
也就是說(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í):
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ò)程。
實(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é)如下:
通過(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)的用途包括:
注意,在使用閉包的時(shí)候,需要及時(shí)清理不再使用到的變量,否則可能導(dǎo)致內(nèi)存泄漏問(wèn)題。
在 JavaScript 中,this指向執(zhí)行當(dāng)前代碼對(duì)象的所有者,可簡(jiǎn)單理解為this指向最后調(diào)用當(dāng)前代碼的那個(gè)對(duì)象。
根據(jù) JavaScript 中函數(shù)的調(diào)用方式不同,this的指向分為以下情況。
可以看到,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ù),我們可以更好地管理作用域。
今天我們了解了 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。
要在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>
與解析嵌入式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è)的速度。
JavaScript 解析過(guò)程包括兩個(gè)階段:預(yù)處理(也稱預(yù)編譯)和執(zhí)行。
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í)行的流程也各不相同:
此種嵌入方法無(wú)法操作<script>之后的DOM元素。因?yàn)?lt;script>之后的DOM元素還未構(gòu)造,因此在<script>標(biāo)簽內(nèi)就無(wú)法取得位于其后的DOM元素。
此種嵌入方法可以指定defer、async屬性。defer可以推遲執(zhí)行,async可以異步執(zhí)行。
此種嵌入方法在頁(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è)。
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.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-
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。