JavaScript基础篇(一)数据类型的种类、判断以及转换

图一.png

  • 图二.png

  • 而引用类型的值都是存储在堆中,我们定义的 a 和 b 这两个变量可以理解为存储在栈中,但是因为它们的值属于引用类型,计算机会将这些值放在堆中,也就是 a 和 b 两个变量其实指向的 value 都是堆中的同一个内存地址。如下图:

    变量 a 赋值,value 指向堆中的内存地址1.png

    a 和 b 同时存在,都指向堆中的内存地址1.png

    改变 b 的值,即改变堆中内存地址1的值,所以 a 的值也跟着改变.png

  • 总结:基础类型的值存储在栈中,引用类型的值存储在堆中,栈中存储的值因为都比较小、易处理,所以值的存储是分开的;而堆中存储的值可能是一个很庞大的值(如很长很长的 json ),从性能的角度考虑,浅拷贝时就会出现两个变量共用一个堆的情况。


    typeof 运算符进行数据类型的判断

    • 识别所有基础类型
    let a;                   typeof a // 'undefined'
    const str = 'abc'        typeof str // 'string'
    const n = 100            typeof n // 'number'
    const b = true           typeof b // 'boolean'
    const s = Symbol('s')    typeof s // 'symbol'
    
    • 识别函数
    typeof console.log       // function
    typeof function() {}     // function
    
    • 可以判断该类型是否是引用类型(不可再向下细分,即不能区分该引用类型是否为 array 或者 object)
    typeof null             // object
    typeof ['a', 'b']      // object
    typeof {x: 100}       // object
    

    延伸思考:知道堆和栈的概念以及了解 typeof 的用法之后思考什么是浅拷贝?什么是深拷贝?如何手写一个深拷贝函数?

    • 我们上面的栗子中知道对象的直接重新赋值其实共用的是一个堆中的内存地址,改了 b 之后 a 对象中的相应的属性也会改变,这里我们就可以理解为 b 是通过浅拷贝 a 生成的。那么我们想 b 有自己的内存地址,a 和 b 的值在堆中完全独立分开,互不影响就需要用到深拷贝,这也是深拷贝的基本概念。相信聪明的小伙伴应该已经理解两者之间的差别了吧!那么就让我们来手写一个实现深拷贝的函数吧:
    /**
       * 深拷贝 
       * @params {Object} obj 要拷贝的对象
       */
      function deepClone(obj = {}) {
        // 如果传入的 obj 不是 object 类型直接返回
        if (typeof obj !== 'object' || typeof obj == null) {
          return
        }
        // 初始化返回结果
        let result
        // 通过 instanceof 判断传入的 obj 是数组类型还是对象类型
        if (obj instanceof Array) {
          result = []
        } else {
          result = {}
        }
        // 遍历传入的 obj
        for (let key in obj) {
          // 保证 key 不是原型的属性
          if (obj.hasOwnProperty(key)) {
            // 递归调用,保证如果这个对象中有嵌套对象将嵌套对象传入当前函数继续执行
            result[key] = deepClone(obj[key])
          }
        }
        // 返回最后得到的结果
        return result
    }
    

    上面出现了一个 instanceofhasOwnProperty,我们前面说了 typeof 可以用来判断基础类型,而 instanceof 可以用来判断引用类型,直白点就是它可以判断你是数组还是对象;而 hasOwnProperty 在很多轮子的源码中其实都会看到,主要是判断该对象的属性是自身属性还是通过原型 prototype 添加的属性,我们深拷贝的话是不拷贝对象通过原型添加的属性和方法的。最后就是递归继续遍历拷贝对象中的子对象。

    // 验证我们写的函数
    let obj1 = {
      age: 20,
      name: 'zhangsan',
      address: {
        city: 'wuhan'
      },
      arr: ['a', 'b', 'c']
    }
    let obj2 = deepClone(obj1)
    obj2.address.city = 'beijing'
    console.log(obj1.address.city, obj2.address.city) // wuhan beijing 互不影响
    

    instanceof 进行数据类型的判断

    instanceof 其内部机制是通过原型链来判断的,如果对原型和原型链不理解可能比较绕,下一部分会去记录原型和原型链,到时候再来提。这里先总结:

    • instanceof 左为实例,右为构造函数。例如 A instanceof B,用来判断 A 是否为 B 的实例,返回 boolean 值。instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
    • instanceof 可以精准判断引用数据类型 ArrayFunctionObject,而基本数据类型不能被 instanceof 精准判断,因为它本身不是一个实例对象
    console.log(2 instanceof Number);                    // false
    console.log(new Number(2) instanceof Number)         // true
    console.log(true instanceof Boolean);                // false 
    console.log('str' instanceof String);                // false  
    console.log(Symbol('protein') instanceof Symbol)     // false 不支持语法:"new Symbol()"
    console.log([] instanceof Array);                    // true
    console.log(function(){} instanceof Function);       // true
    console.log({} instanceof Object);                  // true
    new Date() instanceof Date;                          // true
    new RegExp() instanceof RegExp                      // true  
    null instanceof Null                               // 报错
    undefined instanceof undefined                    // 报 错
    

    Object.prototype.toString.call()

    好吧,摊牌了!前面介绍的两种感觉都不是很满意对吧,王炸要来了。使用 Object 对象的原型方法 toString,使用 call 改变 this 指向

    const a = Object.prototype.toString;
    console.log(a.call(2));             // [object Number]
    console.log(a.call(true));          // [object Boolean]
    console.log(a.call('str'));         // [object String]
    console.log(a.call(Symbol()))       // [object Symbol]
    console.log(a.call([]));            // [object Array]
    console.log(a.call(function(){}));  // [object Function]
    console.log(a.call({}));            // [object Object]
    console.log(a.call(undefined));     // [object Undefined]
    console.log(a.call(null));          // [object Null]
    console.log(a.call(new Date()))     // [object Date]
    console.log(a.call(new Error()))    // [object Error]
    console.log(a.call(new RegExp()))   // [object RegExp]
    

    思考:为什么这样就能区分呢?

    追根溯源我们还是要先了解 toString 方法 的用法,toString 方法返回反映这个对象的字符串,如下栗子:

    console.log("jerry".toString());//jerry
    console.log((1).toString());//1
    console.log([1,2].toString());//1,2
    console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
    console.log(function(){}.toString());//function (){}
    console.log(null.toString());//error
    console.log(undefined.toString());//error
    

    那么新的题来了:同样是检测对象 obj 调用 toString 方法,obj.toString() 的结果和Object.prototype.toString.call(obj) 的结果不一样,这是为什么?

    这是因为 toStringObject 的原型方法,而 ArrayFunction 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(Function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…..),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 上原型 toString 方法。


    数据类型的转换

    字符串转 Number
    // 传统写法
    var a = '1'
    console.log(typeof a)
    console.log(typeof Number(a)) // 普通写法
    console.log(typeof +a) // 高端写法
    
    Number 转字符串
    var a = 1
    console.log(typeof a.toString())
    
    快速转 Boolean
    var a = 0
    console.log(typeof a)
    console.log(typeof Boolean(a)) // 普通写法
    console.log(typeof !!a) // 高端写法
    console.log(!!a) // false
    
    跟着(抄袭)大佬整理一份类型转换表:
    原始值 转换目标 结果
    number Boolean 除了 0、-0、NaN 都为 true
    string Boolean 除了空字符串 ‘ ‘ 都为 true
    undefined、null Boolean false
    引用类型 Boolean true
    number string 6 => ‘6’
    Boolean、函数、Symbol string ‘true’
    数组 string [1, 2] => ‘1, 2’
    对象 string ‘[ object, Object ]’
    string number ‘6’ => 6, ‘p’ => NaN
    数组 number [] => 0, [ ‘6’ ] => 6, 其他都是 NaN
    非数组的引用类型 number NaN
    null number 0
    Symbol number 抛错
    == 和 === 运算符

    思考:为啥会有 ===== 呢,啥时候用 ==,啥时候用 ===

    来个栗子瞅瞅吧:

    100 == '100' // true
    0 == '' // true
    0 == false // true
    false == '' // true
    null == undefined // true
    

    可以看出 == 运算符对类型判断非常不严谨,不推荐使用,日常开发中除了 ==null 之外其余情况一律都用 === 。至于为啥 ==null 可以用呢,你咋这么多为什么?算了,你长得帅你有理,看如下栗子:

    const obj = {x: 100}
    if (obj.a == null) {}
    // 相当于
    if(obj.a === null || obj.a === undefined){}
    
    四则运算符
    • 加法 + 运算符的特点
      运算中其中一方为字符串,那么就会把另一方也转化成字符串,如果一方不是字符串或者数字,那么会将他转化成数字或者字符串。
    console.log(1 + '1'); // 11
    console.log(true + true); // 2
    console.log(4 + [1, 2, 3]); // 41, 2, 3
    console.log('a' + + 'b'); // aNaN 因为 + 'b' 为 NaN
    
    • 非加法运算符来说只要其中一方不是数字就会被转化成数字,说实话感觉这个基本用的不多,可能更多出现在面试题里…
    console.log(2 * '1'); // 2
    console.log(2 * []); // 0
    console.log(2 * ['2']); // 4
    console.log(2 * [1, 2]); // NaN
    

    本文部分内容参考于掘金大佬的文章:https://www.juejin.im/post/6844904201735110669
    如果有理解错误或不足的地方,欢迎大家提出修改意见,此整理仅供学习参考!!!

    文章均来自互联网如有不妥请联系作者删除QQ:314111741 地址:http://www.mqs.net/post/13700.html

    相关阅读

    • 淘宝怎么运营推广(中小卖家必学的操作思路)

      淘宝怎么运营推广(中小卖家必学的操作思路)

      淘宝在很长一段时间内,一直被认为是中国最大的电子商务平台。人们在这里购物,与在别处购物一样,会感觉到很便捷、很实惠。因此,随着电商行业的发展。如今,淘宝网站的活跃用户数已经超过了1亿人(目前该数据仅统计了部分用户)。而作为一个淘宝...

      2025.12.09 14:15:37作者:iseeyuTags:运营
    • 如何保证缓存和数据的双写一致性

      如何保证缓存和数据的双写一致性

      image 但是在更新缓存方面,对于更新完数据库,是更新缓存呢,还是删除缓存。又或者是先删除缓存,再更新数据库,其实大家存在很大的争议。目前没有一篇全面的博客,对这几种方案进行解析。于是博主战战兢兢,顶着被大家喷的风险,写了这篇...

      2025.12.09 09:28:14作者:iseeyu
    • 【百度搜索引擎优化】如何快速了解百度搜索引擎优化的知识?(搜索引擎优化基本)

      【百度搜索引擎优化】如何快速了解百度搜索引擎优化的知识?(搜索引擎优化基本)

      在百度输入SEO优化,下拉框就有很多关键词,SEO优化工具,SEO查询,SEO技巧,SEO优化方案,SEO报价,SEO优化教程,SEO优化软件,SEO优化怎么做,等等,相关搜索也有很多长尾关键词。还可以加入一些群,找些大牛问下,向这些大牛学...

      2025.12.09 07:37:38作者:iseeyu

    添加新评论