每个人都有自己的编程风格,也无可避免的要去感受别人的编程风格——修改别人的代码。"修改别人的代码"对于我们来说的一件很痛苦的事情。因为有些代码并不是那么容易阅读、可维护的,让另一个人来修改别人的代码,或许最终只会修改一个变量,调整一个函数的调用时机,却需要花上1个小时甚至更多的时间来阅读、缕清别人的代码。本文一步步带你重构一段获取位置的"组件"——提升你的javascript代码的可读性和稳定性。

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

分离你的javascript代码

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

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

这一段代码,我们暂且认可它已经构成一个"组件"。

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

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

(function (window, namespace) {
var $ = window.jQuery,
$target,
$text,
status= ['获取中', '获取失败'];
function done(address) {//获取位置成功
$text.html(address);
}
function fail() {
$text.html(status[1]);
}
function checkData(data) {
//检查位置信息是否正确
return !!data;
}
function loadPosition() {
var data = '北京市';//获取位置中
if (checkData(data)) {
done(data);
} else
fail();
}
var init = function () {
$target.on('click', function () {
$text.html(status[0]);
loadPosition();
});
};
window[namespace] = function (targetId, textId) {
$target = $('#' + targetId);
$text = $('#' + textId);
initData();
setData();
}
})(window, 'linkFly');

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

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

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

和页面/DOM相关的都是不可信赖的(例如$target和$text),一旦页面结构发生改变,它的行为很大程度上也会随之改变。而函数也不应该依赖外部的环境。

在不可控的变量上,我们应该解开函数和依赖变量上的关系,让函数变得更加专注自己区域的逻辑,更加的纯粹。简单的说:函数所依赖的外部变量,都应该通过参数传递到函数内部。

新的代码如下:

(function (window, namespace) {
var $ = window.jQuery;
//检查位置信息是否正确
function checkData(data) {
return !!data;
}
//获取位置中
function loadPosition(done, fail) {
var data = '北京市';//获取位置中
if (checkData(data)) {
done(data);
} else
fail();
}
window[namespace] = function (targetId, textId) {
var $target = $('#' + targetId),
$text = $('#' + textId);
var status = ['获取中', '获取失败'];
$target.on('click', function () {
$text.html(status[0]);
loadPosition(function (address) {//获取位置成功
$text.html(address);
}, function () {//获取位置失败
$text.html(status[1]);
});
});
}
})(window, 'linkFly');

语义化和复用

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

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

(function (window, namespace) {
var $ = window.jQuery;
function checkData(data) {
return !!data;
}
function loadPosition(done, fail) {
var data = '北京市';//获取位置中
if (checkData(data)) {
done(data);
} else
fail();
}
window[namespace] = function (targetId, textId) {
var $target = $('#' + targetId),
$text = $('#' + textId),
changeEnum = { LOADING: '获取中', FAIL: '获取失败' },
changeStateText = function (text) {
$text.html(text);
};
$target.on('click', function () {
changeStateText(changeEnum.LOADING);
loadPosition(function (address) {
changeStateText(address);
}, function () {
changeStateText(changeEnum.FAIL);
});
});
}
})(window, 'linkFly');

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

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

上一层环境(整个大闭包)在我们的业务中扮演着工具的身份,它的任务是缔造一套和获取位置逻辑相关的工具,而在window[namespace])函数中,则是工具提供者的身份,它是唯一的入口,负责提供组件完整的业务给工具的使用者。

这里的$text.html()在逻辑上并不属于工具,而是属于工具提供者使用工具后所得到的反馈,所以changeStateText()函数置于工具提供者window[namespace]()中。

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

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

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

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

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

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

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

(function (window, namespace) {
var Gps = {
load: function (fone, fail) {
var data = '北京市';//获取位置伪代码
this.check(data) ?
done(data, Gps.state.OK) :
fail(Gps.state.FAIL);
},
check: function (data) {
return !!data;
},
state: { OK: 1, FAIL: 0 }
};
window[namespace] = Gps;
})(window, 'Gps');

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

至此,重构完成。

形成自己风格的代码

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

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


单var和多var

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

对象属性的屏蔽

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

(function () {
//第一种,曝露了_name属性
var Demo = function () {
if (!(this instanceof Demo))
return new Demo();
this._name = 'linkFly';
};
Demo.prototype.getName = function () {
return this._name;
} //第二种,多一层闭包意味内存消耗更大,但是屏蔽了_name属性
var Demo = function () {
var name = 'linkFly';
return {
getName: function () {
return name;
}
}
}
});

巧用变量置顶[hoisting]

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

(function () {
var names = [];
return function (name) {
addName(name);
}
function addName(name) {
if (!~names.indexOf(name))//如果存在则不添加
names.push(name);
console.log(names);// ["linkFly"]
}
}())('linkFly');

if和&&

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

