早该知道的7个JavaScript技巧

作者:寒星

时间:2026-10-30

浏览:145

本文总结了JavaScript开发中七个高效技巧,包括对象与数组的字面量简写、JSON数据格式应用、三元运算符简化逻辑、利用Math.max处理边界值、split/join操作CSS类名、事件委托优化性能以及模块模式隔离全局变量。掌握这些技巧有助于提升代码简洁性与可维护性。

文章目录

引言

我从事JavaScript编程已有很长时间,具体起始年份已难以考证。对于这门语言近年来的发展与成就,我深感兴奋,并幸运地从中受益。尽管我撰写了大量文章、章节以及一本专著,但仍不断发现新的知识点。以下分享的是一些曾让我惊叹的编程技巧,建议立即尝试,而非等待未来偶然发现。

简洁写法

简洁写法

JavaScript中我最喜欢的特性之一是创建对象和数组的简写语法。过去,若要创建一个对象,代码通常如下所示:


 var car = new Object();
 car.colour = 'red';
 car.wheels = 4;
 car.hubcaps = 'spinning';
 car.age = 4;

以下写法可实现相同效果:


 var car = {
  colour:'red',
  wheels:4,
  hubcaps:'spinning',
  age:4
 }

这样更加简洁,无需重复使用对象名称。此时car变量已定义。需要注意的是,在IE浏览器中可能会遇到invalidUserInSession的问题,只要确保右大括号前不添加逗号,即可避免此类麻烦。

另一个便捷的简写方式适用于数组。传统定义数组的方法如下:


 var moviesThatNeedBetterWriters = new Array(
  'Transformers','Transformers2','Avatar','IndianaJones 4'
 );

简写版本如下:


 var moviesThatNeedBetterWriters = [
  'Transformers','Transformers2','Avatar','IndianaJones 4'
 ];

对于数组,这里存在一个误区:实际上并不存在真正的“图组”功能。但常有人像定义上述car对象那样定义数组,写法如下:


 var car = new Array();
 car['colour'] = 'red';
 car['wheels'] = 4;
 car['hubcaps'] = 'spinning';
 car['age'] = 4;

数组并非万能工具;这种写法是错误的,容易引起混淆。图组实际上是对象的功能,人们常将两者概念混淆。

另一个非常酷的简写方式是使用三元条件运算符。无需编写如下代码:


 var direction;
 if(x < 200){
  direction = 1;
 } else {
  direction = -1;
 }
…

你可以使用三元条件运算符简化逻辑: var direction = x < 200 ? 1 : -1; 当条件为true时取问号后的值,否则取冒号后的值。

使用JSON格式存储数据

使用JSON格式存储数据

在发现JSON之前,我使用各种复杂方法将数据存储于JavaScript固有数据类型中,例如数组、字符串,并夹杂容易拆分的标志符号及其他繁琐操作。Douglas Crockford发明JSON后,情况彻底改变。使用JSON,你可以利用JavaScript原生功能将数据以复杂格式存储,无需额外转换即可直接访问。JSON是“JavaScript Object Notation”的缩写,它结合了上述两种简写方法。因此,若要描述一个乐队,你可以这样编写:


 var band = {
  "name":"The Red Hot Chili Peppers",
  "members":[
  {
  "name":"Anthony Kiedis",
  "role":"lead vocals"
  },
  {
  "name":"Michael 'Flea' Balzary",
  "role":"bass guitar, trumpet, backing vocals"
  },
  {
  "name":"Chad Smith",
  "role":"drums,percussion"
  },
  {
  "name":"John Frusciante",
  "role":"Lead Guitar"
  }
  ],
 "year":"2009"
 }

