编写更加稳定、可读性强的JavaScript代码

每个人都有自己的编程

格,也无可避免的要去感受别人的编程风格——修改别人的代码。”修改别人的代码”对于我们来说的一件很痛苦的事情。因为有些代码并不是那么容易阅读、可维

护的,让另一个人来修改别人的代码,或许最终只会修改一个变量,调整一个函数的调用时机,却需要花上1个小时甚至更多的时间来阅读、缕清别人的代码。本文
一步步带你重构一段获取位置的”组件”——提升你的javascript代码的可读性和稳定性。

本文内容如下:

  • 分离你的javascript代码
  • 函数不应该过分依赖外部环境
  • 语义化和复用
  • 组件应该关注逻辑,行为只是封装
  • 形成自己的风格的代码

 分离你的javascript代码

下面一段代码演示了难以阅读/修改的代码:


  1. (function (window, namespace) { 
  2.     var $ = window.jQuery; 
  3.     window[namespace] = function (targetId, textId) { //一个尝试复用的获取位置的"组件" var $target = $('#' + targetId),//按钮 $text = $('#' + textId);//显示文本 $target.on('click', function () { $text.html('获取中'); var data = '北京市';//balabala很多逻辑,伪代码,获取得到位置中 
  4.             if (data) { 
  5.                 $text.html(data); 
  6.             } else 
  7.                 $text.html('获取失败'); 
  8.         }); 
  9.     } 
  10. })(window, 'linkFly'); 

这一段代码,我们暂且认可它已经构成一个”组件”。
上面的代码就是典型的一个方法搞定所有事情,一旦填充上内部的逻辑就会变得生活不能自理,而一旦增加需求,例如获取位置返回的数据格式需要加工,那么就要去里面寻找处理数据的代码然后修改。

我们分离一下逻辑,得到代码如下:


  1. (function (window, namespace) { 
  2.     var $ = window.jQuery, 
  3.         $target, 
  4.         $text, 
  5.         states= ['获取中', '获取失败']; 
  6.     function done(address) {//获取位置成功 
  7.         $text.html(address); 
  8.     } 
  9.     function fail() { 
  10.         $text.html(states[1]); 
  11.     } 
  12.     function checkData(data) { 
  13.         //检查位置信息是否正确 
  14.         return !!data; 
  15.     } 
  16.     function loadPosition() { 
  17.         var data = '北京市';//获取位置中 
  18.         if (checkData(data)) { 
  19.             done(data); 
  20.         } else 
  21.             fail(); 
  22.     } 
  23.     var init = function () { 
  24.         $target.on('click', function () { 
  25.             $text.html(states[0]); 
  26.             loadPosition(); 
  27.         }); 
  28.     }; 
  29.     window[namespace] = function (targetId, textId) { 
  30.         $target = $('#' + targetId); 
  31.         $text = $('#' + textId); 
  32.         initData(); 
  33.         setData(); 
  34.     } 
  35. })(window, 'linkFly'); 

 函数不应该过分依赖外部环境

上面的代码中,我们已经把整个组件,切割成了各种函数(注意这里我说的是函数,不是方法),这里常出现一个新的问题:函数过分依赖不可控的变量。

变量$target和$text身为环境中的全局变量,从组件初始化便赋值,而我们切割后的代码大多数的操作方法都依赖$text,尤其是$text和done()、fail()之间暧昧的关系,一旦$text相关的结构、逻辑改变,那么我们的代码将会进行不小的改动。

和页面/DOM相关的都是不可信赖的(例如$target和$text),一旦页面结构发生改变,它的行为很大程度上也会随之改变。而函数也不应该依赖外部的环境。
在不可控的变量上,我们应该解开函数和依赖变量上的关系,让函数变得更加专注自己区域的逻辑,更加的纯粹。简单的说:函数所依赖的外部变量,都应该通过参数传递到函数内部。
新的代码如下:


  1. (function (window, namespace) { 
  2.     var $ = window.jQuery; 
  3.     //检查位置信息是否正确 
  4.     function checkData(data) { 
  5.         return !!data; 
  6.     } 
  7.     //获取位置中 
  8.     function loadPosition(done, fail) { 
  9.         var data = '北京市';//获取位置中 
  10.         if (checkData(data)) { 
  11.             done(data); 
  12.         } else 
  13.             fail(); 
  14.     } 
  15.     window[namespace] = function (targetId, textId) { 
  16.        var  $target = $('#' + targetId), 
  17.             $text = $('#' + textId); 
  18.         var states = ['获取中', '获取失败']; 
  19.         $target.on('click', function () { 
  20.             $text.html(states[0]); 
  21.             loadPosition(function (address) {//获取位置成功 
  22.                 $text.html(address); 
  23.             }, function () {//获取位置失败 
  24.                 $text.html(states[1]); 
  25.             }); 
  26.         }); 
  27.     } 
  28. })(window, 'linkFly'); 

 语义化和复用

