Git 实战系列(十)git diff 命令对比文件差异
对比两个分支中,所有文件的详细差异,常用于合并操作之后确认有没有遗漏文件:
1 | $ git diff branch1 branch2 |
对比两个分支中,指定文件的详细差异:
1 | $ git diff branch1 branch2 文件名(带路径) |
对比两个分支中,差异的文件列表:
1 | $ git diff branch1 branch2 --stat |
对比两个分支中,所有文件的详细差异,常用于合并操作之后确认有没有遗漏文件:
1 | $ git diff branch1 branch2 |
对比两个分支中,指定文件的详细差异:
1 | $ git diff branch1 branch2 文件名(带路径) |
对比两个分支中,差异的文件列表:
1 | $ git diff branch1 branch2 --stat |
本文总结出一些广受认可的编程最佳实践,用于解决特定领域的问题。
在 JavaScript 所有的糟糕特性之中,最为糟糕的一个就是它对全局变量的依赖。JS 大神 Douglas Crockford 甚至称之为“毒瘤”。想象一下,一个全局变量可以被程序的任何部分在任意时间修改,将使得程序的行为变得极度复杂。可怕的全局变量还带来了以下问题:
共有三种方式定义全局变量,这些方式都是我们要避免的:
1 | var foo = value; // 1、在任何函数之外放置一个 var 语句 |
下面是一些解决办法:
如果你编写的是一段不会被其它脚本访问到的完全独立的脚本,可以使用一个立即执行的匿名函数来创建私有作用域。
最小化使用全局变量的方法之一是为你的应用创建唯一一个全局变量,并将你所有的功能代码都挂载到这个全局对象上。这种做法既降低了模块之间发生冲突的可能,又能保证模块之间的正常通信。可以参考 JavaScript 模块模式。
目前这种单全局变量模式已经在各种流行的库中广泛使用了:
$
和 jQuery
。只有在 $
被其它库使用了的情况下,为了避免冲突,才使用 jQuery
。YUI
全局对象。dojo
全局对象。最后一种、也是最为推崇的做法是使用“模块化”方式组织代码:
JavaScript 独一无二之处在于任何东西都不是神圣不可侵犯的。默认情况下,你可以修改任何你可以触及的对象。解析器根本就不在乎这些对象是开发者定义的还是默认执行环境的一部分——只要是能访问到的对象都可以修改。如果你的代码没有创建这些对象,禁止修改它们,包括:
Object
、Array
等等);document
等等);window
等等);$
、jQuery
等等)。覆盖方法将会导致所有依赖该方法的代码失效:
1 | // 不好的写法 - 覆盖了 DOM 方法 |
新增方法将会导致未来潜在的命名冲突,因为一个对象此刻没有某个方法不代表它未来没有。更糟糕的是如果将来原生的方法和你新增的方法行为不一致,将会陷入一场代码维护的噩梦:
1 | // 不好的写法,在 DOM 对象上增加了方法 |
删除方法将会导致所有依赖该方法的代码运行时错误。对于已发布的库来说,无用的方法应该被标识位“废弃”而不是直接删掉:
1 | // 不好的写法 - 删除了 DOM 方法 |
下面介绍一些解决方法:
如果一种类型的对象已经做到了你想要的大多数工作,那么继承它然后再新增一些功能是最好的做法。JavaScript 中有两种基本的继承形式:
例如:
1 | var MyError = function(message) { |
JavaScript 的继承有一些很大的限制,就是无法继承自 DOM 或 BOM 对象。解决办法是利用门面模式为这些已存在的对象创建一个新的接口,达到二次封装的效果。jQuery 和 YUI 的 DOM 接口都使用了门面模式。例如:
1 | // 自定义一个 DOM 对象包装器 |
事件处理常见的问题是将事件处理程序和业务逻辑紧紧耦合在一起,降低了代码的可维护性:
1 | // 不好的写法 |
正确的做法应该是解耦事件处理程序和业务逻辑,提高代码的可维护性:
1 | // 好的写法 |
可见,业务逻辑不应该依赖于 event
对象来完成功能,原因如下:
event
对象作为参数并不能告诉你 event
的哪些属性是有用的,用来干什么?event
对象并作为参数传入。这迫使你关注方法内部实现,以确切地知道这个方法使用了哪些信息,这样才能正确地写出测试代码。关于“事件绑定(Event Binding)”和“事件委托(Event Delegation)”两种机制的区别在 本文 有详细的描述。简而言之,从“内存消耗”、“处理速度”、“新增元素的处理”三方面考虑,都更建议使用“事件委托”。下例演示了如何使用 jQuery 语法进行“事件委托”:
1 | $('#list').on('click', 'li', function() { |
当 #list
内任一 li
子元素被点击时,click
事件将冒泡到其父元素 #list
并触发 #list
的事件处理程序,即子元素的事件都委托给父元素进行处理。这种做法有利于提升性能,推荐使用。
保持 Web UI 层的松耦合,以便在以下场景中调试代码,定位问题:
这种快速定位问题的能力是 Web 界面可维护性的核心关键。
禁止使用 CSS 表达式(CSS Expression)。
1 | // 不好的写法 |
CSS 表达式是 IE8 及更早版本中的一个特性,它允许你将 JavaScript 直接插入到 CSS 中,这样可以在 CSS 代码中直接执行运算或其它操作。但 CSS 表达式会带来两个问题:
禁止在 JavaScript 脚本中直接操作 CSS 样式:
1 | // 不好的写法 |
当需要通过 JavaScript 来操作元素样式的时候,最佳方法是操作 CSS 的 className
:
1 | element.className = 'className'; // 原生方法 |
CSS 的 className
应该成为 CSS 和 JavaScript 之间通信的桥梁。JavaScript 不应当直接操作 CSS 样式,以便保持和 CSS 的松耦合。
禁止在 HTML 标签中嵌入 JavaScript 脚本:
1 | <!-- 不好的写法,不该直接为 HTML 标签的 on 属性挂载事件处理程序 --> |
这样会导致 HTML 页面和 JavaScript 脚本紧紧耦合。正确的做法应当是在外部脚本文件中添加事件处理程序:
1 | var doSomeThing() { } |
这种做法的优势在于,函数 doSomeThing()
的定义和事件处理程序的绑定都是在同一个文件中完成的。如果函数名称需要修改,则只需修改一个文件即可;如果点击发生时想额外做一些动作,也只需在一处做修改。
此外,不到迫不得已,不建议在 HTML 页面中嵌入 JavaScript 脚本:
1 | <!-- 不好的做法 --> |
不建议在 JavaScript 脚本文件中嵌入 HTML 操作:
1 | // 不好的做法 |
这样会导致 JavaScript 脚本和 HTML 标签紧紧耦合,从而降低了代码的可维护性,增加了跟踪文本和结构性问题的复杂度。正常来说,调试上述这段标签的典型方法,应当是先去浏览器调试工具中的 DOM 树中查找,然后打开页面的 HTML 源码对比其不同。一旦 JavaScript 脚本文件中做了除简单 DOM 操作之外的事情,如渲染标签,追踪 Bug 就变得很麻烦。因为脚本和标签都耦合成一坨了,让人望而却步。
HTML 文本和标签应该只存放于一个地方:可以控制你 HTML 代码的地方。最为推崇的做法是利用 JavaScript 模板引擎 解决这个问题。
项目中我引入了模板引擎 artTemplate 进行 HTML 渲染,并通过修改源码内置了两个常用的格式化工具:
详见 DEMO:finance-marketres-mobi\js\utility\util-demo.html
在团队开发中,所有的代码看起来风格一致是极其重要的,原因有以下几点:
当项目变得庞大时,统一的编程风格能够节省的大量时间成本。
本节编程风格(Style Guideline)是用于规范单文件中的代码,使团队编程风格保持一致。
每一行的层级由 四个空格 组成,避免使用制表符(Tab)进行缩进,以便在所有的系统和编辑器中,文件的展现格式不会有任何差异。建议在文本编辑器中配置敲击 Tab
键时插入四个空格。
1 | // 好的写法 |
每行长度不应该超过 80 个字符。如果一行多于 80 个字符,应当在一个运算符(逗号、加号等)后换行。下一行应当增加两级缩进(8 个字符)。
1 | // 好的写法 |
;
结束一个语句。禁止省略分号,因为:{}
包住块语句,可以让编程意图更清晰,降低修改代码时出错的几率。这里展示了一些例子:
1 | // 不好的写法,缺少花括号 |
二元操作符(如赋值、逻辑运算)前后必须使用一个空格来保持表达式的整洁。
1 | // 好的写法 |
注释有时候可以用于给一段代码声明额外的信息。这些声明的格式如下:
注释声明 | 描述 |
---|---|
TODO |
说明代码还未完成。此时应当描述下一步要做的事情。 |
HACK |
说明代码实现走了一个捷径。此时应当描述为何使用 hack 的原因。这也可能表明该问题可能会有更好的解决方法。 |
FIXME |
说明代码是有问题的需要尽快修复。此时应当描述问题出在哪里,或者提供解决方案。 |
REVIEW |
说明代码任何可能的改动都需要评审。 |
注释声明可以用于单行或多行注释,例如:
1 | // TODO: 我希望找到一种效率更快的实现方式 |
_
。所有字母大写,不同单词之间用单个下划线 _
分隔。
构造函数使用大驼峰式(Pascal Case)命名法,即以大写字母开头,后续每个单词首字母都大写。
函数变量使用前缀:fn
。
js-
。注意用于 JS 的类严禁用于样式文件中引用。前缀 | 描述 |
---|---|
ipt |
input 输入框 |
btn |
按钮 |
lbl |
Label |
chk |
CheckBox |
lnk |
A链接 |
img |
图片 |
JavaScript 中有三种基本包装类型:Boolean
、Number
、String
,每种类型都代表全局作用域中的一个构造函数,并分别表示各自对应的原始值的对象。基本包装类型的主要作用是让原始值具有对象般的行为。
禁止使用这些基本包装类型声明变量,应该直接使用对应的字面量:
类型 | 描述 | 注意项 |
---|---|---|
布尔值 | 统一使用字面量 true 、false 而不是构造函数 new Boolean() |
|
数字值 | 统一使用字面量,而不是构造函数 new Number() |
避免使用八进制字面量 |
字符串 | 统一使用单引号 '' ,而不是构造函数 new String() |
避免在字符串中使用斜杠 \ 另起一行 |
对象 | 统一使用字面量 {} 而不是构造函数 new Object() |
|
数组 | 统一使用字面量 [] 而不是构造函数 new Array() |
由于相等(==
)和不相等(!=
)操作符存在 自动类型转换 的问题,因此禁止使用。为了保持代码中数据类型的完整性,要求使用全等(===
)和不全等(!==
)操作符。
setTimeout()
、setInterval()
函数中的回调代码禁止使用字符串格式。
eval()
函数禁止使用。
常用的三种空链接跳转:
1 | # |
在具有块级作用域的语言中,在狭小的作用域内让变量声明和使用变量的代码尽可能彼此靠近,通常是个好的编程习惯。因此在编写 JavaScript 时常常会出现类似的惯性思维:
1 | for(var i = 0; i < 3; i++) { |
输出如下:
1 | for 语句内,i=0 |
但由于 JavaScript 中并没有块级作用域(block scope),只有函数作用域(function scope),因此函数内声明的所有变量在函数体内始终是可见的。这个特性被非正式地称为 声明提前(hoisting),即 JavaScript 函数内声明的所有变量(但不涉及赋值)都被“提前”至函数顶部。这步操作是在代码开始运行之前、JavaScript 引擎的“预编译”阶段进行的。上述代码编译如下:
1 | var i; // 变量声明提前 |
变量声明提前意味着:在函数内部任意地方声明变量和在函数顶部声明变量是完全一样的。为了让源代码能够非常清晰地反映出真实的变量作用域,避免潜藏错误,规范要求始终在函数顶部使用单 var
语句统一声明所有变量,例如:
1 | // 每个变量声明都独占一行,同时注意每行的缩进 |
和上述变量声明提前一样,函数声明也会被 JavaScript 引擎提前(function declaration hoisting)。因此,在代码中函数的调用可以出现在函数声明之前:
1 | // 不好的写法 |
这段代码是可以正常运行的,因为 JavaScript 引擎将这段代码解析为:
1 | // 函数声明提前 |
由于 JavaScript 的这种行为会放宽函数必须 先声明后使用 的要求,因此会导致代码混乱。
规范要求函数始终 先声明后使用。
更好的办法是使用 函数表达式 代替函数声明:
1 | // 好的写法 |
这种形式看起来像是常规的变量赋值语句,即创建一个函数并将它赋值给变量 doSomeThing
。这种情况下创建的函数叫做 匿名函数(anonymous function)(也称为 拉姆达函数),因为 function
关键字后面没有标识符,其 name
属性为空。
与使用函数声明的区别在于,如果执行顺序颠倒,函数调用 doSomeThing()
将会报错。因为函数表达式必须等到解析器执行到它所在的代码行,才会真正被解释执行:
1 | typeof doSomeThing === 'undefined'; // true |
除此之外,函数声明与函数表达式的语法其实是等价的。尽管如此,规范仍然要求优先使用函数表达式,原因有二:
使用函数表达式可以声明匿名函数,并将匿名函数赋值给变量或者属性:
1 | var doSomeThing = function() { |
这种匿名函数可以通过在最后加上一对圆括号 ()
来 立即执行并返回 一个值给变量:
1 | // 不好的写法 |
这种写法的问题在于,会让人误以为将一个匿名函数赋值给了这个变量。除非读完整段代码并看到最后一行的那对圆括号 ()
,否则你不会知道是将函数赋值给变量还是将函数的执行结果赋值给变量。这种困惑会影响代码的可读性。
为了让立即执行的函数能够被一眼看出来,可以用一对圆括号 ()
将函数包起来。这样做并不会影响代码的执行结果,却能让人一眼就看出这是个立即执行的函数:
1 | // 好的写法 |
此外,还可以使用立即执行的匿名函数(immediately executed anonymous function)来创建私有作用域,从而解决全局变量污染的问题。这种函数一般是没有返回值的:
1 | (function() { |
要注意的是在这种场景下,函数表达式外的那对圆括号 ()
绝不能省略,因为官方的语法假定以单词 function
开头的语句是一个函数声明语句,而函数声明语句是无法匿名的,否则会报错。
鉴于 ECMAScript 是松散类型(loosely typed)的,因此需要有一种手段来检测给定变量的数据类型——typeof
和 instanceof
操作符提供了这方面的信息:
typeof
操作符可能返回下列某个字符串:
类型字符串 | 描述 |
---|---|
undefined |
如果这个值未定义 |
boolean |
如果这个值是布尔值 |
string |
如果这个值是字符串 |
number |
如果这个值是数值 |
object |
如果这个值是对象或 null |
function |
如果这个值是函数 |
例如:
1 | typeof undefined |
由于在检测对象的值时,typeof
无法辨别出 null
与对象,因此建议使用下列这样的判断:
1 | var my_value = null; |
typeof
无法辨别出 NaN
和数字:
1 | typeof NaN === 'number'; // true |
isNaN()
函数可以解决这类判断问题:
1 | isNaN(NaN); // true |
typeof
无法辨别出 Infinity
和数字:
1 | typeof Infinity === 'number'; // true |
可以自定义一个 isNumber()
函数用于判断数字:
1 | function isNumber(value) { |
比较特殊的类型是 function
:
1 | typeof function(){} |
从技术角度讲,函数在 ECMAScript 中是对象,而不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过 typeof
操作符来区分函数和其他对象是有必要的。
typeof
操作符存在一个问题:在判断任何引用类型时都会返回 "object"
,因此 ECMAScript 引入了 instanceof
操作符来解决这个问题:
1 | [] instanceof Array |
除了 Object
之外,Array
类型恐怕是 ECMAScript 中最常用的类型了。ECMAScript 的数组特点如下:
创建 Array
实例的方式有两种。第一种是使用 Array
构造函数:
1 | var colors = new Array(); // 创建一个空数组 |
另一种方式是使用数组字面量表示法:
1 | var colors = []; // 创建一个空数组 |
针对数组有很多常用方法:
方法 | 描述 |
---|---|
push() |
入栈(向数组的末尾添加一个或更多元素,并返回新的长度) |
pop() |
出栈(删除并返回数组的最后一个元素) |
unshift() |
向数组的开头添加一个或更多元素,并返回新的长度。 |
shift() |
出队(删除并返回数组的第一个元素) |
方法 | 描述 |
---|---|
sort() |
按升序排列数组项 |
reverse() |
反转数组项的顺序 |
方法 | 描述 |
---|---|
concat() |
拼接并返回新数组 |
slice() |
裁剪并返回新数组 |
方法 | 描述 |
---|---|
join() |
把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 |
toString() |
把数组转换为字符串,并返回结果。 |
toLocaleString() |
把数组转换为本地字符串,并返回结果。 |
ECMAScript 5 新增的方法:
方法 | 描述 |
---|---|
indexOf() |
查询特定项在数组的起始索引 |
lastIndexOf() |
查询特定项在数组的结束索引 |
ECMAScript 5 新增的方法:
every()
和 some()
是一组相似的方法,用于查询数组中的项是否满足某个条件:
方法 | 描述 |
---|---|
every() |
对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true 。 |
some() |
对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true 。 |
方法 | 描述 |
---|---|
filter() |
对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。 |
map() |
对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。 |
forEach() |
对数组中的每一项运行给定函数。这个方法没有返回值。 |
这些数组方法通过执行不同的操作,可以大大方便处理数组的任务。
ECMAScript 5 新增的方法:
方法 | 描述 |
---|---|
reduce() |
从数组的第一项开始,逐个遍历到最后,执行给定的归并函数。 |
reduceRight() |
从数组的最后一项开始,向前遍历到第一项,执行给定的归并函数。 |
ECMAScript 中使用最多的类型就是 Object
。虽然 Object
的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们是非常理想的选择。
创建 Object
实例的方式有两种。第一种是使用 new
操作符后跟 Object
构造函数,如下所示:
1 | var person = new Object(); |
另一种方式是使用对象字面量表示法。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程:
1 | var person = { |
使用这种对象字面量语法要求的代码量更少,而且能够给人以封装数据的感觉。
实际上,对象字面量也是向函数传递大量可选参数的首选方式。一般来讲,命名参数虽然容易处理,但在有多个可选参数的情况下就会显示不够灵活。例如:
1 | function doSomething(arg0, arg1, arg2, arg3, arg4) { |
但最好的做法是对那些必需值使用命名参数,而使用对象字面量来封装多个可选参数:
1 | function doSomething() { |
由于在 ECMAScript 中 Object
是所有对象的基础,因此所有对象都具有下列这些基本的属性和方法:
属性 | 描述 |
---|---|
constructor |
保存着用于创建当前对象的函数。 |
isPrototypeOf(object) |
用于检查传入的对象是否是传入对象的原型。 |
hasOwnProperty(propertyName) |
用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。 |
propertyIsEnumerable(propertyName) |
用于检查给定的属性是否能够使用 for-in 语句来枚举。 |
toLocaleString() |
返回对象的字符串表示,该字符串与执行环境的地区对应。 |
toString() |
返回对象的字符串表示。 |
valueOf() |
返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值相同。 |
例如,要检查某个对象的专有属性,可以使用 hasOwnProperty(propertyName)
方法进行判断:
1 | var obj = {foo: 'foo', bar: 'bar'}; |
尽管 ECMAScript 是一门弱类型语言,但它的内部提供了五种基本数据类型以便开发者使用。下面分别介绍:
在使用 var
声明变量但未对其加以初始化时,这个变量的值就是 undefined
。
表示一个空对象指针。
String 类型用于表示 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号(""
)或单引号(''
)表示,内含转义字符。
虽然 Boolean 类型只有两个字面值——true
、false
,但 ECMAScript 中所有类型的值都有与之等价的值。下表给出了转换规则:
数据类型 | 转换为 true 的值 |
转换为 false 的值 |
---|---|---|
Boolean | true |
false |
String | 任何非空字符串 | "" (空字符串) |
Number | 任何非零数字值(包括无穷大 Infinity ) |
0 、NaN |
Object | 任何对象 | null |
Undefined | 不适用 | undefined |
这些转换规则对于理解流控制语句(如 if
语句)、布尔操作符(!
、&&
、||
)自动执行相应的 Boolean 转换非常重要,例如:
1 | if('false') {console.log('true')} |
字面量 | 描述 |
---|---|
70 |
十进制的 70。 |
-70 |
十进制的负 70。 |
070 |
八进制的 56。八进制字面值的第一位必须是零(0 ),然后是八进制数字序列(0~7 )。 |
0xA |
十六进制的 10。十六进制字面值的前两位必须是 0x ,后跟任何十六进制数字(0~9 及 A~F )。 |
3.125e7 |
科学计数法,表示“3.125 乘以 10 的 7 次幂(3.125 * Math.pow(10, 7) )”,即 31250000。推荐使用这种简洁的方式来表示那些极大或极小的数值。 |
3e-7 |
科学计数法,表示 0.0000003。默认情况下,ECMASctipt 会将那些小数点后面带有 6 个零以上的浮点数值转换为以 e 表示法表示的数值。 |
Infinity |
如果某次计算的结果得到了一个超出 ECMAScript 数值范围的值,那么该值将被自动转换成特殊的 Infinity 值。该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(即是否位于最小和最大的数值之间),可以使用 isFinite() 函数进行判断。 |
NaN |
非数值(Not a Number)是一个特殊的数值,用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。ECMAScript 定义了 isNaN() 函数,接收任何类型参数并(调用 Number() 函数)进行 自动类型转换,如果转换失败则这个参数“不是数值”。 |
Number 类型使用 IEEE754 格式来表示整数和浮点数值。这种格式有个通病:浮点数值计算会产生 舍入误差 的问题,从而导致无法测试 特定的 浮点数值,例如:
1 | if (a + b == 0.3) { // 不要做这样的浮点测试!例如 0.1 加 0.2 的结果不是 0.3,而是 0.30000000000000004。 |
可见,浮点数值的最高精度虽然有 17 位小数,但在进行算术计算时其精确度远远不如整数。因此建议先将浮点数值转换成整数值进行计算后,再转回浮点数,如此一来就能缓解这个问题。
此外,由于保存浮点数值需要的内存空间是保存整数值的 两倍,因此 ECMAScript 会不失时机地将浮点数值转换为整数值,例如:
1 | var floatNum1 = 1.; // 小数点后面没有数字——解析为 1 |
由于内存限制,ECMAScript 并不能保存世界上所有的数值,其限制范围下表:
常量 | 描述 |
---|---|
Number.MIN_VALUE |
ECMAScript 能够表示的最小数值,大多数浏览器中为 5e-324 |
Number.MAX_VALUE |
ECMAScript 能够表示的最大数值,大多数浏览器中为 1.7976931348623157e+308 |
如果某次计算的结果得到了一个超出 ECMAScript 数值范围的值,那么该值将被自动转换成特殊的 Infinity
值。该值将无法继续参与下一次的计算,因为 Infinity
不是能够参与计算的数值。要想确定一个数值是不是有穷的(即是否位于最小和最大的数值之间),可以使用 isFinite()
函数进行判断。
有 3 个函数可以把非数值转换为数值:Number()
、parseInt()
和 parseFloat()
。第一个函数可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。
但由于 Number()
函数在转换字符串时比较复杂而且不够合理,因此更常用的是 parseInt()
和 parseFloat()
函数。
parseInt()
函数在转换字符串时,更多的是看其是否符合数值模式:
1 | var num1 = parseInt(' 70'); // 70(忽略字符串前面的空格,直至找到第一个非空格字符) |
如果字符串中的第一个字符是数字字符,parseInt()
也能够识别出各种整数格式:
1 | var num6 = parseInt("70"); // 70(十进制数) |
为了消除在使用 parseInt()
函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换时使用的基数(即多少进制):
1 | var num1 = parseInt("10", 2); // 2 (按二进制解析) |
多数情况下,我们要解析的都是十进制数值,因此始终将 10 作为第二个参数是非常必要的。
为了便于操作基本类型值,ECMAScript 还提供了以下 3 个特殊的引用类型,它们都具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会隐式地创建一个对应的基本包装类型的对象,从而让我们能够调用一些实用方法来操作这些数据。
字面量 | 包装方法 | 实用方法 |
---|---|---|
true 、false |
Boolean() |
|
70 十进制070 八进制0xA 十六进制3.125e7 科学计数法 |
Number() |
toFixed(fractionDigits) 按照指定的小数位四舍五入toExponential(fractionDigits) 科学计数法toPrecision(precision) toString(radix) 使用指定基数(即多少进制)将数字转换为字符串 |
"" 、'' |
String() |
charAt() concat() substring() indexOf() toLowerCase() match() …… |
不建议显式地创建基本包装类型的对象,因为会造成 typeof
操作符判断不符合预期:
1 | typeof new Boolean(true) |
最后是一些类型转换的小技巧:
1 | var myVar = "3.14159", |
一个完整的 JavaScript 实现由下列三个不同的部分组成:
1 | +--------------------------+ |
下面分别介绍这些部分:
以网景的 Netscape Navigator 内置的 JavaScript 1.1 为蓝本,由 ECMA-262 定义的 ECMAScript 是一种 与 Web 浏览器没有依赖关系 的脚本语言标准,它由下列基础部分组成:
ECMA-262 定义的只是这门语言的基础部分,而在此基础之上,宿主环境(host environment) 可以构建更完善的脚本语言。常见的宿主环境有:
以我们最常见的 Web Broswer 为例,不仅提供了基本的 ECMAScript 实现,同时还提供了该语言的扩展,以便语言与环境之间对接交互。而这些扩展——如 DOM,则利用 ECMAScript 的核心类型(Types)和语法(Syntax)提供更多更具体的功能,以便实现针对环境的操作。
ECMA-262 目前已经发布了六个大版本的 ECMAScript:
版本 | 发布时间 | 描述 |
---|---|---|
1 | 1997 年 6 月 | 以网景的 Netscape Navigator 内置的 JavaScript 1.1 为蓝本制定,但删除了所有针对浏览器的代码,并支持 Unicode 标准(从而支持多语言开发)。 |
2 | 1998 年 6 月 | 基本没有修改。 |
3 | 1999 年 12 月 | 标准的第一次大修改,涉及:新增的正则表达式,更好的字符串处理,新的控制语句,try / catch 异常处理的支持,更严格的错误定义,数值格式化输出和其它增强功能。该版标志着 ECMAScript 成为了一门真正的编程语言。 |
该版对 ECMAScript 进行了大刀阔斧的修改,但由于复杂的语言政治分歧而被废弃了。 | ||
5 | 2009 年 12 月 | 澄清了第三版规范许多模糊之处,并增加了一些新功能,如:原生 JSON 对象、继承的方法和高级属性定义,以及“严格模式(strict mode)”。是目前浏览器兼容性最好、最主流的版本。 |
5.1 | 2011 年 6 月 | 基本没有修改。 |
6 | 2015 年 6 月 | 标准的又一次大修改,被称为 ECMAScript 2015。它为编写日益复杂的应用程序增加了大量重要的新语法,包括:类(classes)和模块(modules)、新的迭代器(iterators)和 for/of 循环(loops)、Python 风格的生成器(generators)和生成器表达式、arrow functions, binary data, typed arrays, collections (maps, sets and weak maps), promises, number and math enhancements, reflection, and proxies … 更多特性详见这里。 |
7 | 制定中 |
各个浏览器对 ECMAScript 5 的兼容性可查看 这里 。
文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的 API。借助 DOM 提供的 API,开发人员可以轻松自如地删除、添加、替换或修改任何节点,获得控制页面内容和结构的主动权。
浏览器对象模型(BOM,Browser Object Model)是一组浏览器提供的自定义扩展 API,可以控制浏览器显示的页面以外的部分,例如:
常用的 BOM API 如下:
1 | window |
由于没有 BOM 标准可以遵循,因此每个浏览器都有自己的实现。虽然也存在一些事实标准,例如要有 window 对象和 navigator 对象等,但每个浏览器都会为这两个对象乃至其它对象定义自己的属性和方法。如今 HTML 5 致力于把很多 BOM 功能纳入正式规范,BOM 实现的细节有望朝着兼容性越来越高的方向发展。
从 Windows 转到 Linux 的初学者,往往对 Linux 的目录结构感到无所适从。本文介绍的 FHS 标准,是理解这些目录结构的关键。
FHS(Filesystem Hierarchy Standard,文件系统层次结构标准)采用树形结构组织文件,并定义了 Linux 系统中主要目录的用途、所需要的最小构成的文件和目录,同时还给出了例外处理与矛盾处理。多数 Linux 版本采用这种目录组织形式,类似于 Windows 操作系统中 C 盘的文件目录。
事实上,FHS 针对目录树结构仅定义出两层目录(/
及 /usr
、/var
)底下应该放置什么数据,下面分别介绍这些目录:
在 FHS 标准中,所有的文件和目录都必须出现在根目录 /
下,即使它们存储在不同的存储设备或网络主机中。此外还要求根目录 /
下必须要有以下目录或符号链接(symbolic links):
目录 | 描述 | 备注 |
---|---|---|
/etc |
Host-specific system configuration | 系统配置文件 |
/dev |
Device files | 设备文件 |
/bin |
Essential command binaries (for use by all users) | 重要的执行文件 |
/sbin |
Essential system binaries (for use by root) | 重要的系统执行文件 |
/lib |
Essential shared libraries and kernel modules | 执行文件所需的函数库与内核所需的模块。/bin 和 /sbin 中二进制文件必要的函数库 |
/boot |
Static files of the boot loader (include kenerl file、drivers) | 系统开机文件 |
/media |
Mount point for removeable media | |
/mnt |
Mount point for mounting a filesystem temporarily (include hard disk、U disk、CD、DVD…) | |
/opt |
Add-on application software packages | |
/srv |
Data for services provided by this system | |
/tmp |
Temporary files | 临时文件 |
注意:
/
与开机、还原、系统修复等操作有关,而开机过程中仅有根目录会被挂载,其它分区则是在开机完成之后才会持续进行挂载,因此,根目录下与开机过程有关的目录(即上表前六个目录)不能够与根目录分开到不同分区。该目录与软件安装/执行有关。
/usr
下,因此这个目录有点类似 Windows 系统的 C:\Windows\
和 C:\Program files\
这两个目录的综合体,系统刚安装完毕时,这个目录会占用最多的硬盘容量。/usr
下。该目录与系统运作过程有关。
如果 /usr
是系统安装时会占用较大硬盘容量的目录,那么 /var
则是在系统运行时才会渐渐占用硬盘容量的目录。 /var
目录主要针对常态性变动的文件,包括缓存(cache)、登录文件(log file)以及某些软件运行所产生的日志文件,因此这个目录会越来越大,建议单独挂载分区。
由于根目录所在分区的容量有限,因此像 /usr
、/var
、/home
这种大目录最好不要与根目录放在同一个分区内,而是建议单独挂载分区。如此一来不但可以提高系统性能,根目录所在的文件系统也不容易发生问题。
续上文。
GNU/CoreUtils 的 Text utilities 提供了一些便利的文本处理命令,配合“管道”组合使用可以大大提高文本处理效率。
下面介绍一些最常用的利用管道进行组合的命令:
grep
命令使用正则表达式以行为单位进行文本搜索(global search regular expression(RE) and print out the line),其命令格式如下:
1 | grep [选项] 'PATTERN' [文本文件] |
常用选项:
选项 | 描述 | 例子 |
---|---|---|
-c, --count |
打印匹配的行数 | |
-m NUM, --max-count=NUM |
打印前 NUM 行 | |
-n, --line-number |
打印行号 | |
-v, --revert-match |
反转查找(非) | |
-A , --after |
可加数字,表示打印后面n行 | |
-B , --before |
可加数字,表示打印前面n行 | |
-i |
不区分大小写 | |
-r |
递归目录查找 | grep -r "XXX" * |
--color |
关键字高亮 | |
-E |
使用正则表达式,替代 egrep 命令 |
|
-F |
在一个或多个文件中搜索固定的字符串,替代 fgrep 命令 |
且(AND):
1 | # 使用管道符连接多个 grep 命令 |
或(OR):
1 | # 方法一:使用转义字符 \| |
非(NOT):
1 | grep -v 'pattern' filename |
参考:
https://stackoverflow.com/questions/9969414/always-include-first-line-in-grep
tr
命令用于替换或删除指定的字符(注意不接收文件参数),其命令格式如下:
1 | tr [options] string1 string2 |
可用于将小写转换成大写:
1 | $ echo 'abcdef' | tr 'a-z' 'A-Z' |
-d
参数可用于删除指定的字符:
1 | $ echo 'abcdef' | tr -d 'def' |
-s
参数可用于删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串:
1 | $ echo 'abbbbbbbbbc' | tr -s 'b' |
-d
和 -s
常用于删除所有换行符 \n
和合并空格 [:space:]
:
1 | $ cat logfile | tr -d '\n\t' | tr -s [:space:] |
cut
命令以行为单位,用于截取某段数据,如字节、字符和字段。其命令格式如下:
1 | cut [选项] [范围] [文本文件] |
使用 -d
指定分隔符(默认为制表符),例如:cut -d ':' -f -2 /etc/passwd
。
常用的几种选项如下:
选项 | 描述 |
---|---|
-f, --fields |
以字段为单位 |
-c, --characters |
以字符为单位 |
-b, -- bytes |
以字节为单位 |
常用的几种范围如下:
选项 | 描述 |
---|---|
n |
第 n 个 |
n- |
从第 n 个到最后一个 |
n-m |
从第 n 个到第 m 个 |
-m |
从第一个到第 m 个 |
- |
从第一个到最后一个 |
n,m |
第 n、m 个 |
注意,在 UTF-8 编码下,汉字占三个字节。
sort
命令以行为单位,用于对文本文件内容进行排序。其命令格式如下:
1 | sort [选项] [文本文件] |
常用的选项如下:
选项 | 描述 |
---|---|
-n, --numeric-sort, --sort=numeric |
依照数值的大小排序(默认是以文字) |
-r, --reverse |
反向排序 |
uniq
命令以行为单位,用于合并文本文件中重复出现的行列。它比较相邻的行,在正常情况下,第二个及以后更多个重复行将被删去,因此在合并前常常会先使用 sort
命令排序。行比较是根据所用字符集的排序序列进行的。其命令格式如下:
1 | uniq [选项] [文本文件] |
常用的选项如下:
选项 | 描述 |
---|---|
-i, --ignore-case |
Case insensitive comparison of lines. 忽略大小写 |
-c, --count |
Precede each output line with the count of the number of times the line occurred in the input, followed by a single space. 进行计数 |
-d, --repeated |
Output a single copy of each line that is repeated in the input. 只显示重复行(交集) |
-u, --unique |
Only output lines that are not repeated in the input. 只显示不重复的行(差集) |
例子:
1 | cat a b | sort | uniq > c # c is a union b |
wc
命令用于统计字节数、字数、行数,其命令格式如下:
1 | wc [选项] [文本文件] |
常用的选项如下:
选项 | 描述 |
---|---|
-l, --lines |
只显示行数 |
-w, --words |
只显示字数 |
-c, --chars 或 --bytes |
只显示字节数 |
tee
是一种双向重定向命令,用于可以将数据流处理过程中的某段结果保存到文件,其处理过程如下:
常用的选项如下:
选项 | 描述 |
---|---|
-a, --append |
附加到既有文件的后面,而非覆盖它 |
-i, --ignore-interrupts |
忽略中断信号 |
1、统计 Nginx 独立 IP 数:
1 | $ cut -d " " -f 1 nginx_log | sort | uniq | wc –l |
2、统计当前用户最常用的 10 条命令:
1 | $ cut -d " " -f 1 ~/.bash_history | sort | uniq -c | sort -nr | head |
3、统计重复行,逆序方式:
1 | $ sort /data/tradehistory_20150804.txt | uniq -cd | sort -nr |
4、统计多个文件:
1 | $ cat /data/tradehistory_2015080*.txt | cut -d ',' -f 13 | sort | uniq -c | sort -nr |