JavaScript基础笔记(十四)最佳实践

最佳实践

一)松散耦合

1.解耦HTML/JavaScript:

1)避免html种使用js

2)避免js种创建html

2.解耦CSS/JS

操作类

3.解耦应用逻辑和事件处理

以下是要牢记的应用和业务逻辑之间松散耦合的几条原则:
勿将 event 对象传给其他方法;只传来自 event 对象中所需的数据;
任何可以在应用层面的动作都应该可以在不执行任何事件处理程序的情况下进行;
任何事件处理程序都应该处理事件,然后将处理转交给应用逻辑。
牢记这几条可以在任何代码中都获得极大的可维护性的改进,并且为进一步的测试和开发制造了很
多可能 。

二)尊重对象所有权

1.尽量不修改不属于你的对象

2.避免全局变量(以单一的全局变量作为一个容器,在其中定义其他对象)

3.避免与null进行比较(能直接进行类型或值检查的请检查)

如果看到了与 null 比较的代码,尝试使用以下技术替换:
如果值应为一个引用类型,使用 instanceof 操作符检查其构造函数;
如果值应为一个基本类型,使用 typeof 检查其类型;
如果是希望对象包含某个特定的方法名,则使用 typeof 操作符确保指定名字的方法存在于对
象上。

4.常量的使用

var Constants = {
INVALID_VALUE_MSG: "Invalid value!",
INVALID_VALUE_URL: "/errors/invalid.php"
};
function validate(value){
if (!value){
alert(Constants.INVALID_VALUE_MSG);
location.href = Constants.INVALID_VALUE_URL;
}
}

三)性能问题

1.避免全局查找

使用全局变量和函数肯定要比局部的开销更大 。

    function updateUI(){
        var imgs = document.getElementsByTagName("img");
        for (var i=0, len=imgs.length; i < len; i++){
            imgs[i].title = document.title + " image " + i;
        }
        var msg = document.getElementById("msg");
        msg.innerHTML = "Update complete.";
    }

更改后:

    function updateUI(){
        var doc = document;
        var imgs = doc.getElementsByTagName("img");
        for (var i=0, len=imgs.length; i < len; i++){
            imgs[i].title = doc.title + " image " + i;
        }
        var msg = doc.getElementById("msg");
        msg.innerHTML = "Update complete.";
    }

2.避免with语句

3.避免不必要的属性查找

在计算机科学中,算法的复杂度是使用 O 符号来表示的。最简单、最快捷的算法是常数值即 O(1)
之后,算法变得越来越复杂并花更长时间执行

标 记 名 称 描 述
O(1) 常数 不管有多少值,执行的时间都是恒定的。一般表示简单值和存储在变量中的值
O(log n) 对数 总的执行时间和值的数量相关,但是要完成算法并不一定要获取每个值。例如:二分查找
O(n) 线性 总执行时间和值的数量直接相关。例如:遍历某个数组中的所有元素
O(n2) 平方 总执行时间和值的数量有关,每个值至少要获取n次。例如:插入排序

对象上的任何属性查找都要比访问变量或者数组花费更长时间,
因为必须在原型链中对拥有该名称的属性进行一次搜索

问题代码:

var query = window.location.href.substring(window.location.href.indexOf("?"));

改进后

var url = window.location.href;
var query = url.substring(url.indexOf("?"));

4.优化循环

(1) 减值迭代——大多数循环使用一个从 0 开始、增加到某个特定值的迭代器。在很多情况下,从
最大值开始,在循环中不断减值的迭代器更加高效。
(2) 简化终止条件——由于每次循环过程都会计算终止条件,所以必须保证它尽可能快。也就是说
避免属性查找或其他 O(n)的操作。
(3) 简化循环体——循环体是执行最多的,所以要确保其被最大限度地优化。确保没有某些可以被
很容易移出循环的密集计算。
(4) 使用后测试循环——最常用 for 循环和 while 循环都是前测试循环。而如 do-while 这种后测
试循环,可以避免最初终止条件的计算,因此运行更快。

待优化:

for (var i=0; i < values.length; i++){
     process(values[i]);
}

优化1:

for (var i=values.length -1; i >= 0; i--){
    process(values[i]);
}

进一步优化:

var i=values.length -1;
if (i > -1){
    do {
        process(values[i]);
    }while(--i >= 0);
}

5.避免双重解释

JavaScript 代码想解析 JavaScript 的时候就会存在双重解释惩罚。当使用 eval()函数或者是
Function 构造函数以及使用 setTimeout()传一个字符串参数时都会发生这种情况

//某些代码求值——避免!!
eval("alert('Hello world!')");
//创建新函数——避免!!
var sayHi = new Function("alert('Hello world!')");
//设置超时——避免!!
setTimeout("alert('Hello world!')", 500);

