loading...

0

你不知道的javacript

阅读书籍读完大概需要14分钟

  • 发布时间:2018-11-08 11:21 星期四
  • 刘伟波
  • 308
  • 更新于2018-11-14 14:25 星期三

关于阅读书籍部分,是个人在本书籍中收集的精华部分和实战部分,为了后续再次阅读节省时间
和方便在工作中的应用,后续会分享本书籍的电子版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 ___ 没有返回值,因此返回结果是 undefinedvoid 并不改变表达式的结果,
只是让表达式不返回值

var a = 42;
console.log( void a, a ); // undefined 42

按惯例我们用 void 0 来获得 undefined(这主要源自 C 语言,当然使用 void true 或其他
void 表达式也是可以的)。void 0void 1undefined 之间并没有实质上的区别。

上下文规则

JavaScript 通过标签跳转能够实现 goto 的部分功能。continuebreak 语句都可以带
一个标签,因此能够像 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

来源:刘伟波博客

本文原创版权属于刘伟波 ,转载请注明出处,谢谢合作

你可能感兴趣的文章

    发表评论

    评论支持markdown,评论内容不能超过500字符,如果内容过多或者要及时回复,建议去 segmentfault平台, 也可以来我的直播间来提问。
    关于技术问题或者有啥不懂的都可以留言, 我会定期回复答疑, 也可以来 我的直播间 提问, 推荐最新仓库 前端知识体系, 感謝支持!
    刘伟波作者说道:

    测试

    • 谁这么调皮,

      刘伟波1232019-01-18 17:59 星期五