JavaScript是一种描述型的脚本语言,是一种解析语言,由浏览器动态解析,不同类型的浏览器、不同版本的浏览器对于JavaScript的解析有着微小的差别,不同浏览器的JavaScript解析引擎效率也有差异。
JavaScript的执行过程分为两大部分:
总的来说,JavaScript的执行分为两部分:解析过程和执行过程。解析时按照代码块,一段一段进行解析,执行时按照代码块顺序逐行执行,解析一个代码块,执行一个代码块。
因为是解释型语言,所以JavaScript如果在解析过程中有错误,则不会提示,也可以理解为JavaScript不会出现编译错误,但如果出现了运行时错误,出现错误以下的所有JavaScript代码将不会继续执行。
预处理:创建一个词法环境(LexicalEnvironment,简写为LE),扫描JavaScript中用声明的方式声明的函数,用var定义的变量并将它们加到预处理阶段的词法环境中去。
预处理阶段先读取代码块,不是一行一行的解析执行定义的方法和用var定义的变量,会放到一个(不同的环境,会有对应的词法环境)词法环境环境中。
var a=1; // 用var定义的变量,已赋值
var b; // 用var定义的变量,未赋值
c=3; // 未定义的变量,直接赋值
// 用声明的方式声明的函数
function d(){
console.log('hello');
}
// 函数表达式
var e=function() {
console.log('world');
}
词法环境:
LE { // 此时的LE相当于window
a: undefined
b: undefined
d: 函数引用
e: undefined
}
预处理的函数必须是JavaScript中用声明的方式声明的函数,不是函数表达式。
示例:
d();
e();
// 用声明的方式声明的函数
function d(){
console.log('hello');
}
// 函数表达式
var e=function() {
console.log('world');
}
执行结果:
hello
TypeError: e is not a function
函数优先原则:在既有函数声明又有变量声明的时候,函数声明的权重高于变量声明,所以最终结果往往是指向函数的引用。
示例 1:
console.log(f);
var f=1;
function f() {
console.log('func');
}
结果:
[Function: f]
示例 2:
console.log(f);
function f() {
console.log('func');
}
var f=1;
结果:
[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]
词法环境:
LE {
a: undefined
c: [Function: f]
d: undefined
}
预处理阶段传输参数值一一对应
function func(a, b) {
console.log(a);
console.log(b);
var b=100;
function a{}
}
func(1, 2);
词法环境:
LE {
b: 2
a: 指向函数的引用
arguments: 2 // 调用函数时实际调用的参数个数
}
运行结果:
[Function: f]
2
没有用var声明的变量,会变成最外部LE的成员,即全局变量:
用代码分割、延迟加载、使用 Web Workers、压缩文件和异步加载等技术提升您的 JavaScript 技能。
译自 How To Master JavaScript Performance Optimization,作者 Alexander T Williams。
JavaScript 是现代 Web 应用程序的基石,为从动态内容到交互式功能的一切提供支持。然而,随着应用程序变得越来越复杂,确保 JavaScript 能够高效运行变得至关重要。
随着用户对更快、更具响应性的应用程序的需求不断增长,开发人员必须优先考虑 JavaScript 优化以满足这些期望。从减少加载时间到提高交互性,优化您的 JavaScript 可以显著提高 Web 应用程序的整体性能。
正确理解网站的性能是优化 JavaScript 代码的第一步。
考虑到这一点,衡量您的网站或应用程序的性能至关重要,因为它可以帮助您识别影响下载时间、渲染速度和整体用户体验的瓶颈。
如果没有对性能进行适当的衡量,您可能会浪费时间应用优化,而这些优化并不能解决您的网站所面临的实际问题。
有几种工具可以帮助您有效地衡量性能。内置的浏览器工具,例如 Chrome DevTools,提供关于网络活动、加载时间和 CPU 使用率的全面而有价值的见解。
收集完性能数据后,下一步是确定哪些优化是必要的。
这些工具可以让您看到页面中哪些部分加载时间最长,以及哪些脚本可能会减慢网站速度。除此之外,性能 API 还可以提供更复杂的数据,用于深入分析。
收集完性能数据后,下一步是确定哪些优化是必要的。并非每种技术都适合每个项目,因此根据您网站的具体需求进行优先排序非常重要。
例如,如果您的分析表明事件处理程序会导致延迟,您可以专注于改进事件管理。类似地,如果大型 JavaScript 文件会减慢加载时间,缩小和异步加载可能是正确的解决方案。
此外,它还可以帮助您遵守 GDPR,或与您的网站或应用程序相关的欧盟、美国或其他地方的任何数据保护法规。优化您的 JavaScript 有助于提高性能,同时确保您的数据处理实践符合标准。
正确管理的代码可以帮助最大限度地减少不必要数据的收集,从而简化尝试遵守和遵循重要监管要求的过程。
我们都经历过:如果您的代码没有得到妥善管理,JavaScript 有时会成为一个真正的头痛问题。
您可能遇到的一些常见问题包括质量较差的事件处理,这会导致深层调用堆栈和更慢的性能。无序的代码是另一个大问题,会导致资源分配效率低下,并使浏览器更难快速执行脚本。
代码拆分允许您将 JavaScript 代码分解成更小、更易于管理的块。
然后是过度依赖的问题,这会减慢应用程序的速度,通常会显著减慢速度,尤其是对于带宽有限的移动用户而言——而且不要忘记,低效的迭代会不必要地拖延处理时间。
代码拆分允许您将 JavaScript 代码分解成更小、更易于管理的块——这在您的应用程序变得越来越复杂时至关重要,有助于减少加载时间并提高用户的初始渲染速度。
那么,如何进行代码拆分呢?一种常用的方法是使用动态导入,它允许您仅在需要时加载 JavaScript 模块,而不是一次性将整个应用程序加载到用户身上。这就像只为周末旅行打包必需品,而不是打包整个衣橱。
根据最近的调查统计,48.9% 的开发人员已采用动态导入按需加载模块,45.7% 的开发人员正在使用服务工作者 来增强离线用户体验。
同样,对于 JS 库也是如此,允许进行各种应用内操作,例如在 React 应用中查看文档,动态在实时分析仪表板中渲染图表,或加载交互式地图以用于基于位置的服务。然后是 webpack,一个工具,一旦你掌握了它,就会感觉有点像魔法;它可以自动将你的代码拆分成更小的块,按需加载它们。
import('./module.js').then(module=> {
module.doSomething();
});
const MyComponent=React.lazy(()=> import('./MyComponent'));
延迟加载是一种很棒的技术,可以通过延迟加载非必要资源来提高 Web 应用的性能,直到它们真正需要时才加载。
简而言之,延迟加载允许这些元素仅在进入用户的视野时加载,而不是让用户等待每个图像、视频或媒体文件预先加载。
延迟加载最常见的用例包括图像、视频和其他媒体密集型内容等元素。使用延迟加载可以大幅减少初始加载时间,从而增强网站或应用的整体用户体验。
实现延迟加载的一种流行方法是通过 Intersection Observer API。这个特定的 API 允许你检测元素何时进入或退出视窗,因此你可以在内容即将对用户可见时才加载它。它效率高且设置起来相对容易。
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 是现代 Web 开发中的一项强大功能,旨在帮助处理繁重的计算,而不会减慢用户界面。
Web Workers 从主线程卸载密集型任务,通过在后台线程中运行脚本,提供流畅且响应迅速的用户体验。
Web Workers 通过启用并行执行来显著提高性能;因此,当主线程处理用户交互和渲染时,Web Workers 负责后台的资源密集型操作,例如数据处理和计算。这可以防止 UI 由于长时间运行的脚本而变得无响应。
使用 Web Workers 的一些更实际的示例包括卸载基本数据处理任务。例如,当处理需要排序、过滤或复杂计算的大型数据集时,Web Worker 可以管理这些操作,而不会冻结主 UI 线程。
// 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);
优化 JavaScript 不仅仅是代码分割和延迟加载,还有其他一些技术可以显著提高应用程序的性能。
异步加载允许脚本与其他资源并行获取。
一种重要的方法是 压缩和压缩 JavaScript 文件,这涉及从代码中删除不必要的字符和空格,而不会改变其功能。像 UglifyJS 这样的工具可以帮助完成此过程,使用 gzip 或 Brotli 压缩可以进一步减小文件大小,从而加快加载时间。
另一方面,异步加载允许脚本 与其他资源并行获取,防止它们阻塞页面的渲染。HTML 中的 async 属性通常用于此目的。
使用 defer 属性延迟脚本,确保 代码在初始 HTML 解析后执行,这提高了用户与网站交互的速度。
利用 HTTP/2 和 JavaScript CDN 可以进一步提高网站或应用程序的性能。
HTTP/2 引入了多路复用等功能,允许多个请求同时通过单个连接发送,从而减少延迟。使用 内容交付网络 (CDN) 为您的 JavaScript 文件提供服务 可以保证它们从更靠近用户的位置提供服务,从而加快交付速度。
代码分割、延迟加载、使用 Web Workers、压缩文件和利用异步加载等技术并不完全是秘密,但开发人员并没有充分利用它们——远非如此。
每种方法都可以提高应用程序的速度和响应能力,将它们纳入开发工作流程将提供更流畅的用户体验,并使您的应用程序保持领先地位。
了执行Javascript,需要在HTML文件内以特定的方式书写JavaScript的代码,JavaScript的书写方法有多种,其执行的流程也各不相同:
此种嵌入方法无法操作<script>之后的DOM元素。因为<script>之后的DOM元素还未构造,因此在<script>标签内就无法取得位于其后的DOM元素。
此种嵌入方法可以指定defer、async属性。defer可以推迟执行,async可以异步执行。
此种嵌入方法在页面读取完后再对其执行,所以可以对所有的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 谁在下面就是谁覆盖谁,只会执行后面的那个。
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.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-
*请认真填写需求信息,我们会在24小时内与您取得联系。