变量states是一个数组,它描述的行为难以阅读,每次看到states[0]都有一种分分钟想捏死原作者的冲动,因为我们总是要记住变量states的值,在代码上,我们应该尽可能让它可以很好的被阅读。

另外,上面的代码中$text.html就是典型的代码重复,我们再一次的修改代码,请注意这一次修改的代码中,我们所抽离的changeStateText()的代码位置,它并没有被提升到上一层环境中(也就是整个大闭包的环境)。


  1. (function (window, namespace) { 
  2.     var $ = window.jQuery; 
  3.     function checkData(data) { 
  4.         return !!data; 
  5.     } 
  6.     function loadPosition(done, fail) { 
  7.         var data = '北京市';//获取位置中 
  8.         if (checkData(data)) { 
  9.             done(data); 
  10.         } else 
  11.             fail(); 
  12.     } 
  13.     window[namespace] = function (targetId, textId) { 
  14.         var $target = $('#' + targetId), 
  15.             $text = $('#' + textId), 
  16.             changeEnum = { LOADING: '获取中', FAIL: '获取失败' }, 
  17.             changeStateText = function (text) { 
  18.                 $text.html(text); 
  19.             }; 
  20.         $target.on('click', function () { 
  21.             changeStateText(changeEnum.LOADING); 
  22.             loadPosition(function (address) { 
  23.                 changeStateText(address); 
  24.             }, function () { 
  25.                 changeStateText(changeEnum.FAIL); 
  26.             }); 
  27.         }); 
  28.     } 
  29. })(window, 'linkFly'); 

提及语义化,我们必须要知道当前整个代码的逻辑和语义:

在这整个组件中,所有的函数模块可以分为:工具和工具提供者。

上一层环境(整个大闭包)在我们的业务中扮演着工具的身份,它的任务是缔造一套和获取位置逻辑相关的工具,而在window[namespace])函数中,则是工具提供者的身份,它是唯一的入口,负责提供组件完整的业务给工具的使用者。
这里的$text.html()在逻辑上并不属于工具,而是属于工具提供者使用工具后所得到的反馈,所以changeStateText()函数置于工具提供者window[namespace]()中。

 组件应该关注逻辑,行为只是封装

到此为止,我们分离了函数,并让这个组件拥有了良好的语义。但这时候来了新的需求:当没有获取到位置的时候,需要进行一些其他的操作。这时候会发现,我们需要window[namespace]()上加上新的参数。

当我们加上新的参数之后,又被告知新的需求:当获取位置失败了之后,需要修改一些信息,然后再次尝试获取位置信息。

不过幸好,我们的代码已经把大部分的逻辑抽离到了工具提供者中了,对整个工具的逻辑影响并不大。

同时我们再看看代码就会发现我们的组件除了工具提供者之外,没有方法(依赖在对象上的函数)。也就是说,我们的组件并没有对象。

我见过很多人的代码总是喜欢打造工具提供者,而忽略了工具的本质。迎合上面的增加的需求,那么我们的工具提供者将会变得越来越重,这时候我们应该思考到:是不是应该把工具提供出去?

让我们回到最初的需求——仅仅只是一个获取位置的组件,没错,它的核心业务就是获取位置——它不应该被组件化。它的本质应该是个工具对象,而不应该和页面相关,我们从一开始就不应该关注页面上的变化,让我们重构代码如下:


  1. (function (window, namespace) { 
  2.     var Gps = { 
  3.         load: function (fone, fail) { 
  4.             var data = '北京市';//获取位置伪代码 
  5.             this.check(data) ? 
  6.                 done(data, Gps.state.OK) : 
  7.                 fail(Gps.state.FAIL); 
  8.         }, 
  9.         check: function (data) { 
  10.             return !!data; 
  11.         }, 
  12.         state: { OK: 1, FAIL: 0 } 
  13.     }; 
  14.     window[namespace] = Gps; 
  15. })(window, 'Gps'); 

