《JavaScript Good Parts》附录书摘
第十章:Beautiful Features
提炼JavaScript的子集,这样我就不用解析整个语言,并且也就不需要描述整个语言了,我把这个语言叫做精简的JavaScript(Simplified JavaScript). 精简的JavaScript中都是好东西,包括一下主要的内容
- 函数时顶级对象
- 在精简的JavaScript中,函数是有语法作用域的闭包(lambda);
- 基于原型继承的动态对象
- 对象是无类型的,我们可以通过普通的赋值给如何对象添加一个新的成员属性.一个对象可以从另一个对象继承成员属性
- 对象字面量和数组字面量
- 这对创建新的对象和数组来说是一种非常方便的表示法,JavaScript字面量是数据交换格式JSON的灵感来源
附录A:Awful Parts
在本附录中,我会展示JavaScript的一些难以避免的问题特性,你必须知道这些问题并准备好对应的措施.
全局变量 Global Variables
- 在JavaScript所有的糟糕特性之中,最为糟糕的一个就是它对全局变量的依赖.全局变量就是在所有作用域中都可见的变量.全局变量就是在所有作用域中都可见的变量.全局变量在小型程序中可能会带来方便.因为一个全局变量可以在程序的任何部分在任何时间修改,它们使得程序的行为变的极度复杂.在程序中使用全局变量降低了程序的可靠性.
- 全局变量使得在同一个程序中运行独立的子程序变得更难.如果某些全局变量的名称碰巧和子程序中的变量名相同.那么它们会互相冲突,可能导致程序无法运行,而且通常难以调试.
作用域 Scope
JavaScript的语法来源于C.在所有其他类似C语言风格的语言里,一个代码块会创造一个作用域.代码块中声明的变量在其外部是不可见的.JavaScript采用了这样的块语法,却没有提供块级作用域:代码块声明的变量在包含次代码块的任何位置都是可见的.
在大多数语言中,一般来说,声明变量的最好地方是第一次用到它的地方.但这种做法在JavaScript中反而是一个坏习惯,因为它没有块级作用域.更好的方式是在每个函数的开头部分声明所有变量.
自动插入分号 Semicolon Insertion
JavaScript有一个自动修复机制.它试图通过自动插入分号来修正有缺陷的程序.但是,千万不要指望它,它可能会掩盖更为严重的错误.
有时它会不合时宜的插入分号.请考虑在return语句中自动插入分号而导致的后果.如果在一个return语句返回一个值.这个值表达式的开始部分必须和return在同一行.
1
2
3
4return
{
status:true
};这看起来是要返回一个包含status成员元素的对象.遗憾的是,自动插入分号让它成为了返回undefined.自动插入分号导致程序被误解,却没有任何警告提醒.如果把( 放在上一行的尾部而不是下一行的头部可以避免该问题.
保留字 Reserved Words
- 下面的单词在JavaScript中被保留
- abstract boolean break byte case catch char class const continue debugger default
- delete do double else enm export extends false final finally float for function goto
- if implements import in instanceof int interface long native new null package private
- protected public return short static super switch synchronized this throw throws
- transient true try typeof var volatile void while with
- 这些单词的大多数并没有在语言中使用
- 它们不能被用来命名变量或函数,当保留字被用做对象字面量的键值时,它们必须被引号括起来.它们不能被用在点表示法中,所以有时必须使用括号表示法:
- var method; //ok
- var class; //非法
- object ={boc:value}; //ok
- object = {case: value}; //非法
- object = {‘case’ :value}; // ok
- object.box = value; //ok
- object.case = value; //非法
- object[‘case’] = value; //ok
typeof
- typeof运算符返回一个用于识别其运算数类型的字符串所以: typeof 98.7 ,返回’number’,遗憾的是 typeof null返回’object’而不是’null’,这太糟糕了.其实有更简单也更好的检查null的方式: my_value === null
parseInt
- parseInt是一个把字符串转为整数的函数.它在遇到非数字会停止解析.所有parseInt(“16”)与parseInt(“16 tons”)产生相同的结果.但是该函数会提醒我们出现了额外额文本就好了,但它不会那么做.
- 那么该字符串第一个字符是0,那么该字符串会基于八进制而不是十进制来求值,在八进制中,8和9不是数字,所以parseInt(“08”)和parseInt(“09”)都产生0转为结果,这个错误会导致程序解析日期和时间时出现问题.幸运的是,parseInt可以接受一个基数转为参考,如此一来parseInt(“08”,10)结果为8,我建议你总是加上一个基数参数
+
- +运算符可以用于加法运算或字符串连接.它究竟会如何执行取决于其参数的类型.如果其中一个字符串是空字符串,它会把另一个运算符转换成字符串并返回.如果两个运算符都是数字,它返回两者之合.否则,它把两个运算符都转为字符串并连接起来.这个复杂的行为是bug的常见来源.如果你要使用+去做加法运算.请保证两个运算数都是整数.
浮点数 Floating Point
- 二进制的浮点数不能正确的处理十进制的小数,因此0.1+0.2不等于0.3.这是JavaScript中最经常被报告的bug,并且它是遵循二进制浮点数算术标准(IEEE 754)而有意导致的结果.但是它违背了大多数你在中学中学到的关于数学的知识.幸运的是,浮点数中的整数运算是精准的,所以小数表现出来的错误可以通过指定精度来避免
NaN
- NaN是IEEE中定义的一个特殊的数量值.它表示的不是一个数字,尽管下面的表达式返回的是true: typeof NaN === ‘number’//true
- 如果NaN是属性运算中的一个运算数,那么结果就是NaN.所以,如果你有一个公式链产生了NaN的结果,那肯定是其中某一个输入项是NaN,要么在某个地方产生了NaN
- 你可以对NaN进行检测,typeof不能识别数字和NaN,而且NaN不等于它字节.所以下面的代码结果让人惊讶:
- NaN === NaN //false
- NaN != NaN //true
- ps:此处黑人问号
- javaScript中提供了一个isNaN函数,可以分辨数字和NaN
伪数组 Phony Arrays
- JavaScript没有真正的数组,这也不全是坏事.JavaScript的数字确实非常容易使用,你不必给它们设置维度,而且它们永远不会产色越界错误,但是它们的性能相比真正的数组可能相当糟糕.
- typeof运算符不能辨别数组和对象.要判断一个值是否为数组,你还需要检查它的constructor属性
- if (my_value && typeof my_value === ‘object” && my_value.constructor === Array)
假值 Falsy Values
- JavaScript拥有一组数量齐大的假值
值 | 类型 |
---|---|
0 | number |
NaN(非数字) | number |
“”(空字符串) | String |
false | Boolean |
null | Object |
undefined | Undefined |
- 这些值全部都等同于假,但是它们是不可互换的
Object
- JavaScript的对象永远不会是真的空对象.因为它们可以从原型链中取得成员属性
《JavaScript Good Parts》附录书摘