JavaScript性能优化小知识总结
引言
对于长期致力于JavaScript学习的我而言,《犀利开发Jquery内核详解与实践》是一本深入剖析的宝典。尽管我对这本书赞不绝口,但不得不承认在某些深入的理解方面仍显不足。可能是因为对JavaScript的理解尚未达到炉火纯青的境界,或是自身思考深度不够。我渴望突破自我,探索更广阔的天地,为自己找到一个知识积累与应用的居所。我时常有意无意地积累关于jQuery使用的实用技巧,特别是在性能优化方面,我始终在探寻是否有更佳的实现方式。
下面,我将分享一些总结的小技巧,并为每个技巧提供简要的解释和示例代码,以供大家参考。
优化技巧一览
避免全局查找
全局查找会影响代码性能。为了减少全局查找,我们可以将全局对象存储为局部变量。访问局部变量的速度要优于全局变量。例如:
原始方式:
```javascript
function search() {
alert(window.location.href + window.location.host);
}
```
优化方式:
```javascript
function search() {
var location = window.location;
alert(location.href + location.host);
}
```
合理使用定时器
对于需要持续运行的代码,推荐使用setInterval而非setTimeout。因为setInterval只在开始时初始化一个定时器,而setTimeout每次都会初始化一个新的定时器。例如:
使用setTimeout的方式:
```javascript
var timeoutTimes = 0;
function timeout() {
timeoutTimes++;
if (timeoutTimes < 10) {
setTimeout(timeout, 10);
}
}
timeout();
```
可以优化为使用setInterval的方式:
```javascript
var intervalTimes = 0;
function interval() {
intervalTimes++;
if (intervalTimes >= 10) {
clearInterval(interv);
}
}
var interv = setInterval(interval, 10);
```
字符串连接优化
在连接多个字符串时,应减少使用“+=”操作符。如果需要进行大量字符串连接操作,最好使用一个缓存或JavaScript数组来收集,最后使用“join”方法连接。例如:
原始方式(使用“+=”连接多个字符串):
在编程的世界里,代码的优化和简洁性总是让人追求。让我们深入探讨一些编程中的常见问题和优化方法。
对象属性的设置
以往我们可能会这样设置对象的属性:
```javascript
with (a.b.c.d) {
property1 = 1;
property2 = 2;
}
```
现在,我们可以采用更为直观和简洁的方式:
```javascript
var obj = a.b.c.d;
obj.property1 = 1;
obj.property2 = 2;
```
这样的写法不仅使代码更易读,还提高了效率。
数字转字符串
当我们需要将数字转换为字符串时,虽然直接将数字与空字符串拼接看起来不太美观,但这种方式却是最高效的:
`(“” + 数字)` 的效率高于 `String()`、`.toString()` 和 `new String()`。
浮点数转整型
在转换浮点数到整型时,很多人喜欢使用 `parseInt()`,但实际上这个函数主要用于将字符串转换为数字。对于浮点数和整型的转换,我们应该选择 `Math.floor()` 或 `Math.round()`。
各种类型转换
当我们面对不同类型的转换时,可以尝试以下方式:
```javascript
var myVar = "3.14159";
str = "" + myVar; // 转换为字符串
i_int = ~~myVar; // 转换为整型
f_float = 1 myVar; // 转换为浮点型
b_bool = !!myVar; // 转换为布尔型(任何非零数字和任何长度的字符串均为true)
array = [myVar]; // 转换为数组形式
```
如果涉及到自定义的 `toString()` 方法,建议直接调用它,因为直接调用效率更高。JavaScript 在尝试其他转换方法后会尝试对象的 `toString()` 方法。
多个类型声明
在 JavaScript 中,所有的变量都可以使用单个 `var` 语句来声明,这样可以减少整个脚本的执行时间。规范的代码格式能让代码更易于阅读和理解。
使用直接量 对于创建数组或对象,使用直接量是一种简洁而高效的方式。例如: `var aTest = new Array();` 可以替换为 `var aTest = [];`。这样的替换不仅使代码看起来更简洁,还提高了性能。对于对象字面量,我们可以这样简化创建和赋值的过程: `var oFruit = { color: "red", name: "apple" };` 这样的写法使得代码更加直观和易读。
让我们回顾一种常见的方法:通过循环创建元素并逐个添加到文档中。这种方法虽然直观,但在处理大量元素时效率较低。例如:
```javascript
for (var i = 0; i < 1000; i++) {
var el = document.createElement('p');
el.innerHTML = i;
document.body.appendChild(el);
}
```
为了优化这个过程,我们可以使用 `document.createDocumentFragment()` 方法创建一个文档片段来暂存创建的节点,然后再一次性将片段添加到文档中。这种方法确实可以提升性能。
```javascript
var frag = document.createDocumentFragment();
var htmlArray = []; // 用于存放HTML字符串的数组
for (var i = 0; i < 1000; i++) {
htmlArray.push('
' + i + '
'); // 将每个段落添加到数组中}
document.body.appendChild(frag); // 将文档片段添加到页面中
```
```javascript
// 先准备一个样板节点模板(例如一个包含
标签的div)
var templateDiv = document.createElement('div'); // 创建模板容器div并设置它的innerHTML属性包含p标签模板内容
templateDiv.innerHTML = '
...
'; // 这里填充你的模板内容,例如一个段落等。注意确保模板的有效性。// 使用循环克隆模板节点并添加到文档中以提高效率
for (var i = 0; i < 1000; i++) {
var cloneEl = templateDiv.cloneNode(true); // 使用true参数来深复制节点及其子节点内容到新的节点上
cloneEl.firstChild.textContent = i; // 修改克隆节点的子节点内容(这里是文本内容)以适应循环需求
document.body.appendChild(cloneEl); // 将克隆的节点添加到文档中展示内容
}
创建大量段落元素并添加到文档片段中
我们创建一个文档片段 `frag`。接着,通过循环创建大量的 `
` 元素,并将它们添加到 `frag` 中。当所有段落都添加完毕后,我们将整个片段一次性添加到文档的 body 中。这样,我们可以在不阻塞浏览器的情况下快速生成大量元素。现在,让我们稍作改进。我们依然使用文档片段 `frag`,但这次我们从文档中获取第一个 `
` 元素作为模板进行克隆,而不是每次都创建一个新元素。这样做可以节省时间和资源。下面是改进后的代码:
```javascript
var frag = document.createDocumentFragment();
var pEl = document.getElementsByTagName('p')[0]; // 获取第一个段落元素作为模板
for (var i = 0; i < 1000; i++) {
var el = pEl.cloneNode(false); // 克隆模板元素
el.innerHTML = i; // 修改克隆元素的内部HTML
frag.appendChild(el); // 将元素添加到文档片段中
}
document.body.appendChild(frag); // 将整个片段添加到文档中
```
使用 `firstChild` 和 `nextSibling` 遍历 DOM 元素
除了使用 `childNodes` 属性遍历 DOM 元素外,我们还可以利用 `firstChild` 和 `nextSibling` 属性来实现同样的效果。这两个属性可以更简洁地遍历元素的所有子节点。下面是如何使用这两个属性进行遍历的示例:
```javascript
var node = element.firstChild; // 从第一个子节点开始遍历
while (node) {
// 在这里处理节点...
node = node.nextSibling; // 移动到下一个兄弟节点
}
```
删除 DOM 节点的注意事项
在删除 DOM 节点之前,我们需要先删除注册在该节点上的所有事件。这是因为如果事件没有被正确移除,可能会导致内存无法被有效回收。在比较 `removeChild` 和设置 `innerHTML=''` 的效果时,后者通常能更有效地释放节点占用的内存。在需要删除节点时,优先考虑使用设置 `innerHTML` 的方式。
使用事件代理
优化JavaScript代码:事件监听、循环与局部变量
在前端开发中,优化JavaScript代码对于提升网页性能至关重要。本文将探讨如何通过合理的事件监听、优化循环以及有效利用局部变量来提升代码的执行效率。
一、事件监听
现代JS库多采用observe方式创建事件监听,这种方式有效隔离了DOM对象和事件处理函数之间的循环引用,有助于减少内存占用和提高性能。我们应优先采用这种方式来创建事件监听。
二、局部变量与重复调用
为了避免多次取值的调用开销,我们可以将重复使用的值保存到局部变量中。例如,计算元素高度时,可以先获取一次element1.clientHeight的值,然后保存到局部变量eleHeight中,再用于后续的计算。
三、优化NodeList访问
访问NodeList对象的次数应该尽可能减少,以改进脚本的性能。当使用getElementsByTagName()或其他方法获取NodeList时,要清楚何时返回NodeList对象,以便最小化对它们的访问。合理利用NodeList对象可以极大地提升代码执行速度。
四、循环优化
1. 减值迭代:大多数循环使用一个从0开始、增加到某个值的迭代器。在很多情况下,从最大值开始、在循环中不断减值的迭代器更加高效。
2. 简化终止条件:循环的终止条件应该尽可能简单快速。可以将循环控制量保存到局部变量中,避免在循环的每一步重复取值。例如,遍历数组或列表时,提前将length保存到局部变量中。
3. 简化循环体:循环体是执行最多的部分,应该确保其被最大限度地优化。减少不必要的操作,避免在循环体内进行复杂的计算。
4. 后测试循环:相对于前测试循环,如do-while这种后测试循环可以避免最初终止条件的计算,因此运行更快。在某些情况下,可以考虑使用后测试循环来提高性能。
五、展开循环
当循环次数确定时,有时候消除循环并使用多次函数调用会更快。这种情况下,可以考虑将循环逻辑拆分为多个独立函数,以提高代码的可读性和执行效率。
提高代码性能的关键在于避免双重解释和不必要的复杂性。让我们深入探讨一些JavaScript编程中的最佳实践。
尽量减少使用eval函数。eval函数在运行时需要重新调用解释引擎,这会导致时间消耗并引发安全性问题。相反,我们应优先使用具有明确语义和高效执行的方式编写代码。
避免给setTimeout或setInterval传递字符串参数。字符串参数需要在运行时进行解析,这会增加不必要的开销。相反,应传递函数引用,这样可以提高代码的执行效率。
当我们谈论否定检测时,逻辑非操作符提供了一种简洁而有效的方式来检查变量是否为假值(null、undefined、false等)。使用if (!oTest)这样的语句可以一次性检查多个条件,避免了冗长的代码。
关于条件分支,我们应该按照可能性从高到低的顺序排列条件分支,以减少解释器对条件的探测次数。当涉及到多个条件分支时,使用switch语句通常比if语句更高效,特别是在IE浏览器中。我们还可以考虑使用三目运算符来简化条件分支的代码。
在编程过程中,使用常量是一个良好的习惯。对于任何在多处使用的值,我们都应该考虑将其抽取为常量。对于用户界面字符串、URLs以及任何可能会更改的值,也应采用类似的处理方式。这样做有助于提高代码的可读性和可维护性。
在JavaScript的世界里,尊重对象的所有权至关重要。由于JavaScript的灵活性,它允许我们在任何时候修改任意对象,这虽然带来了无限的可能性,但同时也带来了潜在的风险。如果我们不拥有某个对象的所有权,就不应该随意修改它的对象、方法或属性。这是对编程职业道德的尊重,也是对代码稳定性的保障。
在JavaScript中,为了防止不可预见的错误和潜在的对象所有权问题,我们需要遵循一些重要的原则。我们要避免为实例或原型添加额外的属性或方法,避免重定义已经存在的方法或团队成员已经实现的功能。我们必须尊重对象的原始设计和结构,不去随意修改那些不属于我们的对象。
当我们需要为对象添加新功能时,应该采取更为谨慎和合理的方式。我们可以创建一个包含所需功能的新对象,并通过适当的方式与原有对象进行交互。我们还可以创建自定义类型,继承需要修改的类型,并为自定义类型添加额外的功能。这样既能满足我们的需求,又能避免潜在的对象所有权问题。
我们还需要注意循环引用的问题。循环引用是指两个或多个对象之间相互引用,导致它们无法被正常释放和回收。如果循环引用涉及到DOM对象或ActiveX对象,就可能导致内存泄露。即使刷新页面或关闭浏览器,这部分内存也可能不会被释放。一个简单的循环引用示例是:为一个DOM元素添加一个闭包作为扩展属性时,可能会形成循环引用。为了避免这种情况,我们可以采取一些措施来打破循环引用,例如将引用的DOM对象置空。通过将DOM对象置空,我们可以避免上下文中的循环引用问题。这是一种有效的解决方案,可以帮助我们避免潜在的内存泄露问题。
---
关于DOM对象的返回方法
当我们需要从DOM中获取一个元素并返回时,可以使用如下方法:
```javascript
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {
// 执行相关操作
}
return el; // 返回该元素
}
init(); // 执行函数并获取元素
```
为确保资源的妥善管理,可以采用try-finally结构来尝试返回元素,并在finally块中确保元素引用被清除:
```javascript
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {
// 执行相关操作
}
try {
return el; // 尝试返回元素
} finally {
el = null; // 确保元素引用被清除
}
}
init(); // 执行函数并进行资源管理
```
构建新的Context:打断循环引用与DOM对象的上下文
在JavaScript中,有时我们需要将函数与特定元素(如DOM元素)关联起来,但又希望避免循环引用的问题。为此,我们可以将函数从元素的上下文中抽离出来,从而切断两者之间的直接联系。这样做可以更有效地管理内存和资源。例如:
```javascript
function elClickHandler() {
// 此处定义与特定元素相关的操作逻辑
}
function init() {
---
松散耦合:HTML与JavaScript的优雅解耦
在软件开发中,"松散耦合"是一个核心概念,尤其在前端开发中,它强调的是组件或模块之间的独立性和可重用性。当我们谈论HTML与JavaScript的关系时,这一概念显得尤为重要。
一、HTML与JavaScript的紧密耦合
在传统的开发实践中,我们经常可以看到HTML与JavaScript紧密耦合的情况。JavaScript直接写在HTML文件中,或者使用包含内联代码的标签,这种方式虽然简单,但却存在着明显的问题。
这种紧密耦合的做法带来了诸多不便。它不利于代码的维护和复用。当JavaScript代码量增大,复杂性提高时,将其与HTML混杂在一起会使代码变得难以理解和维护。这种耦合限制了模块化的可能性,使得代码的模块化和组件化变得困难。这阻碍了代码的可重用性和可扩展性。
二、解耦HTML与JavaScript
为了解决这个问题,我们需要实现HTML与JavaScript的解耦。这意味着我们需要将JavaScript代码从HTML文件中分离出来,将其写为独立的文件或模块。这样可以使代码结构更加清晰,易于维护和管理。这也为代码模块化、组件化提供了可能,大大提高了代码的可重用性和可扩展性。
在实现解耦的过程中,我们可以使用各种技术,如事件监听、DOM操作等,来实现HTML元素与JavaScript之间的交互。通过这种方式,我们可以保持HTML的语义结构,同时使JavaScript代码更加清晰、可复用。这种解耦的做法不仅可以提高代码质量,也有助于提高网站或应用的性能和用户体验。
在这个基础上,我们还可以进一步采用现代前端框架和库,如React、Vue等,来更好地实现HTML与JavaScript的解耦。这些框架提供了强大的组件化、模块化支持,使得我们能够将复杂的界面和功能拆分为独立的、可复用的组件或模块,从而实现更高级别的代码组织和解耦。
解耦HTML与JavaScript是前端开发的重要方向之一。通过解耦,我们可以提高代码质量,增强代码的可维护性、可复用性和可扩展性,为前端开发带来更多的可能性。
文章从网络整理,文章内容不代表本站观点,转账请注明【蓑衣网】