《编写可维护的javascript》读书笔记

为什么要编写可维护的javascript?

  • 软件生命周期80%的成本消耗在了维护上。
  • 几乎所有的软件维护者都不是它的最初作者。
  • 编码规范提高了软件的可读性,它让工程师能够快速且充分地理解新的代码。

第一部分:编程风格

程序是写给人读的,只是偶尔让计算机执行以下。 ——高德纳

一、基本的格式化

1、缩进层级

如何处理缩进是几乎所有语言首先讨论的,缩进甚至关系到软件工程师的价值观!
第一种缩进:使用制表符进行缩进。例如:jQuery核心风格,Dojo编程风格。
第二种缩进:使用空格符进行缩进。例如:Goolge的javascript规定使用两个空格缩进。
推荐:4个空格字符为一个缩进层级,可以在编辑器中配置Tab键插入4个空格。

2、语句结尾

你加没加分号代码可能都会正常运行,没看到这里之前,你可能不知道这是分析器的自动分号插入机制(ASI)在偷偷的帮你干活。常言道:常在河边走,哪有不湿鞋?看看下面的代码。

function getData() {
        return 
        {
            text: '看看不加分号的后果!'
        }
    }
ASI会解析成下面的样子:
function getData() {
        return ;
        {
            text: '看看不加分号的后果!'
        };
    }

所以如果调用上面的getData的方法,返回的就是undefined。但是这样不能全怪分号,谁让你把花括号写到下面一行的?(反正我看到有第一个花括号跟之前的语句不在一行的,我就要暴走。)

//这段代码工作正常,尽管没有分号
function getData() {
        return {
            text: '看看不加分号的后果!'
        }
    }
3、行的长度: 倾向于80个字符。

如果一行多于80个字符,应当在一个运算符(逗号,加号等)后换行。下一行应当增加两级缩进。

4、命名

计算机科学只存在两个难题:缓存失效和命名。

ECMAScript遵照了小驼峰的命名法,所谓小驼峰,官方称之为“驼峰大小写命名法(Camel Case)”,是由小写字母开始的,后续每个单词首字母都大写。

//例如
var thisIsMyName;

变量命名前缀应该是名词。函数名前缀应该是动词。

//变量
var myName = 'chen';
//函数名
function getName() {
    return myName;
}
5、常量

在ECMAScript6之前,javascript中没有真正的常量概念。根据C语言的样子,约定使用大写字母和下划线来命名,下划线用以分割单词。

var MAX_COUNT = 10;
//现在我们应该也可以这样定义常量
const MAX_COUNT = 10;
6、构造函数

构造函数的命名遵照大驼峰命名法(Pascal Case),为什么?因为跟本地语言保持一致的命名约定,new RegExp(),正则本身就是一个内置的构造函数,看见大驼峰了木有?
科普一下:Pascal Case 和 Camel Case都表示“驼峰大小写”,区别在于:
Pascal Case是以大写字母开始,所以也叫大驼峰,例如:AnotherName;
Camel Case是以小写字母开始,之后的单词首字母大写,所以也叫小驼峰,例如:anotherName,之前在命名的那块提到过小驼峰。

7、直接量

直接量可以这么理解,它是程序中可以直接使用的数据,没有进行特别的封装。

2   //数字直接量
"其心"    //字符串直接量
[1,2,3] //数组直接量
true    //布尔型直接量
{name: "其心"}    //对象直接量
(function(){})()    //函数直接量
/*分割线*/
new String("其心")    //这就不是直接量

在直接量里着重说一下null,我就经常误解它,用的很随意,现在我明确了解了,应该在下列场景中使用它。

  • 用来初始化一个变量,这个变量可能赋值给一个对象
  • 用来和一个已经初始化的变量比较,这个变量可以是也可以不是一个对象。
  • 当函数的参数期望是对象时,用作参数出入。
  • 当函数的返回值期望是对象时,用作返回值传出。
    理解null最好的方式是让他们当做对象的占位符。
    直接量的用法简洁高效,是值得提倡的,这一点谨记。

二、注释

1、单行注释

单行注释有三种使用方法:

  • 独占一行的注释,用来解释下一行代码。这行注释之前总要有一个空行,且缩进层级和下一行代码保持一致。
// 好的写法
function print(){

    // 控制台输出数字1
    console.log(1);
}
  • 在代码行的尾部的注释。代码结束到注释之间至少有一个缩进。注释(包括之前代码部分)不应当超过单行最大字符数限制,如果超过了,就讲这条注释放置于当前代码的上方。
  • 被注释掉的打断代码。
//好的写法
// function print(){
//  console.log(1);
// }
2、多行注释

比较青睐Java风格的多行注释

/*
*这是一段注释
*这段注释包含两行文本
*/

多行注释之前也应该有一个空行,且缩进层级和其描述的代码保持一致。

3、文档注释

