JavaScript 中的一些有形无形的坑

前言

可以说 JavaScript 发展到现在已经出人投地了,并且我相信未来 JavaScript 会超乎我们的想象,这只是时间问题。不过话又说回来,对于天天敲 JavaScript 的开发人员来说,我们最关注的还是如何高效地使用 JavaScript。为此本文就来得理直气壮了,下面我就把一些自己知道的,见过的关于在使用 JavaScript 时的一些坑整理分享出来。

1、慎用 isNaN()

我们来欣赏下面的几个小例子就你就知道为什么了

console.log(isNaN({})); // true
console.log(isNaN("abc")); // true
console.log(isNaN(undefined)); // true

惊呆了有没有,除了 NaN 外,上面的这些值(包括但不限于)返回 true

而数组则有所不同

console.log(isNaN([])); // false

这跟 isNaN() 方法的判断原理有关,isNaN() 会尝试把参数转为数值,如果转换不成功,则会认为参数是 NaN,而数组(空数组或者长度为 1 的数组)是可以转为数值的,其它情况就不会转成 NaN。

2、奇葩的 ==

console.log([] == 0);  // true
console.log([0] == 0);  // true
console.log(["1"] == 1);  // true
console.log(["\n"] == 0);  // true
console.log("" == 0);  // true

原因是左边的值最终会被 Number() 方法强制转换成对应的数字。所以我们需要知道  == 能不能满足实际场景。不然这个小坑可能会耗费你不少时间。

3、swith() 有脾气

var x = "1";
switch(x){
    case 1:
    console.log("我是数字1");
    case "1":
    console.log("我是字符串1");
}
// 结果:我是字符串1

switch 中的 case 判断是严格相等(===),而不是我们熟知的 ==。也就是说在 switch 的 case 条件判断不会作类型转换的,都是原汁原味地进行比较。

4、连接符 || && 

一般在条件判断(比如:if 语句)极其常见,可以说是孪生兄弟了。但在使用它们俩的时候需要注意下他们的优先级和返回值。关于优先级网上都说 && 的优先级比 || 高,但我始终想不到一个例子来证明这个结论,反而让自己长见识了:

const a = "hellow";
const b = 0;

if(a || obj.a && obj.b){
    console.log("进来了,这算不是意外惊喜!");
}
// 结果打印:进来了,这算不是意外惊喜!

如果按前人的结论应该会给我报错才对,因为 && 的优先级更高会先执行,而 obj 并未定义,取 obj 中的属性肯定是会报错的,但是结果却让我不知所措。

注意:|| 和 && 比较会返回对应的变量值,而不是一个 true 或者 false 的布尔值,这一点可能很多人都觉得是布尔值。

console.log(a || b); // hellow

5、函数默认值

function func (x=88, y=x+66) {
  console.log(x, y);
  console.log(arguments.length, arguments[0], arguments[1]);
}

func();
// 88,154
// 0,undefined,undefined

func("");
// "","66"
// 1,"",undefined

func(1);
// 1,67
// 1,1,undefined

func(undefined, 0);
// 88,0
// 2,undefined,0

func(null);
// null,66
// 1,null,undefined

func(NaN);
// NaN,NaN
// 1,NaN,undefined

如果我们有给函数预置默认值,那么我们需要知道:

1、只有当参数为 undefined 或者没有给函数传参时才会起作用。

2、如果传的是 null 那么 null 会被转为数字 0

3、arguments 不会指向默认值,也就是说如果函数没传参数,那么 arguments 就不会有值

所以如果给函数预设了默认值,我们就得非常地小心,尽量不要混用参数的引用。

6、this 指向

const obj1 = {
    a:"hello",
    func:function(){
        console.log(this.a);
    }
}

const obj2 = {
    a:"world",
    func:obj1.func
}

obj2.func(); // world

是不是很惊讶,为什么打印的不是 obj1.a 的值?虽然这个不很常见,但我们也有可能会遇到,我们只需要记住一点就好,谁调用函数函数中的 this 就会指向谁。

7、注意 DOM 中的 id 属性

如果我们给 html 元素添加了 id 属性,那么此时就得打起十二分精神了,因为 id 的值会变成一个全局变量,并且这个全局变量会指向元素本身。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="a"></div>
    <script>
        console.log(a); // <div id="a"></div>
    </script>
</body>
</html>

8、JS 引入

我们使用 JS 有两种引入方式,第一种直接在 HTML 文件中添加 <script> 标签,第二各就是通过引入外部文件。但不管哪一种,它们的效果都是一样的。但这里有一些地方需要注意,比如:一个页面添加了两个 <script>标签块,或者引入了两个 JS 文件,<script> 代码块或者 JS 文件代码块之间不是你想像的那样它们只是一个的整体那么简单,国为它们又相对封闭,比如:变量的提升不会跨 <script> 或者 JS 文件。 

如果先声明后使用,那就没什么问题

<script>
    function func() {
        console.log("hello");
    }
</script>
<script>
    func(); // hello
</script>

如果先使用,再声明,那么出问题了

<script>
    func(); // Uncaught ReferenceError: func is not defined
</script>
<script>
    function func() {
        console.log("hello");
    }
</script>