你可以在JavaScript中直接使用JSON,将其封装在函数中,甚至作为API的返回值形式。这被称为JSON-P,许多API都采用这种形式。 你可以调用数据源,在script代码中直接返回JSON-P数据:


 <div id="delicious"></div><script>
 function delicious(o){
  var out = '<ul>';
  for(var i=0;i<o.length;i++){
  out += '<li><a href="' + o[i].u + '">' +
  o[i].d + '</a></li>';
  }
  out += '</ul>';
  document.getElementById('delicious').innerHTML = out;
 }
 </script>
 <script src="http://feeds.delicious.com/v2/json/codepo8/javascript?count=15&callback=delicious"></script>

这是调用Delicious网站提供的Web service功能,获取JSON格式的最近无序书签列表。

基本上,JSON是最轻量级描述复杂数据结构的方法,且能在浏览器中运行。你甚至可以在PHP中使用json_decode()函数来处理它。JavaScript的内置函数(Math、Array和String)中,令我惊讶的是,深入研究这些函数后,我发现它们能极大简化编程工作。使用它们,你可以省去复杂的循环处理和条件判断。例如,过去寻找数字数组中最大数时,我会编写如下循环:


 var numbers = [3,342,23,22,124]; 
 var max = 0;
 for(var i=0;i<numbers.length;i++){
  if(numbers[i] > max){
  max = numbers[i];
  }
 }
 alert(max);

现在无需循环即可实现:


 var numbers = [3,342,23,22,124];
 numbers.sort(function(a,b){return b - a});
 alert(numbers[0]);

需要注意的是,你不能对数字字符数组进行sort()操作,因为此时它仅按字母顺序排序。若想了解更多用法,可阅读这篇关于sort()的优秀文章。

另一个有趣的函数是Math.max()。该函数返回参数中最大的数字: Math.max(12,123,3,2,433,4); // returns 433 由于该函数能校验数字并返回最大值,因此可用于测试浏览器对某特性的支持情况:


 var scrollTop=Math.max(
  doc.documentElement.scrollTop,
  doc.body.scrollTop
 );

这是解决IE兼容性问题的一种方法。你可以获取当前页面的scrollTop值,但根据页面DOCTYPE的不同,上述两个属性中仅有一个存放该值,另一个为undefined。因此,你可以通过使用Math.max()获取该数值。阅读相关文章可了解更多关于使用数学函数简化JavaScript的知识。

另外,split()和join()是一对非常有用的字符串操作函数。最具代表性的例子是编写一个为页面元素附加CSS样式的功能。

具体而言,当给页面元素附加CSS class时,要么它是该元素的第一个CSS class,要么它已有一些class,需在已有class后加空格再追加新class。当你要去掉这个class时,也需要去掉其前面的空格(这在旧版浏览器中非常重要,因为某些老浏览器无法识别后面跟着空格的class)。

因此,原始写法如下:


 function addclass(elm,newclass){
  var c = elm.className;
  elm.className = (c === '') ? newclass : c+' '+newclass;
 }

你可以使用split()和join()函数自动完成此任务:


 function addclass(elm,newclass){
  var classes = elm.className.split(' ');
  classes.push(newclass);
 elm.className = classes.join(' ');
 }

这会确保所有class都被空格分隔,且你要追加的class正好位于最后。

事件委派

事件委派

Web应用由事件驱动运行。我喜欢事件处理,尤其是自定义事件。它能使产品具备可扩展性,而无需修改核心代码。然而存在一个重大问题(也可视为强大功能的体现),即移除页面上的事件监听器。你可以为某个元素安装事件监听器,使其开始工作。但页面上并无指示表明存在监听器。由于这种不可见性(尤其令新手头疼),以及像IE6这样的“浏览器”在使用过多事件监听器时会出现内存问题,你不得不承认尽量减少事件编程是明智之举。

于是,事件委托应运而生。

