你编,或者不编程,项目就在那里,还未完成。你调,或者不调试,BUG就在那里,早晚得改。你踩,或者不踩,坑都在那里,等下一个受害者。
变量作用域
未使用var关键字定义的变量都是全局变量
在JavaScript中定义变量时漏掉var并不会报错。
1 | function foo() { |
没有块作用域,只有函数作用域
1 | function foo() { |
上面的代码中可以看出,for循环外依然能访问变量i和value,但函数foo外面则无法访问。
如何优雅地绕过此坑:
- 在函数体的顶部声明可能用到的变量
- 函数体内不要使用同一名称命名不同意义的变量
变量的优先级
下面的代码会让人觉得“匪夷所思”
1 | var x = 1; |
WHY?
JavaScript 是解释型语言,但它并不是直接逐步执行的,JavaScript解析过程分为先后两个阶段,一个是预处理阶段,另外一个就是执行阶段。在预处理阶段 JavaScript解释器将完成把JavaScript脚本代码转换到字节码,然后第二阶段JavaScript解释器借助执行环境把字节码生成机械 码,并顺序执行。
JavaScript的变量作用域是基于其特有的作用域链的,使用变量时JavaScript是按从下往上的顺序依次查找变量,没有找到就进入上一级直至全局变量。基于这一规则,函数体内部局部变量的优先级比同名的全局变量高。另外,函数中声明的变量在函数体内都可以使用,并可以重新赋值。
因此,上面的代码同于如下代码:
1 | var x = 1; |
this的指向
在函数执行时,this 总是指向调用该函数的对象。要判断 this 的指向,其实就是判断 this 所在的函数属于谁。
在《javaScript语言精粹》这本书中,把 this 出现的场景分为四类,简单的说就是:
- 有对象就指向调用对象
- 没调用对象就指向全局对象
- 用new构造就指向新对象
- 通过 apply 或 call 或 bind 来改变 this 的所指。
看下面的例子:
1 | var myObject = { |
闭包
闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数。只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数,而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。
1 | <button>0</button> |
乍一看,上面的代码应该是点击不同按钮会输出不同的内容,但发现点击四个button都是输出“This is element # 4”,很奇怪,为什么?
理一下执行过程:
当i的值为4的时,for循环执行完毕,但是因为每个button的onclick方法这时候为内部函数,所以i被闭包引用,内存不能被销毁,i的值会一直保持4,直到程序改变它或者所有的onclick函数销毁(主动把函数赋为null或者页面卸载)时才会被回收。这样每次我们点击button的时候,onclick函数会查找i的值(作用域链是引用方式),一查等于4,然后就输出了“This is element # 4”。
解决:
1 | var elements = document.getElementsByTagName('button'); |
第二种方式是使用了一个立即执行的函数又创建了一层闭包,函数声明放在括号内就变成了表达式,后面再加上括号括号就是调用了,这时候把i当参数传入,函数立即执行,num保存每次i的值(依次是0、1、2、3),我们点击button的时候,就会得到我们想要的效果。
行尾的分号
Javascript代码中分号是可选的,似乎很方便,但是很不幸的是经常会遇到因为漏掉了分号出现的错误,而解释器有时对这种错误定位不明确,必须由我们自己追溯并尝试去猜测因为哪些分号漏写导致的问题。
1 | function foo1() { |
foo2中分号自动加到了return语句后面,所以得到了“奇怪”的结果。
操作符
双等号
==操作符比较时会进行类型的强制转换,它可以比较两个不同类型的对象,在执行比较之前它将会尝试把这两个对象转换成同一个类型。
1 | console.log("" == 0); //true - 空字符串会被强制转换为数字0. |
如果使用三重等号(===),上面的三个比较都将返回false。
加减号
1 | console.log(typeof (1 + "1"), 1 + "1"); // string 11 +号的操作数是数字与字符串,执行连接操作 |
数字类型
浮点数运算
看一个例子:
1 | console.log(0.1 + 0.2 === 0.3); // false |
惊天BUG?
JavaScript只有一种数字类型Number,没有Integer和Float,Number是IEEE标准中双精度浮点运算(64位)类型。 浮点数的精度问题不是JavaScript特有的,因为有些小数以二进制表示位数是无穷的,这就意味着JavaScript中浮点数运算会有无法避免的精度丢失。
1 | console.log(0.1 + 0.2 === 0.3); // false |
解决:
在判断浮点运算结果前对计算结果进行精度缩小,因为在精度缩小的过程总会自动四舍五入:
1 | var result = (0.1 + 0.2).toFixed(1); |
NaN
NaN的类型是Number,NaN和任何东西比较都是false。
1 | console.log(typeof NaN); // number |
还有很多坑没记上来,还有很多坑未踩过去
END!!!