在这里,我们直接捏死了工具提供者,我们直接将工具提供给外面的工具使用者,让工具使用者直接使用我们的工具,这里的代码无关状态、无关页面。

至此,重构完成。

 形成自己风格的代码

之所以讲这个是因为大家都有自己的编程风格。有些人的编程风格就是开篇那种代码的…

我觉得形成自己的编程风格,是建立在良好代码的和结构/语义上的。否则只会让你的代码变得越来越难读,越来越难写。

****

单var和多var

我个人是喜欢单var风格的,不过我觉得代码还是尽可能在使用某一方法/函数使用前进行var,有时候甚至于为了单var而变得丧心病狂:由于我又过分的喜爱函数表达式声明,函数表达式声明并不会在var语句中执行,于是偶尔会出现这种边声明边执行的代码,为了不教坏小朋友就不贴代码了(我不会告诉你们其实是我找不到了)。

对象属性的屏蔽

下面的代码演示了两种对象的构建,后一种通过闭包把内部属性隐藏,同样,两种方法都实现了无new化,我个人…是不喜欢看见很多this的..但还是推荐前者。


  1. (function () { 
  2.     //第一种,曝露了_name属性 
  3.     var Demo = function () { 
  4.         if (!(this instanceof Demo)) 
  5.             return new Demo(); 
  6.         this._name = 'linkFly'; 
  7.     }; 
  8.     Demo.prototype.getName = function () { 
  9.         return this._name; 
  10.     } 
  11.  
  12.     //第二种,多一层闭包意味内存消耗更大,但是屏蔽了_name属性 
  13.     var Demo = function () { 
  14.         var name = 'linkFly'; 
  15.         return { 
  16.             getName: function () { 
  17.                 return name; 
  18.             } 
  19.         } 
  20.     } 
  21. }); 

巧用变量置顶[hoisting]

巧用函数声明的变量置顶特性意味着处女座心态的你放弃单var,但却可以让你的函数在代码结构上十分清晰,例如下面的代码:


  1. (function () { 
  2.     var names = []; 
  3.     return function (name) { 
  4.         addName(name); 
  5.     } 
  6.     function addName(name) { 
  7.         if (!~names.indexOf(name))//如果存在则不添加 
  8.             names.push(name); 
  9.         console.log(names);// ["linkFly"] 
  10.     } 
  11. }())('linkFly'); 
  12.  
  13. if和&& 

这种代码,在几个群里都见过讨论:


  1. (function () { 
  2.     var key = 'linkFly', 
  3.         cache = { 'linkFly': 'http://www.cnblogs.com/silin6/' }, 
  4.         value; 
  5.     //&&到底 
  6.     key && cache && cache[key] && (value = cache[key]); 
  7.     //来个if 
  8.     if (key && cache && cache[key]) 
  9.         value = cache[key]; 
  10. })(); 

大概就想到这么些了,我突然发现我不太推荐的代码,都是我写的代码,囧。如果各位也还有更多有趣的代码,希望各位看官能掏出来让小弟见识见识。

来源:51CTO

时间: 2024-10-06 09:54:54

编写更加稳定、可读性强的JavaScript代码的相关文章

【译】如何编写避免垃圾开销的实时 JavaScript 代码

本文讲的是[译]如何编写避免垃圾开销的实时 JavaScript 代码, 哇,这篇文章已经写了有很长一段时间了,十分感谢那些精彩的回复!其中有一些对于一些技术的指正,如使用 'delete' .我知道了使用它可能会导致其他的降速问题,因此,我们在引擎中极少使用它.一如既往的你还需要对所有的事进行权衡并且需要通过其他关注点来平衡垃圾回收机制,这也只是一个在我们引擎中发现的的实用.简单的技术列表,它并不是一个完整的参考列表.但是我希望它还是有用的! 一个用 Javascript 编写的 HTML5

如何编写可维护的面向对象JavaScript代码