当页面上某个元素触发事件时,若该元素的所有子元素在DOM继承关系上也能接收此事件,此时你可以使用父元素上的事件处理器来处理,而非使用一堆子元素上的事件监听器。具体含义是什么?举例说明,页面上有许多超链接,你不想直接操作这些链接,而是希望通过一个函数调用链接,HTML代码如下:


 <h2>Great Web resources</h2>
 <ul id="resources">
 <li><a href="http://opera.com/wsc">Opera Web Standards Curriculum</a></li>
 <li><a href="http://sitepoint.com">Sitepoint</a></li>
 <li><a href="http://alistapart.com">A List Apart</a></li>
 <li><a href="http://yuiblog.com">YUI Blog</a></li>
 <li><a href="http://blameitonthevoices.com">Blame it on the voices</a></li>
 <li><a href="http://oddlyspecific.com">Oddly specific</a></li>
 </ul>

常见做法是通过循环这些链接,将每个链接附加一个事件处理器:


 // 典型的事件处理例子
 (function(){
  var resources = document.getElementById('resources');
  var links = resources.getElementsByTagName('a');
  var all = links.length;
  for(var i=0;i<all;i++){
  // Attach a listener to each link
  links[i].addEventListener('click',handler,false);
  };
  function handler(e){
  var x = e.target; // Get the link that was clicked
  alert(x);
  e.preventDefault();
  };
 })();

我们仅用一个事件处理器也能完成此任务:


 (function(){
  var resources = document.getElementById('resources');
  resources.addEventListener('click',handler,false);
  function handler(e){
  var x = e.target; // get the link tha
  if(x.nodeName.toLowerCase() === 'a'){
  alert('Event delegation:' + x);
   e.preventDefault();
  }
  };
 })();

因为点击事件发生在这些页面元素内,你只需比较它们的nodeName,找出应响应该事件的元素即可。

免责声明:上述两个事件示例在所有浏览器中均可运行,除IE6外。在IE6上,你需要使用一种特定的事件模型,而非简单的W3C标准实现。这也是推荐使用工具包的原因。

这种方法的好处不仅限于将多个事件处理器缩减为一个。试想,若需动态向链接表追加更多链接,使用事件委托后无需其他修改;否则,你需要重新循环链接表,并为每个链接重新安装事件处理器。

匿名函数和模块化

匿名函数和模块化

JavaScript中最令人懊恼的事情之一是变量缺乏作用域。任何变量、函数、数组、对象,只要不在函数内部,均被视为全局变量。这意味着页面上的其他脚本也可以访问它,甚至覆盖重写它。

解决方法是将变量置于匿名函数内部,定义完成后立即调用。例如,以下写法将产生三个全局变量和两个全局函数:


 var name = 'Chris';
 var age = '34';
 var status = 'single';
 function createMember(){
 // [...]
 }
 function getMemberDetails(){
 // [...]
 }

若页面上的其他脚本中存在名为status的变量,麻烦便会发生。若将它们封装在myApplication中,问题即可解决:


 var myApplication = function(){
  var name = 'Chris';
  var age = '34';
  var status = 'single';
  function createMember(){
  // [...]
 }
  function getMemberDetails(){
  // [...]
  }
 }();

然而,这样一来函数外部便无功能可用。若此为你所需,则无妨。你还可以省略函数名称:


 (function(){
  var name = 'Chris';
  var age = '34';
  var status = 'single';
  function createMember(){
  // [...]
  }
  function getMemberDetails(){
  // [...]
  }
 })();

若想在函数外部使用内部内容,则需进行修改。为了能访问createMember()或getMemberDetails(),需将它们变为myApplication的属性,从而暴露于外部世界:


 var myApplication = function(){
  var name = 'Chris';
  var age = '34';
  var status = 'single';
  return{
  createMember:function(){
   // [...]
  },
  getMemberDetails:function(){
   // [...]
  }
  }
 }();
 //myApplication.createMember() 和
 //myApplication.getMemberDetails() 就可以使用了。

