本文总结了 9 个实用的 JavaScript 编程技巧,涵盖类型转换、运算符使用、正则表达式、数组处理、内存管理、对象创建、作用域模拟以及 DOM 操作性能优化。重点解析了 NodeList 动态更新导致的无限循环问题及解决方案,并提供了避免频繁 DOM 重绘的性能建议,帮助开发者写出更高效、规范的代码。
JavaScript 基础技巧:判断、运算符与正则
1. 巧妙利用条件判断:
在 JavaScript 中,NaN、undefined、Null、0 和空字符串("")在转换为布尔值时均为 false。因此,可以利用这一特性简化代码。
代码如下:
if(!obj) {}
上述代码表示当对象为假值时执行的操作。由于 obj 若为上述任一假值,其布尔值为 false,取反后即为 true,从而无需编写冗长的 if(obj==null || obj == NaN ...) 判断。
2. 巧妙利用运算符:
这里介绍一个获取时间戳的经典技巧。
代码如下:
var dataspan = new Date()*1;
JavaScript 是弱类型语言,Date() 返回表示时间的字符串。通过算术运算(乘以 1),字符串会被隐式转换为数字,即得到时间戳。
3. 巧妙利用正则表达式:
代码如下:
/.a/ig.exec('xsas')
这相当于创建了一个 RegExp 对象并调用 exec 方法。当然,也可以调用其他方法,如 test()、match() 等。
数组处理与内存管理
4. 获取数组最大值和最小值:
代码如下:
var values = [1,2,3,40,23];
var max = Math.Max.apply(Math,values);
调用 Max.apply,将上下文对象设置为 Math,并传入 values 数组,即可确定最大值。
5. 内存优化:
代码如下:
function p(){this.p='moersing'}; var p1 = new p();
p1.xx
p1.xx
.......
p1=null; // 执行完操作之后,最后手动解除对 p1 的引用。
对象创建与作用域模拟
6. 最受欢迎的创建对象方式(原型模式):
代码如下:
function c(){
this.name ='moersing';
this.age=18;
this.books=['javascript develop','C# develop'];
}
c.prototype={
displayBookName:function (){
foreach(var t in this.books)
{
document.write(this.books[t]);
}
}
}
原型构造模式的最大缺点在于引用类型的共享,因此建议将引用类型定义在构造函数中,而将通用方法定义在原型中,使用 this 引用。
7. 块级作用域和私有变量
在 JavaScript 中,没有块级作用域和私有变量的概念,但可以利用某些特性模拟这些效果。
7.1 块级作用域:
代码如下:
(function(){
//块级作用域
}
)();
在匿名函数外加上括号,可称为“函数标准化”,即可以像标准函数那样调用,例如:
代码如下:
var name =function(){};
(name)();//一般不会这么写;
这样做的好处是,在 () 外部无法访问到函数中的变量,从而形成块级作用域。这种方式通常用于编写插件,避免在全局(global)中添加额外变量。此外,函数执行完毕后,其内部定义的变量会被销毁,因此也不存在闭包特性带来的问题。
7.2 私有变量:
代码如下:
function private()
{
var name = 'moersing';
this.getName = function(){
return this.name;
}
}
私有变量实际上是利用函数的作用域作为限制(外部无法访问),然后定义一个方法返回相应的变量,仅此而已。
DOM 操作与性能优化
8. DOM 之 NodeList:
nodeList 是一个动态的元素集合,这意味着在文档中添加任何元素,nodeList 都会实时更新。例如:
代码如下:
var alldiv = document.getElementsByTagName('div');
for(var i=0;i<alldiv.length;i++)
{
var div = document.createElement('div');
div.innerHTML= i.toString();
document.body.appendChild(div);
}
这段代码会造成无限循环。在循环内部创建了一个 div,然后通过 appendChild 方法将其添加到 body 中,导致 alldiv 立即更新,因此 i<alldiv.length 永远无法成立。要解决这个问题,可以使用以下方式:
代码如下:
var alldiv = document.getElementsByTagName('div');
var len,i;
for(i=0,len=alldiv.length;i<len;i++)
{
var div = document.createElement('div');
div.innerHTML= i.toString();
document.body.appendChild(div);
}
这里建议:最好不要频繁地对 NodeList 操作,因为每次操作都会执行一次 DOM 树的查询。
除了以上介绍的方法外,HTML5 新加入的 API(selector API Level1)也能解决这个问题。它类似 C# 的 LINQ 即时查询,至于什么是 LINQ 即时查询,以后会更新博客,敬请关注:
代码如下:
var allDiv= document.querySelectorAll('div');
for(var i=0;i<alldiv.length;i++)
{
var div = document.createElement('div');
div.innerHTML= i.toString();
document.body.appendChild(div);
}
querySelectorAll 需要一个参数,即 CSS 选择器,类似 jQuery 中的 $()。它返回的 NodeList 是一个及时的、非动态的 DOM 集合。
另外还有一个 querySelector,返回匹配的第一个元素。有关 HTML5 API 详见
http://www.w3.org/standards/techs/dom#w3c_all
或者
https://developer.mozilla.org/zh-CN/docs/Web/API
另外,本人也在酝酿一篇博客,专门讲 HTML5 API 的,敬请关注。
9. DOM 性能:
不要做这种傻事(我做过……)
代码如下:
for(var i=0;i<10;i++)
{
document.querySelector('ul').innerHTML="<li>"+i+"</li>";
}
给对象的 innerHTML 赋值,会调用内置的 C++ 解析器解析这个字符串。虽然速度很快,但最好不要这样操作,因为会造成一定的性能流失。
最好这样做:
代码如下:
var ih=null;
for(var i=0;i<10;i++)
{
ih+="<li>"+i+"</li>";
}
document.querySelector('ul').innerHTML=ih;
另外的一些性能优化话题,等有时间再更新。
以上所述就是本文的全部内容了,希望大家能够喜欢。