能够写出可维护的面向对象JavaScript代码不仅可以节约金钱,还能让你很受欢迎.不信?有可能你自己或者其他什么人有一天会回来重用你 的代码.如果能尽量让这个经历不那么痛苦,就可以节省不少时间.地球人都知道,时间就是金钱.同样的,你也会因为帮某人省去了头疼的过程而获得他的偏爱. 但是,在开始探索如何编写可维护的面向对象JavaScript代码之前,我们先来快速看看什么是面向对象.如果已经了解面向对象的概念了,就可以直接跳 过下一节. 什么是面向对象? 面向对象编程主要通过代码代表现实世界中的实

深入理解JavaScript系列(1) 编写高质量JavaScript代码的基本要点_javascript技巧

具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多. 此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档.执行同行评审以及运行JSLint.这些习惯和最佳做法可以帮助你写出更好的,更易于理解和维护的代码,这些代码在几个月或是几年之后再回过头看看也是会觉得很自豪的. 书写可维护的代码(Writing Maintainable Code ) 软件bug的修复是昂贵的,并且

编写高质量JavaScript代码的基本要点_javascript技巧

才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会是件很美妙的事情.具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多. 此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档.执行同行评审以及运行JSLint.这些习惯和最佳做法可以

研发周报:神奇!1KB JavaScript代码编写的3D蜜蜂

研发周报:神奇!1KB JavaScript代码编写的3D蜜蜂 发表于2013-03-29 13:43| 次阅读| 来源CSDN| 0 条评论| 作者张红月 研发周报JavaScriptPatrick Wyatt游戏开发第三方应用开放平台开源Polycode 摘要:忙碌的一周总算过去,闲暇时不妨来细细品味我们精心为你呈现的这份技术大餐.本期热点:神奇!1KB JavaScript代码编写的3D蜜蜂:魔兽之父专访:今年游戏产业会出现 一场革命:回顾:那些被平台方封杀的第三方应用. 我们挑选了本周研

使用CoffeeScrip优美方式编写javascript代码_javascript技巧

JavaScript无疑是在web最伟大的发明之一,几乎一切网页动态效果都是基于它丰富的计算能力.而且它的能力在各种新的JavaScript的Engine下也越来越强了,比如Google Chrome用的V8 Engine. 但是由于诞生的太早,有很多语法定义在今天看来有些效率低下了,一些更加先进的语法形式,由于历史原因,没办法加入到现在的JavaScript语言中,可以说一种遗憾. 世界上的很多天才都在为构建更好的JavaScript而努力.已经有了很多尝试,其中最有前途的,无非就是Coffe

javascript引擎-用javascript编写一个登陆的页面,求代码 ,急求!!

问题描述 用javascript编写一个登陆的页面,求代码 ,急求!! 像是这样的:function request(paras){//获取url参数 var url=location.href; var paraString=url.substring(url.indexOf("?")+1,url.length).split("&"); var paraObj={}; for (i=0; j=paraString[i]; i++)paraObj[j.sub

JAVASCRIPT代码编写俄罗斯方块网页版_javascript技巧

俄罗斯方块方块是小时候的一个回忆,从最开始的掌上的黑白游戏机,到电视游戏机,到电脑,无不有它的痕迹,今天我们来一起重温它的一种实现方法,也算是整理一下我的思路吧...... HTML+CSS+JS实现俄罗斯方块完整版,素材只有图片,想要的下载图片按提示名字保存,css中用的时候注意路径!!主要在JS中!JS附有详细注释 效果: 按键提示:[键盘按键] 素材:图片名字与代码里对应 1.背景图片:tetris.png 2.失败时候的弹出框图片:game-over.png 3.七种色彩小方块图片:  

《编写可测试的JavaScript代码》——1.4 小结

1.4 小结 编写可测试的JavaScript代码,并不能自动从敏捷.瀑布.TDD.BDD或任何其他软件开发方式中产生结果.可测试的JavaScript是编写短小.松耦合.独立的简单小块代码的一个保证.如何编写这样的代码取决于我们自己.希望本书能够帮助大家了解编写这种代码的方式. 编写可测试的代码会让我们的工作以及后续者的工作变得更加容易.从更少的Bug到更容易修复的Bug,从容易测试到简单调试,编写可测试的JavaScript是让我们保持清醒的方式. 最重要的是,不要忘记我们是为人编写代码,而