这被称为module模式或singleton。Douglas Crockford多次讨论过这些,Yahoo User Interface Library (YUI)中对此有大量应用。但这样一来令我感到不便的是,我需要改变句式以使函数和变量能被外界访问。更甚者,调用时还需加上myApplication前缀。因此,我不喜欢这样做,我更愿意简单地将需被外界访问的元素指针导出。这样做后,反倒简化了外界调用的写法:


 var myApplication = function(){
  var name = 'Chris';
  var age = '34';
  var status = 'single';
  function createMember(){
  // [...]
  }
  function getMemberDetails(){
  // [...]
  }
  return{
  create:createMember,
  get:getMemberDetails
  }
 }();
 //现在写成 myApplication.get()和 myApplication.create() 就行了。

我将其称为“revealing module pattern”。

可配置化

可配置化

一旦我将编写的JavaScript代码发布,便有人想修改它,通常是希望它完成原本无法完成的任务——但通常也是因为我编写的程序不够灵活,未提供用户自定义功能。解决方法是为脚本增加配置项对象。我曾撰写一篇深入介绍JavaScript配置项对象的文章,要点如下:

----在脚本中添加名为configuration的对象。 ----该对象内存放人们使用脚本时常需修改的内容: **CSS ID和类名称; **按钮名称、标签文字等; **诸如“每页显示图片数”的值,“图像显示尺寸”的值; **地点、位置及语言设置。 ----将该对象作为公共属性返回给用户,以便用户修改覆盖。 通常情况下,这是编程过程中最后一步要做的事情。我将这些集中体现在一个例子中:“Five things to do to a script before handing it over to the next developer.”

实际上,你也希望代码能方便人们使用,并根据各自需求进行一些调整。若实现此功能,你会减少收到那些抱怨脚本的困惑邮件,这些信件会告知你,有人修改了你的脚本且效果良好。

与后台交互

与后台交互

在多年的编程经历中,我领悟到重要一点:JavaScript是优秀的界面交互开发语言,但若用于处理数字或访问数据源,则略显力不从心。

最初学习JavaScript时,我是为了替代Perl,因为我讨厌必须将代码拷贝到cgi-bin文件夹下才能运行Perl。后来我明白,应使用后台语言处理主要数据,而不能事事由JavaScript承担。更重要的是要考虑安全性和语言特性。

若访问Web service,我可以获取JSON-P格式数据,在客户端浏览器中进行各种数据转换。但当我拥有服务器时,我有更多方法转换数据,可在Server端生成JSON或HTML格式数据返回给客户端,以及执行缓存数据等操作。若事先了解并准备这些,你将长期受益,省去许多烦恼。编写适用于各种浏览器的程序是浪费时间,请使用工具包!

在我最初从事Web开发时,访问页面时究竟使用document.all还是document.layers的问题上痛苦挣扎了很久。我选择了document.layers,因为我喜欢每个层都是独立document的思想(且我编写了大量document.write来生成元素)。层模式最终失败,于是我开始使用document.all。当Netscape 6公布仅支持W3C DOM模型时,我很高兴,但用户并不关心这些。用户只是看到该浏览器无法显示大多数浏览器能正常显示的内容——这是我们的编码问题。我们编写了短视的代码,仅能在当前环境下运行,而不幸的是,运行环境却在不断改变。

我已浪费太多时间处理各种浏览器版本的兼容性问题。善于处理此类问题为我提供了良好的工作机会。但现在我们不必再忍受这种痛苦。

一些工具包,例如YUI、jQuery以及Dojo,都能帮我们处理此类问题。它们通过抽象各种接口实现来处理浏览器问题,如版本不兼容、设计缺陷等,将我们从痛苦中解救出来。除非你要测试某个Beta版浏览器,千万不要在自己的程序中添加修正浏览器缺陷的代码,因为你很可能在浏览器已修复该问题时却忘了删除你的代码。

另一方面,完全依赖工具包也是短视行为。工具包可帮你快速开发,但若不深入理解JavaScript,你也会犯错。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章