csjiabin's blog csjiabin's blog
首页
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
  • 学习笔记

    • 《面向Node.js开发者的Go》
更多
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

csjiabin

前端界的小菜鸟
首页
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
  • 学习笔记

    • 《面向Node.js开发者的Go》
更多
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《React》笔记
    • 《TypeScript 从零实现 axios》
    • TypeScript笔记
    • 小程序笔记
    • JS设计模式总结笔记
    • 用图标和示例解释Await和Async
    • ES6的Set和Map,以及WeakSet和WeakMap
    • JS中的内存管理
    • JavaScript 易错知识点整理
      • JavaScript有用的代码片段和trick
      • js数组详细操作方法及解析合集
    • 前端
    • 学习笔记
    csjiabin
    2018-10-18
    目录

    JavaScript 易错知识点整理

    # 变量作用域

    var a = 1;
    function test() {
      var a = 2;
      console.log(a); // 2
    }
    test();
    
    1
    2
    3
    4
    5
    6

    上面的 test 函数作用域中声明并赋值了 a,且在 console 之上,所以遵循就近原则输出 a 等于 2。

    var a = 1;
    function test2() {
      console.log(a); // undefined
      var a = 2;
    }
    test2();
    
    1
    2
    3
    4
    5
    6

    上方的函数作用域中虽然声明并赋值了 a,但位于 console 之下,a 变量被提升,输出时已声明但尚未被赋值,所以输出 undefined。

    var a = 1;
    function test3() {
      console.log(a); // 1
      a = 2;
    }
    test3();
    
    1
    2
    3
    4
    5
    6

    上方的函数作用域中 a 被重新赋值,未被重新声明,且位于 console 之下,所以输出全局作用域中的 a。

    let b = 1;
    function test4() {
      console.log(b); // b is not undefined
      let b = 2;
    }
    test4();
    
    1
    2
    3
    4
    5
    6

    上方的函数作用域中使用了 ES6 的 let 重新声明了变量 b,而 let 不同于 var 其不存在变量提升的功能,所以输出报错b is not undefined

    function test5() {
      let a = 1;
      {
        let a = 2;
      }
      console.log(a); // 1
    }
    test5();
    
    1
    2
    3
    4
    5
    6
    7
    8

    上方的函数作用域中用 let 声明了 a 为 1,并在块级作用域中声明了 a 为 2,因为 console 并不在函数内的块级作用域中,所以输出 1。

    # 类型比较

    var arr = [],
      arr2 = [1];
    
    console.log(arr === arr2); // false
    
    1
    2
    3
    4

    上方两个不同的数组比较,console 为 false。

    var arr = [],
      arr2 = [];
    
    console.log(arr === arr2); // false
    
    1
    2
    3
    4

    上方两个相同的数组比较,因为两个单独的数组永不相等,所以 console 为 false。

    var arr = [],
      obj = {};
    
    console.log(typeof arr === typeof obj); // true
    
    1
    2
    3
    4

    上方利用 typeof 比较数组和对象,因为 typeof 获取null、数组、对象的类型都为object,所以 console 为 true。

    var arr = [];
    console.log(arr instanceof Object); // true
    console.log(arr instanceof Array); // true
    
    1
    2
    3

    上方利用 instanceof 判断一个变量是否属于某个对象的实例,因为在 JavaScript 中数组也是对象的一种,所以两个 console 都为 true。

    # this 指向

    var obj = {
      name: "xiaoming",
      getName: function () {
        return this.name;
      },
    };
    console.log(obj.getName()); // xiaoming
    
    1
    2
    3
    4
    5
    6
    7

    上方对象方法中的 this 指向对象本身,所以输出 xiaoming

    var obj = {
      name: "xiaoming",
      getName: function () {
        return this.name;
      },
    };
    var nameFn = obj.getName;
    console.log(nameFn()); // undefined
    
    1
    2
    3
    4
    5
    6
    7
    8

    上方将对象中的方法赋值给了一个变量,此时方法中的 this 也将不再指向 obj 对象,从而指向 window 对象,所以 console 为 undefined。

    var obj = {
      name: "xiaoming",
      getName: function () {
        return this.name;
      },
    };
    
    var obj2 = {
      name: "xiaohua",
    };
    
    var nameFn = obj.getName;
    console.log(nameFn.apply(obj2)); // xiaohua
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    上方同样将 obj 对象中的方法赋值给了变量 nameFn,但是通过 apply 方法将 this 指向了 obj2 对象,所以最终 console 为 xiaohua。

    # 函数参数

    function test6() {
      console.log(Array.prototype.slice.call(arguments)); // [1,2]
    }
    test6(1, 2);
    
    1
    2
    3
    4

    上方利用函数中的 arguments 类数组对象获取传入函数的参数数组,所以输出数组[1, 2]。

    function test7() {
      return function () {
        console.log(Array.prototype.slice.call(arguments)); // 未执行到此,无输出
      };
    }
    test7(1, 2);
    
    1
    2
    3
    4
    5
    6

    上方同样利用 arguments 获取参数,但因 test7(1, 2)未执行 return 中的函数,所以无输出。若执行 test7(1, 2)(3, 4)则会输出[3, 4]。

    var args = [1, 2];
    function test8() {
      console.log(Array.prototype.slice.call(arguments)); // [1,2,3,4]
    }
    Array.prototype.push.call(args,3, 4));
    test8(...args);
    
    1
    2
    3
    4
    5
    6

    上方利用 Array.prototype.push.call()方法向 args 数组中插入了 3 和 4,并利用 ES6 延展操作符(…)将数组展开并传入 test9,所以 console 为[1, 2, 3, 4]。

    # 闭包问题

    var elem = document.getElementsByTagName("div"); // 如果页面上有5个div
    for (var i = 0; i < elem.length; i++) {
      elem[i].onclick = function () {
        alert(i); //总是5
      };
    }
    
    1
    2
    3
    4
    5
    6

    上方是一个很常见闭包问题,点击任何 div 弹出的值总是 5,因为当你触发点击事件的时候 i 的值早已是 5,可以用下面方式解决:

    var elem = document.getElementsByTagName("div"); // 如果页面上有5个div
    for (var i = 0; i < elem.length; i++) {
      (function (j) {
        elem[j].onclick = function () {
          alert(j); // 依次为 0,1,2,3,4
        };
      })(i);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    在绑定点击事件外部封装一个立即执行函数,并将 i 传入该函数即可。

    # 对象拷贝与赋值

    var obj = {
      name: "xiaoming",
      age: 23,
    };
    var newObj = obj;
    newObj.name = "xiaohua";
    console.log(obj.name); // xiaohua
    console.log(newObj.name); // xiaohua
    
    1
    2
    3
    4
    5
    6
    7
    8

    上方我们将 obj 对象赋值给了 newObj 对象,从而改变 newObj 的 name 属性,但是 obj 对象的 name 属性也被篡改,这是因为实际上 newObj 对象获得的只是一个内存地址,而不是真正的拷贝,所以 obj 对象被篡改。

    var obj2 = {
      name: "xiaoming",
      age: 23,
    };
    var newObj2 = Object.assign({}, obj2, { color: "blue" });
    newObj2.name = "xiaohua";
    console.log(obj2.name); // xiaoming
    console.log(newObj2.name); // xiaohua
    console.log(newObj2.color); // blue
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    上方利用 Object.assign()方法进行对象的深拷贝可以避免源对象被篡改的可能。因为 Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

    var obj3 = {
      name: "xiaoming",
      age: 23,
    };
    var newObj3 = Object.create(obj3);
    newObj3.name = "xiaohua";
    console.log(obj3.name); // xiaoming
    console.log(newObj3.name); // xiaohua
    
    1
    2
    3
    4
    5
    6
    7
    8

    我们也可以使用 Object.create()方法进行对象的拷贝,Object.create()方法可以创建一个具有指定原型对象和属性的新对象。

    编辑 (opens new window)
    #js
    上次更新: 2022/09/30, 11:34:22
    JS中的内存管理
    JavaScript有用的代码片段和trick

    ← JS中的内存管理 JavaScript有用的代码片段和trick→

    最近更新
    01
    咖啡知识
    10-13
    02
    documentation
    09-29
    03
    benchmarking
    09-29
    更多文章>
    Theme by Vdoing | Copyright © 2018-2022 csjiabin | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式