多提一点就是文档的注释,多行注释以单斜线加双星号(/**)开始,接下来是描述信息,其中使用@符号来表示一个或多个属性。

             /**
             * 提交JSON数据
             * @param actionUrl     交互的URL
             * @param json          JSON数据
             * @param postSuccessFn 提交成功的回调函数
             * @param postFailFn    提交失败的方法
             * @param isAsync       是否异步提交,true:是
             * @param timeout       请求时限
             */

三、语句和表达式

首先说一点,见到下面两种写法,都是合法的JavaScript的代码,但是不提倡使用。

// 不好的写法
if(condition)
    doSomething();
// 不好的写法
if(condition)   doSomething();
1、花括号的对齐方式

第一种风格是,将左花括号放置在块语句中第一句代码的末尾。这种风格继承自Java。

if (condition) {
    doSomething();  
}else {
    doSomethingElse();   
}

第二种风格是,将花括号放置于块语句首行的下一行。这是随着C#流行起来的。

if (condition) 
{
    doSomething();  
}else 
{
    doSomethingElse();   
}

为了避免导致错误的分号自动插入(还记得之前说过的“ASI”么),推荐使用第一种花括号对齐方式。

2、switch

对于switch的缩进之类可以直接用编辑器默认的,只提一点就是,如果程序没有默认的行为时,default是可以被省略的。

四、变量、函数和运算符

1、变量
建议将局部变量的定义作为函数内第一条语句,推荐使用单个var语句,每个变量的初始化独占一行。对于没有初始化的变量来说,它们应当出现在var语句的尾部。

function doSomething(items) {
    var value  = 10,
        result = value + 10,
        i,
        len;
    for(i = 0,len = items.length; i < len; i++){
        doOtherSomething(items[i]);
    }
}
2、函数声明

需要注意,函数声明应当在条件语句的外部使用。例如:

//不好的写法,大部分浏览器会自动使用第二个声明,并不会根据condition来做判断。
if (condition) {
        function doSomething() {
            alert("Hi!");
        }
    } else {
        function doSomething() {
            alert("Yo!");
        }
    }
3、相等

JavaScript具有强制类型转换的机制。

  • 数字和字符串比较,字符串会首先转换为数字,然后执行比较。
// 比较数字5和字符串5
console.log(5 == "5"); // true
// 比较数字25和十六进制的字符串25
console.log(25 == "0x19"); // true
  • 一个布尔值和数字比较,布尔值会首先转化为数字,然后进行比较,false是0,true是1。
// 比较数字1和true
console.log(1 == true); // true
// 比较数字0和false
console.log(0== false); // true
//比较数字2和true
console.log(2 == true); // false
  • 比较一个值是对象而另一个不是,会首先调用对象的valueOf()方法得到原始类型在进行比较。如果没有定义valueOf(),则调用toString()。
    由于强制类型转换的缘故,推荐用===或者!==,不应当使用==或者!=

五、避免“空比较”

1、检测原始值

JavaScript有5中简单的原始类型:字符串、数字、布尔值、null和undefined。最佳的选择是用typeof运算符,返回一个值的类型的字符串
用typeof检测一下4种原始值类型是非常安全的。

//检测字符串
if (typeof name === "string") {
    anotherName = name.substring(3);
}
//检测数字
if (typeof count === "number") {
    updateCount(count);
}
//检测布尔值
if (typeof found === "boolean" && found){ 
    message("Found!");
}
//检测underfined
if (typeof MyApp=== "undefined") {
    MyApp = {};
}
2、检测引用值

引用值也称为对象,检测某个引用值的类型的官方最好的方法是使用instanceof运算符。但是它不仅检测构造这个对象的构造器,还检测原型链。因为每个对象都继承自Object,因此每个对象的 value instanceof Object都会返回true

var now = new Date();
console.log(now instanceof object); // true
console.log(now instanceof Date) // true 
3、检测数组

最开始是判断sort()方法在不在。

function  isArray(value) {
    return typeof value.sort === "function";
} 

然后有了一种比较优雅的解决方案。

function isArray() {
    return Object.prototype.toString.call(value) === "[object Array]";
} 

再后来ECMAScript5将Array.isArray()正式引入JavaScript。

4、检测属性

判断属性是否存在的最好的方法是使用in运算符。in运算符只会简单地判断属性是否存在,而不会去读属性的值。但是检测出来的属性可以是对象自身的,也可以是继承来的。

var object = {
    count: 0,
    related: null
};
if ("count" in object) {
    //这里的代码会执行
}

如果只检查对象自身的某个属性是否存在,就使用hasOwnProperty()方法。

六、配置数据分离

配置数据类似:

  • URL
  • 需要展现给用户的字符串
  • 重复的值
  • 设置(比如每页的配置项)
  • 任何可能发生变更的值
    类似于各种工具化文件的config。

七、错误类型

  • Error: 所有的错误类型。实际上引擎从来不会抛出该类型的错误。
  • EvalError: 通过eval()函数执行代码发生错误时抛出。
  • RangeError: 一个数字超出它的边界时抛出。
  • ReferenceError: 期望的对象不存在时抛出。
  • SyntaxError: 给eval()函数传递的代码中有语法错误时抛出。
  • TypeError: 变量不是期望的类型时抛出。

八、阻止修改属性

  • 防止扩展
    禁止为对象“添加”属性和方法,但已存在的属性和方法是可以被修改或删除。
Object.preventExtension(); // 锁定对象
Object.isExtensible(); // 判断对象是否被锁定
  • 密封
    类似“防止扩展”,而且禁止为对象“删除”已存在的属性和方法。
Object.seal(); // 密封对象
Object.isSealed(); // 检测一个对象是否已被密封
  • 冻结
    类似“密封”,而且禁止为对象“删除”已存在的属性和方法。
Object.freeze(); // 冻结对象
Object.isFrozen(); // 判断一个对象是否已被冻结
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容