通过深浅拷贝来学习数据类型判断

234 阅读3分钟

JS数据类型有哪些

必要性

这块知识很杂,但是又特别重要,我们拿到数据经常要做一些判断。而且在手写一些基本方法的时候必定会用到,比如数组常用方法,柯里化、深浅拷贝,call,apply,bind。因为别人在使用你的方法的时候会传入让你意想不到的数据类型,比如用数组调用call方法,所以在我们需要在这些方法针对不同的数据类型做判断。

JS数据类型

基本(原始)数据类型:Number String Boolean Undefined Null Bigint(ES6新加) Symbol(ES6新加)

引用数据类型:object(Function Array Object)

数据类型判断方法

1.typeof

除了Object,Array,Null会被判断为object,其他都正确,NaN会被判断为Number

2.instanceof

只能判断引用类型,基本类型判断不了!

  console.log([] instanceof Array); // true
  console.log([] instanceof Object); // true
  console.log(function () {} instanceof Function); // true
  console.log({} instanceof Object); // true
  

原理是顺着原型链查找,代码如下

  function myInstanceOf(left, right) {
    let proto1 = right.prototype;
    let proto2 = Object.getPrototypeOf(left);  //建议不要用__proto__
    while (proto2) {
      if (proto1 === proto2) {
        return true;
      }
      proto2 = Object.getPrototypeOf(proto2);
    }
    return false;
  }
  let arr = [1, 2, 3];
  let str = "123";
  console.log(myInstanceOf(arr, Array));
  console.log(myInstanceOf(str, Array));
  console.log(myInstanceOf(str, Object));
  console.log(myInstanceOf(arr, Object));`

3.constructor

利用隐式原型上的constructor属性,查找到它的构造函数,做判断。

        `console.log((2).constructor === Number);`

存在的问题

构造函数的显式原型被重写,导致判断错误。

解释

constructor是构造函数原型对象上的属性,所以实例对象用这个属性的时候没有,就顺着原型链找,找到它的隐式原型上的constructor属性,从而指向它的构造函数。但是如果把构造函数的的prototype指向另外一个obj了,那么你实例对象的constrctor就找到obj了,但是obj还是一个实例对象啊,比如[1,2,3],你new Array()创建出来的 ! 然后在顺着arr原型链查找,找到Array.prototype,然后发现有constructor,Array判断正确,但是它其实是Fn的实例对象,不是Array的。判读错误。

4.Object.prototype.toString.call()

终极大Boss,啥都能判断正确,但是判断出来结构是下面这样,需要slice截取

                    [object Number]

手写深拷贝

学习完数据类型的判断,来手写一个完善的深拷贝吧!只能拷贝自身可枚举属性。

代码如下:

function deepClone(obj) {
    if (obj === null) return obj;
    if (typeof obj !== "object") return obj; //不是数组或对象也不是null
    //数组或对象
    let res = obj instanceof Array ? [] : {};
    //不要用传统的for循环,对象没有length!,也不能用for-of,对象没有iterator
    for (let item in obj) {
      //item拿到索引
      if (obj.hasOwnProperty(item)) {
        if (typeof obj[item] !== "object" && obj[item] !== null) {
          //如果里面不是对象和数组
          res[item] = obj[item];
        } else {
          res[item] = deepClone(obj[item]); //递归考虑里面的嵌套数组
        }
      }
    }
    return res;
  }
  let arr = [1, 2, [2, 3, [4, 5, 6]]];
  let obj1 = {
    a: 1,
    b: 2,
    c: { d: 3 },
  };

  console.log(deepClone(arr));
  console.log(deepClone(obj1));
  console.log(deepClone(obj1) === obj1);
  console.log(deepClone(arr) === arr);

补充:

其他深拷贝方法1:

            JSON.parse(JSON.stringify(obj))

存在问题:函数,undefined,symbol会消失。

其他深拷贝方法2:

                    lodash库