关于阅读书籍部分,是个人在本书籍中收集的精华部分和实战部分,为了后续再次阅读节省时间
和方便在工作中的应用,后续会分享本书籍的电子版pdf在线下载。不过我还是建议读着去阅读原著。
--
上卷
提升
a = 2;
var a;
console.log( a );
你认为 console.log(..)
声明会输出什么呢?
很多开发者会认为是 undefined
,因为 var a 声明在 a = 2 之后,他们自然而然地认为变量
被重新赋值了,因此会被赋予默认值 undefined。但是,真正的输出结果是 2
。
考虑另外一段代码:
console.log( a );
var a = 2;
鉴于上一个代码片段所表现出来的某种非自上而下的行为特点,你可能会认为这个代码片
段也会有同样的行为而输出 2。还有人可能会认为,由于变量 a 在使用前没有先进行声明,
因此会抛出ReferenceError
异常。
不幸的是两种猜测都是不对的。输出来的会是 undefined
。
编译器再度来袭
当你看到 var a = 2; 时,可能会认为这是一个声明。但 JavaScript 实际上会将其看成两个
声明:var a; 和 a = 2;。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在
原地等待执行阶段。
我们的第一个代码片段会以如下形式进行处理:
var a;
a = 2;
console.log( a );
其中第一部分是编译,而第二部分是执行。
类似地,我们的第二个代码片段实际是按照以下流程处理的:
var a;
console.log( a );
a = 2;
只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。如果提升改变
了代码执行的顺序,会造成非常严重的破坏。
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
foo
函数的声明(这个例子还包括实际函数的隐含值)被提升了,因此第一行中的调用可
以正常执行。
另外值得注意的是,每个作用域都会进行提升操作。尽管前面大部分的代码片段已经简化
了(因为它们只包含全局作用域),而我们正在讨论的 foo(..) 函数自身也会在内部对 var
a 进行提升(显然并不是提升到了整个程序的最上方)。因此这段代码实际上会被理解为下
面的形式:
function foo() {
var a;
console.log( a ); // undefined
a = 2;
}
foo();
可以看到,函数声明会被提升,但是函数表达式却不会被提升。
foo(); // 不是 ReferenceError, 而是 TypeError!
var foo = function bar() {
// ...
};
中卷
较小的值
0.1+0.2===0.3; // false
在处理小数的数字时特别需要注意
那么应该怎样来判断 0.1 + 0.2 和 0.3 是否相等呢?
最常见的方法是设置一个误差范围值,通常称为“机器精度”(machine epsilon
),对
JavaScript 的数字来说,这个值通常是 2^-52 (2.220446049250313e-16)
。
从 ES6 开始,该值定义在·Number.EPSILON·
中,我们可以直接拿来用,也可以为 ES6 之前
的版本写 polyfill:
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
// 可以使用 Number.EPSILON 来比较两个数字是否相等(在指定的误差范围内):
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
能够呈现的最大浮点数大约是 1.798e+308(这是一个相当大的数字),它定义在Number.
MAX_VALUE
中。最小浮点数定义在 Number.MIN_VALUE 中,大约是 5e-324,它不是负数,但
无限接近于 0 !
整数的安全范围
数字的呈现方式决定了“整数”的安全值范围远远小于Number.MAX_VALUE
。
能够被“安全”呈现的最大整数是 2^53 - 1,即 9007199254740991,在 ES6 中被定义为Number.MAX_SAFE_INTEGER
。最小整数是 -9007199254740991,在 ES6 中被定义为 Number.
MIN_SAFE_INTEGER
。
void 运算符
undefined
是一个内置标识符(除非被重新定义,见前面的介绍),它的值为 undefined,
通过 void
运算符即可得到该值。
表达式 void ___ 没有返回值,因此返回结果是 undefined。void 并不改变表达式的结果,
只是让表达式不返回值:
var a = 42;
console.log( void a, a ); // undefined 42
按惯例我们用 void 0 来获得 undefined(这主要源自 C 语言,当然使用 void true 或其他
void 表达式也是可以的)。void 0
、void 1
和 undefined
之间并没有实质上的区别。
上下文规则
JavaScript 通过标签跳转能够实现 goto 的部分功能。continue
和 break
语句都可以带
一个标签,因此能够像 goto 那样进行跳转。例如:
// 标签为foo的循环
foo: for (var i=0; i<4; i++) {
for (var j=0; j<4; j++) {
// 如果j和i相等,继续外层循环
if (j == i) {
// 跳转到foo的下一个循环
continue foo;
}
// 跳过奇数结果
if ((j * i) % 2 == 1) {
// 继续内层循环(没有标签的)
continue;
}
console.log( i, j );
}
}
// 1 0
// 2 0
// 2 1
// 3 0
// 3 2
带标签的循环跳转一个更大的用处在于,和 break
__ 一起使用可以实现从内层循环跳转到
外层循环。没有它们的话实现起来有时会非常麻烦:
// 标签为foo的循环
foo: for (var i=0; i<4; i++) {
for (var j=0; j<4; j++) {
if ((i * j) >= 3) {
console.log( "stopping!", i, j );
break foo;
}
console.log( i, j );
}
}
// 0 0
// 0 1
// 0 2
// 0 3
// 1 0
// 1 1
// 1 2
// 停止! 1 3
全局 DOM 变量
你可能已经知道,声明一个全局变量(使用 var 或者不使用)的结果并不仅仅是创建一个
全局变量,而且还会在 global 对象(在浏览器中为 window)中创建一个同名属性。
还有一个不太为人所知的事实是:由于浏览器演进的历史遗留问题,在创建带有 id 属性
的 DOM 元素时也会创建同名的全局变量。例如:
<div id="foo"></div>
// 以及:
if (typeof foo == "undefined") {
foo = 42; // 永远也不会运行
}
console.log( foo ); // HTML元素
你可能认为只有 JavaScript 代码才能创建全局变量,并且习惯使用 typeof 或 .. in window
来检测全局变量。但是如上例所示,HTML 页面中的内容也会产生全局变量,并且稍不注
意就很容易让全局变量检查错误百出。
这也是尽量不要使用全局变量的一个原因。如果确实要用,也要确保变量名的唯一性,从
而避免与其他地方的变量产生冲突,包括 HTML 和其他第三方代码。
--
作者:刘伟波
链接:http://www.liuweibo.cn/p/233
来源:刘伟波博客
本文原创版权属于刘伟波 ,转载请注明出处,谢谢合作
发表评论:
测试
谁这么调皮,