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是一種描述型的腳本語(yǔ)言,是一種解析語(yǔ)言,由瀏覽器動(dòng)態(tài)解析,不同類(lèi)型的瀏覽器、不同版本的瀏覽器對(duì)于JavaScript的解析有著微小的差別,不同瀏覽器的JavaScript解析引擎效率也有差異。
JavaScript的執(zhí)行過(guò)程分為兩大部分:
總的來(lái)說(shuō),JavaScript的執(zhí)行分為兩部分:解析過(guò)程和執(zhí)行過(guò)程。解析時(shí)按照代碼塊,一段一段進(jìn)行解析,執(zhí)行時(shí)按照代碼塊順序逐行執(zhí)行,解析一個(gè)代碼塊,執(zhí)行一個(gè)代碼塊。
因?yàn)槭墙忉屝驼Z(yǔ)言,所以JavaScript如果在解析過(guò)程中有錯(cuò)誤,則不會(huì)提示,也可以理解為JavaScript不會(huì)出現(xiàn)編譯錯(cuò)誤,但如果出現(xiàn)了運(yùn)行時(shí)錯(cuò)誤,出現(xiàn)錯(cuò)誤以下的所有JavaScript代碼將不會(huì)繼續(xù)執(zhí)行。
預(yù)處理:創(chuàng)建一個(gè)詞法環(huán)境(LexicalEnvironment,簡(jiǎn)寫(xiě)為L(zhǎng)E),掃描JavaScript中用聲明的方式聲明的函數(shù),用var定義的變量并將它們加到預(yù)處理階段的詞法環(huán)境中去。
預(yù)處理階段先讀取代碼塊,不是一行一行的解析執(zhí)行定義的方法和用var定義的變量,會(huì)放到一個(gè)(不同的環(huán)境,會(huì)有對(duì)應(yīng)的詞法環(huán)境)詞法環(huán)境環(huán)境中。
var a=1; // 用var定義的變量,已賦值
var b; // 用var定義的變量,未賦值
c=3; // 未定義的變量,直接賦值
// 用聲明的方式聲明的函數(shù)
function d(){
console.log('hello');
}
// 函數(shù)表達(dá)式
var e=function() {
console.log('world');
}
詞法環(huán)境:
LE { // 此時(shí)的LE相當(dāng)于window
a: undefined
b: undefined
d: 函數(shù)引用
e: undefined
}
預(yù)處理的函數(shù)必須是JavaScript中用聲明的方式聲明的函數(shù),不是函數(shù)表達(dá)式。
示例:
d();
e();
// 用聲明的方式聲明的函數(shù)
function d(){
console.log('hello');
}
// 函數(shù)表達(dá)式
var e=function() {
console.log('world');
}
執(zhí)行結(jié)果:
hello
TypeError: e is not a function
函數(shù)優(yōu)先原則:在既有函數(shù)聲明又有變量聲明的時(shí)候,函數(shù)聲明的權(quán)重高于變量聲明,所以最終結(jié)果往往是指向函數(shù)的引用。
示例 1:
console.log(f);
var f=1;
function f() {
console.log('func');
}
結(jié)果:
[Function: f]
示例 2:
console.log(f);
function f() {
console.log('func');
}
var f=1;
結(jié)果:
[Function: f]
console.log(a); // undefined
console.log(b); // TypeError: b is not a function
console.log(c); // [Function: f]
console.log(d); // undefined
var a=1;
b=2;
console.log(b); // 2
function c(){
console.log('c');
}
var d=function(){
console.log('d');
}
console.log(d); // [Function: f]
詞法環(huán)境:
LE {
a: undefined
c: [Function: f]
d: undefined
}
預(yù)處理階段傳輸參數(shù)值一一對(duì)應(yīng)
function func(a, b) {
console.log(a);
console.log(b);
var b=100;
function a{}
}
func(1, 2);
詞法環(huán)境:
LE {
b: 2
a: 指向函數(shù)的引用
arguments: 2 // 調(diào)用函數(shù)時(shí)實(shí)際調(diào)用的參數(shù)個(gè)數(shù)
}
運(yùn)行結(jié)果:
[Function: f]
2
沒(méi)有用var聲明的變量,會(huì)變成最外部LE的成員,即全局變量:
用代碼分割、延遲加載、使用 Web Workers、壓縮文件和異步加載等技術(shù)提升您的 JavaScript 技能。
譯自 How To Master JavaScript Performance Optimization,作者 Alexander T Williams。
JavaScript 是現(xiàn)代 Web 應(yīng)用程序的基石,為從動(dòng)態(tài)內(nèi)容到交互式功能的一切提供支持。然而,隨著應(yīng)用程序變得越來(lái)越復(fù)雜,確保 JavaScript 能夠高效運(yùn)行變得至關(guān)重要。
隨著用戶(hù)對(duì)更快、更具響應(yīng)性的應(yīng)用程序的需求不斷增長(zhǎng),開(kāi)發(fā)人員必須優(yōu)先考慮 JavaScript 優(yōu)化以滿(mǎn)足這些期望。從減少加載時(shí)間到提高交互性,優(yōu)化您的 JavaScript 可以顯著提高 Web 應(yīng)用程序的整體性能。
正確理解網(wǎng)站的性能是優(yōu)化 JavaScript 代碼的第一步。
考慮到這一點(diǎn),衡量您的網(wǎng)站或應(yīng)用程序的性能至關(guān)重要,因?yàn)樗梢詭椭R(shí)別影響下載時(shí)間、渲染速度和整體用戶(hù)體驗(yàn)的瓶頸。
如果沒(méi)有對(duì)性能進(jìn)行適當(dāng)?shù)暮饬浚赡軙?huì)浪費(fèi)時(shí)間應(yīng)用優(yōu)化,而這些優(yōu)化并不能解決您的網(wǎng)站所面臨的實(shí)際問(wèn)題。
有幾種工具可以幫助您有效地衡量性能。內(nèi)置的瀏覽器工具,例如 Chrome DevTools,提供關(guān)于網(wǎng)絡(luò)活動(dòng)、加載時(shí)間和 CPU 使用率的全面而有價(jià)值的見(jiàn)解。
收集完性能數(shù)據(jù)后,下一步是確定哪些優(yōu)化是必要的。
這些工具可以讓您看到頁(yè)面中哪些部分加載時(shí)間最長(zhǎng),以及哪些腳本可能會(huì)減慢網(wǎng)站速度。除此之外,性能 API 還可以提供更復(fù)雜的數(shù)據(jù),用于深入分析。
收集完性能數(shù)據(jù)后,下一步是確定哪些優(yōu)化是必要的。并非每種技術(shù)都適合每個(gè)項(xiàng)目,因此根據(jù)您網(wǎng)站的具體需求進(jìn)行優(yōu)先排序非常重要。
例如,如果您的分析表明事件處理程序會(huì)導(dǎo)致延遲,您可以專(zhuān)注于改進(jìn)事件管理。類(lèi)似地,如果大型 JavaScript 文件會(huì)減慢加載時(shí)間,縮小和異步加載可能是正確的解決方案。
此外,它還可以幫助您遵守 GDPR,或與您的網(wǎng)站或應(yīng)用程序相關(guān)的歐盟、美國(guó)或其他地方的任何數(shù)據(jù)保護(hù)法規(guī)。優(yōu)化您的 JavaScript 有助于提高性能,同時(shí)確保您的數(shù)據(jù)處理實(shí)踐符合標(biāo)準(zhǔn)。
正確管理的代碼可以幫助最大限度地減少不必要數(shù)據(jù)的收集,從而簡(jiǎn)化嘗試遵守和遵循重要監(jiān)管要求的過(guò)程。
我們都經(jīng)歷過(guò):如果您的代碼沒(méi)有得到妥善管理,JavaScript 有時(shí)會(huì)成為一個(gè)真正的頭痛問(wèn)題。
您可能遇到的一些常見(jiàn)問(wèn)題包括質(zhì)量較差的事件處理,這會(huì)導(dǎo)致深層調(diào)用堆棧和更慢的性能。無(wú)序的代碼是另一個(gè)大問(wèn)題,會(huì)導(dǎo)致資源分配效率低下,并使瀏覽器更難快速執(zhí)行腳本。
代碼拆分允許您將 JavaScript 代碼分解成更小、更易于管理的塊。
然后是過(guò)度依賴(lài)的問(wèn)題,這會(huì)減慢應(yīng)用程序的速度,通常會(huì)顯著減慢速度,尤其是對(duì)于帶寬有限的移動(dòng)用戶(hù)而言——而且不要忘記,低效的迭代會(huì)不必要地拖延處理時(shí)間。
代碼拆分允許您將 JavaScript 代碼分解成更小、更易于管理的塊——這在您的應(yīng)用程序變得越來(lái)越復(fù)雜時(shí)至關(guān)重要,有助于減少加載時(shí)間并提高用戶(hù)的初始渲染速度。
那么,如何進(jìn)行代碼拆分呢?一種常用的方法是使用動(dòng)態(tài)導(dǎo)入,它允許您僅在需要時(shí)加載 JavaScript 模塊,而不是一次性將整個(gè)應(yīng)用程序加載到用戶(hù)身上。這就像只為周末旅行打包必需品,而不是打包整個(gè)衣櫥。
根據(jù)最近的調(diào)查統(tǒng)計(jì),48.9% 的開(kāi)發(fā)人員已采用動(dòng)態(tài)導(dǎo)入按需加載模塊,45.7% 的開(kāi)發(fā)人員正在使用服務(wù)工作者 來(lái)增強(qiáng)離線(xiàn)用戶(hù)體驗(yàn)。
同樣,對(duì)于 JS 庫(kù)也是如此,允許進(jìn)行各種應(yīng)用內(nèi)操作,例如在 React 應(yīng)用中查看文檔,動(dòng)態(tài)在實(shí)時(shí)分析儀表板中渲染圖表,或加載交互式地圖以用于基于位置的服務(wù)。然后是 webpack,一個(gè)工具,一旦你掌握了它,就會(huì)感覺(jué)有點(diǎn)像魔法;它可以自動(dòng)將你的代碼拆分成更小的塊,按需加載它們。
import('./module.js').then(module=> {
module.doSomething();
});
const MyComponent=React.lazy(()=> import('./MyComponent'));
延遲加載是一種很棒的技術(shù),可以通過(guò)延遲加載非必要資源來(lái)提高 Web 應(yīng)用的性能,直到它們真正需要時(shí)才加載。
簡(jiǎn)而言之,延遲加載允許這些元素僅在進(jìn)入用戶(hù)的視野時(shí)加載,而不是讓用戶(hù)等待每個(gè)圖像、視頻或媒體文件預(yù)先加載。
延遲加載最常見(jiàn)的用例包括圖像、視頻和其他媒體密集型內(nèi)容等元素。使用延遲加載可以大幅減少初始加載時(shí)間,從而增強(qiáng)網(wǎng)站或應(yīng)用的整體用戶(hù)體驗(yàn)。
實(shí)現(xiàn)延遲加載的一種流行方法是通過(guò) Intersection Observer API。這個(gè)特定的 API 允許你檢測(cè)元素何時(shí)進(jìn)入或退出視窗,因此你可以在內(nèi)容即將對(duì)用戶(hù)可見(jiàn)時(shí)才加載它。它效率高且設(shè)置起來(lái)相對(duì)容易。
const observer=new IntersectionObserver((entries)=> {
entries.forEach(entry=> {
if (entry.isIntersecting) {
loadImage(entry.target);
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img=> observer.observe(img));
Web Workers 是現(xiàn)代 Web 開(kāi)發(fā)中的一項(xiàng)強(qiáng)大功能,旨在幫助處理繁重的計(jì)算,而不會(huì)減慢用戶(hù)界面。
Web Workers 從主線(xiàn)程卸載密集型任務(wù),通過(guò)在后臺(tái)線(xiàn)程中運(yùn)行腳本,提供流暢且響應(yīng)迅速的用戶(hù)體驗(yàn)。
Web Workers 通過(guò)啟用并行執(zhí)行來(lái)顯著提高性能;因此,當(dāng)主線(xiàn)程處理用戶(hù)交互和渲染時(shí),Web Workers 負(fù)責(zé)后臺(tái)的資源密集型操作,例如數(shù)據(jù)處理和計(jì)算。這可以防止 UI 由于長(zhǎng)時(shí)間運(yùn)行的腳本而變得無(wú)響應(yīng)。
使用 Web Workers 的一些更實(shí)際的示例包括卸載基本數(shù)據(jù)處理任務(wù)。例如,當(dāng)處理需要排序、過(guò)濾或復(fù)雜計(jì)算的大型數(shù)據(jù)集時(shí),Web Worker 可以管理這些操作,而不會(huì)凍結(jié)主 UI 線(xiàn)程。
// worker.js
self.onmessage=(e)=> {
const result=computeHeavyTask(e.data);
postMessage(result);
};
const worker=new Worker('worker.js');
worker.onmessage=(e)=> {
console.log('Result from worker:', e.data);
};
worker.postMessage(data);
優(yōu)化 JavaScript 不僅僅是代碼分割和延遲加載,還有其他一些技術(shù)可以顯著提高應(yīng)用程序的性能。
異步加載允許腳本與其他資源并行獲取。
一種重要的方法是 壓縮和壓縮 JavaScript 文件,這涉及從代碼中刪除不必要的字符和空格,而不會(huì)改變其功能。像 UglifyJS 這樣的工具可以幫助完成此過(guò)程,使用 gzip 或 Brotli 壓縮可以進(jìn)一步減小文件大小,從而加快加載時(shí)間。
另一方面,異步加載允許腳本 與其他資源并行獲取,防止它們阻塞頁(yè)面的渲染。HTML 中的 async 屬性通常用于此目的。
使用 defer 屬性延遲腳本,確保 代碼在初始 HTML 解析后執(zhí)行,這提高了用戶(hù)與網(wǎng)站交互的速度。
利用 HTTP/2 和 JavaScript CDN 可以進(jìn)一步提高網(wǎng)站或應(yīng)用程序的性能。
HTTP/2 引入了多路復(fù)用等功能,允許多個(gè)請(qǐng)求同時(shí)通過(guò)單個(gè)連接發(fā)送,從而減少延遲。使用 內(nèi)容交付網(wǎng)絡(luò) (CDN) 為您的 JavaScript 文件提供服務(wù) 可以保證它們從更靠近用戶(hù)的位置提供服務(wù),從而加快交付速度。
代碼分割、延遲加載、使用 Web Workers、壓縮文件和利用異步加載等技術(shù)并不完全是秘密,但開(kāi)發(fā)人員并沒(méi)有充分利用它們——遠(yuǎn)非如此。
每種方法都可以提高應(yīng)用程序的速度和響應(yīng)能力,將它們納入開(kāi)發(fā)工作流程將提供更流暢的用戶(hù)體驗(yàn),并使您的應(yīng)用程序保持領(lǐng)先地位。
了執(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)系。