在以上这些例子中,都要解析包含了 JavaScript 代码的字符串。这个操作是不能在初始的解析过程
中完成的,因为代码是包含在字符串中的,也就是说在 JavaScript 代码运行的同时必须新启动一个解
析器来解析新的代码。

6.其他:

原生方法较快——只要有可能,使用原生方法而不是自己用 JavaScript 重写一个。原生方法是用
诸如 C/C++之类的编译型语言写出来的,所以要比 JavaScript 的快很多很多。 JavaScript 中最容
易被忘记的就是可以在 Math 对象中找到的复杂的数学运算;这些方法要比任何用 JavaScript
的同样方法如正弦、余弦快的多。
Switch 语句较快 —— 如果有一系列复杂的 if-else 语句,可以转换成单个 switch 语句则可
以得到更快的代码。还可以通过将 case 语句按照最可能的到最不可能的顺序进行组织,来进一
步优化 switch 语句。
位运算符较快 —— 当进行数学运算的时候,位运算操作要比任何布尔运算或者算数运算快。选
择性地用位运算替换算数运算可以极大提升复杂计算的性能。诸如取模,逻辑与和逻辑或都可
以考虑用位运算来替换。

7.最小化语句数

JavaScript 代码中的语句数量也影响所执行的操作的速度。完成多个操作的单个语句要比完成单个
操作的多个语句快 。

8.优化DOM操作

1)减少现场更新

一旦你需要访问的 DOM 部分是已经显示的页面的一部分,那么你就是在进行一个现场更新。之所
以叫现场更新,是因为需要立即(现场)对页面对用户的显示进行更新。每一个更改,不管是插入单个
字符,还是移除整个片段,都有一个性能惩罚,因为浏览器要重新计算无数尺寸以进行更新。现场更新
进行得越多,代码完成执行所花的时间就越长;完成一个操作所需的现场更新越少,代码就越快。

//10次现场更新
var list = document.getElementById("myList"),
item,
i;
for (i=0; i < 10; i++) {
item = document.createElement("li");
list.appendChild(item);
item.appendChild(document.createTextNode("Item " + i));
}

改进后

//一次现场更新:
var list = document.getElementById("myList"),
fragment = document.createDocumentFragment(),
item,
i;
for (i=0; i < 10; i++) {
item = document.createElement("li");
fragment.appendChild(item);
item.appendChild(document.createTextNode("Item " + i));
}
list.appendChild(fragment);

2)使用innerHTML

有两种在页面上创建 DOM 节点的方法:使用诸如 createElement()appendChild()之类的
DOM 方法,以及使用 innerHTML。对于小的 DOM 更改而言,两种方法效率都差不多。然而,对于大
DOM 更改,使用 innerHTML 要比使用标准 DOM 方法创建同样的 DOM 结构快得多。

使用 innerHTML 的关键在于(和其他 DOM 操作一样)最小化调用它的次数。例如,下面的代码
在这个操作中用到 innerHTML 的次数太多了:

var list = document.getElementById("myList"),
i;
for (i=0; i < 10; i++) {
list.innerHTML += "<li>Item " + i + "</li>"; //避免!!!
}

3)使用事件代理

大多数 Web 应用在用户交互上大量用到事件处理程序。页面上的事件处理程序的数量和页面响应
用户交互的速度之间有个负相关。为了减轻这种惩罚,最好使用事件代理。

事件代理,用到了事件冒泡。任何可以冒泡的事件都不仅仅可以在事
件目标上进行处理,目标的任何祖先节点上也能处理。使用这个知识,就可以将事件处理程序附加到更
高层的地方负责多个目标的事件处理。如果可能,在文档级别附加事件处理程序,这样可以处理整个页
面的事件。

4)注意HTMLCollection

任何时候要访问 HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进
行一个查询,这个查询开销很昂贵。最小化访问 HTMLCollection 的次数可以极大地改进脚本的性能。
也许优化 HTMLCollection 访问最重要的地方就是循环了。前面提到过将长度计算移入 for 循环
的初始化部分。现在看一下这个例子:

var images = document.getElementsByTagName("img"),
i, len;
for (i=0, len=images.length; i < len; i++){
//处理
}

这里的关键在于长度 length 存入了 len 变量,而不是每次都去访问 HTMLCollection length
属性。当在循环中使用 HTMLCollection 的时候,下一步应该是获取要使用的项目的引用,如下所示,
以便避免在循环体内多次调用 HTMLCollection

var images = document.getElementsByTagName("img"),
image,
i, len;
for (i=0, len=images.length; i < len; i++){
image = images[i];
//处理
}

进行了对 getElementsByTagName() 的调用;
获取了元素的 childNodes 属性;
获取了元素的 attributes 属性;
访问了特殊的集合,如 document.formsdocument.images

Simple is important!
原文地址:https://www.cnblogs.com/Shadowplay/p/8711999.html