(function () {
var key = 'linkFly',
cache = { 'linkFly': 'http://www.cnblogs.com/silin6/' },
value;
//&&到底
key && cache && cache[key] && (value = cache[key]);
//来个if
if (key && cache && cache[key])
value = cache[key];
})();

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

作者:linkFly
声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。

看完不要忘记推荐哟

编写更加稳定/可读的javascript代码的更多相关文章

  1. ASP.NET - 编写让别人能读懂的代码

    http://www.cnblogs.com/richieyang/p/4840614.html

  2. 新书《编写可测试的JavaScript代码 》出版,感谢支持

    本书介绍 JavaScript专业开发人员必须具备的一个技能是能够编写可测试的代码.不管是创建新应用程序,还是重写遗留代码,本书都将向你展示如何为客户端和服务器编写和维护可测试的JavaScript代 ...

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

    能够写出可维护的面向对象JavaScript代 码不仅可以节约金钱,还能让你很受欢迎.不信?有可能你自己或者其他什么人有一天会回来重用你的代码.如果能尽量让这个经历不那么痛苦,就可以节省不少时 间.地 ...

  4. 林大妈的JavaScript基础知识(二):编写JavaScript代码前的一些简单工作

    在介绍JavaScript语法前,我们需要知道,学习语法必须要多利用手敲代码来巩固记忆.因此,由于JavaScript的特性,它不能像C++和Java一样独立地编译及运行,我们需要在调试运行JavaS ...

  5. 编写可测试的JavaScript代码

    <编写可测试的JavaScript代码>基本信息作者: [美] Mark Ethan Trostler 托斯勒 著 译者: 徐涛出版社:人民邮电出版社ISBN:9787115373373上 ...

  6. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...

  7. 编写高质量JavaScript代码绳之以法(The Essentials of Writing High Quality JavaScript)翻译

    原文:The Essentials of Writing High Quality JavaScript 才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<Java ...

  8. 编写快速、高效的JavaScript代码

    许多Javascript引擎都是为了快速运行大型的JavaScript程序而特别设 计的,例如Google的V8引擎(Chrome浏览器,Node均使用该引擎).在开发过程中,如果你关心你程序的内存和 ...

  9. 编写高质量JavaScript代码的基本要点记录

    原文:深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 1.最小全局变量(Minimizing Globals)的重要性 JavaScript通过函数管理作用域.在 ...

随机推荐

  1. Spring定时器,定时执行(quartz)

    这个定时器与继承了timertask的定时器不同的是,这个定时器是更强大的,可以指定每分的第n秒,每天的第n时,每周的.每年的.来定时运行这个定时器.那么下面来讲诉如何使用quartz定时器. spr ...

  2. java线程(1)--概念基础

    参考:http://lavasoft.blog.51cto.com/62575/99150 http://blog.csdn.net/baby_newstar/article/details/6783 ...

  3. jquery删除数组中重复元素

    首先定义如下数组: var arr=[0,2,3,5,6,9,2]; 我们可以看到数组中存在重复元素'2'; 最后通过jquery筛选应该得到[0,2,3,5,6,9]; ok,首先我们再定义一个空数 ...

  4. 模拟checkbox的最优最简方法

    <label> <input type="/> <span class="iconfont"></span> </l ...

  5. Log4net 日志使用介绍

    概述 Log4net 有三个主要组件:loggers,appenders 和 layouts.这三个组件一起工作使得开发者能够根据信息类型和等级(Level)记录信息,以及在运行时控制信息的格式化和信 ...

  6. Dapper学习 - Dapper.Rainbow(三) - Read

    前面已经介绍了新增/修改/删除了, 接下来介绍一下Rainbow的Read方法. 一.Read -- Rainbow原生 1. 先看测试代码 var conStr = ConfigurationMan ...

  7. MySQL 函数大全

    mysql函数大全 对于针对字符串位置的操作,第一个位置被标记为1. ASCII(str) 返回字符串str的最左面字符的ASCII代码值.如果str是空字符串,返回0.如果str是NULL,返回NU ...

  8. 再谈ABC

    最近一直在看蒋老师那13篇<我的WCF之旅>,终于看完了,看得很慢,记得最初出来工作的时候那时的技术总监建议我去看的,可几个月前我才开始看,看了几个月才把13篇看完.第一篇WCF的博文是我 ...

  9. Java总结篇系列:java.lang.Object

    从本篇开始,将对Java中各知识点进行一次具体总结,以便对以往的Java知识进行一次回顾,同时在总结的过程中加深对Java的理解. Java作为一个庞大的知识体系,涉及到的知识点繁多,本文将从Java ...

  10. webapi初学项目(增删改查)

    初学wenapi做了一个从数据库增删改查的项目 webapi: 1.创建项目:visual C# —> ASP.NET MVC 4 web应用程序 模板—>web api; 2.注册路由: ...