zoukankan      html  css  js  c++  java
  • js中的 隐式数据类型转换

    隐式转换介绍

    • 在js中,当运算符在运算时,如果两边数据不统一,CPU 就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成一样的数据类型再计算
    • 这种无需程序员手动转换,而由编译器自动转换的方式就称为隐式转换
    • 例如1 > "0"这行代码在js中并不会报错,编译器在运算符时会先把右边的 "0" 转成数字 0 然后在比较大小。

    隐式转换规则

    1. 转成 string 类型: +(字符串连接符)
    2. 转成 number 类型:++ --(自增自减运算符)+ - * / % **(算术运算符)> < >= <= == !=(关系运算符)
    3. 转成 boolean 类型:! !!(逻辑非运算符)

    这里值得注意的 + ,它既是连接符,也是运算符。 1. 当 + 两边都有值,且至少一个值是字符串类型,就会出现字符串拼接。2. 当只有 + 后面有值,例如:+"123"等同于Number("123"),会将字符串转换为数字123

    字符串拼接

    当 + 两边都有值,且至少一个值是字符串类型,就会出现字符串拼接。
    如果 另一个一值是 对象 类型的,需要对该对象进行 隐式类型转换(后文会详细讲解,对象是如何进行隐式类型转换的)
    举例:

      console.log("" + null); // "null"
      console.log("" + undefined); // "undefined"
      console.log("" + 123); // "123"
      console.log("1" + "23"); // "123"
      console.log("" + [1]); // "1"
      console.log("" + {}); // "[object Object]"
    

    不同类型进行 比较 或 运算 时的隐式转换规律

    如图,任意两种不同类型的值进行比较时,会按如图方式进行相应的类型转换,例如:对象和布尔比较的话,对象 => 字符串 => 数值,布尔值 => 数值。
    那么问题来了,对象是如何转成字符串的,又是如何进一步转成数字的?

    对象 进行数据类型转换的过程

    1. 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法(结果是undefined),目前已知的只有new Date()有这个方法,
    2. 再调用对象的 valueOf 获取原始值,如果获取的不是原始值,
    3. 再调用对象的 toString 把其变成字符串
    4. 最后再把字符串基于 Number 转换为数字

    看几个例子:

    console.log([] + 1); // `[].valueOf()`没有原始值,再调用`[].toString()`得到`""`,字符串遇到`+`运算符可以进行拼接,就不需要转成数字 => "1"
    console.log([2] - true); // `[].valueOf()`没有原始值,再调用`[].toString()`得到`"2"`,再调用`Number("2")`得到数字2。true直接调用`Number(true)`得到1。最后2 - 1 => 1
    console.log({} + 1); // `{}.valueOf()`没有原始值,再调用`{}.toString()`得到"[object Object]",遇到`+` 号进行字符串拼接 => "[object Object]1"
    console.log({} - 1); // `{}.valueOf()`没有原始值,再调用`{}.toString()`得到"[object Object]",再调用`Number("[object Object]")`得到NaN,最后NaN - 1 => NaN
    

    其它类型转数字

    Number(xxx) 和 +xxx 效果是一样

    // 字符串转数字:只要遇到非有效数字字符,结果就为NaN
    Number("") // 0
    Number("123") // 123
    Number("123x") // NaN
    
    // 其它基础数据类型转换结果
    Number(true) // 1
    Number(false) // 0
    Number(null) // 0
    Number(undefined) // NaN
    
    // BigInt 类型会去除末尾的 n ,如果超过最大安全数字,就会采用科学计算法来进行显示
    Number(10n) // 10
    Number(100000000000000000000000000000000n) // 1e+32
    
    /*
    把对象转换为数字:
      + 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法(结果是undefined),目前已知的只有`new Date()`有这个方法
      + 再调用对象的 valueOf 获取原始值,如果获取的不是原始值,
      + 再调用对象的 toString 把其变成字符串
      + 最后再把字符串基于 Number 转换为数字
    */ 
    Number([]) // 0
    Number([2]) // 2
    Number([2, 3]) // NaN
    Number({}) // NaN
    

    其它类型转布尔值

    只有 0、NaN、null、undefined、空字符串、false 转为布尔类型时为 false,其余情况转换为布尔类型都是 true,而且没有特殊情况。

    举例:

    console.log(!0); // true
    console.log(!!''); // false
    

    多种数据类型进行比较运算(== 或 ===),得出下面这张图

    结论

    1. 三个等号:只有值和类型都相等,才会相等,不会进行默认的数据类型转换。推荐使用
    2. 两个等号:如果两边的数据类型不同,首先要转换为相同的数据类型(转换的过程看前文),然后进行比较。
      • 对象 == 字符串|数字,是把对象转换为字符串或数字,先后进行比较
      • null == undefined,两个等号的情况下是成立的,除此之外,null 和 undefined 和除本身以外的任何值都不相等
      • 对象 == 对象,比较的是堆内存地址,只有地址一样,结果才为 true
      • NaN !== NaN,NaN和任何值都不相等,包括和他自己
      • 除此之外,如果两边的数据类型不一样,全部统一转换为数字类型,然后进行比较

    面试题:

    第一题: [] == false;![] == false; 的输出结果是什么?为什么?

    解答:
    [] == false:首先是两个等号,两边的数据类型不一样,需要进行数据类型的隐式类型转换,按照转换规律:
    1.先看 [] 的 Symbol.toPrimitive ,不存在的情况下,再调用 [] 的 valueOf,没有原始值,再调用 [] 的 toString,等到的值为空字符串,空字符串基于 Number 转换为数字 0 。
    2.false 基于 Number 转换为数字 0。
    3.0 == 0 ,得到 true

    ![] == false:![]需要先进行布尔值转换,得到 false, false == false 得到 true

    第二题:

    let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
    console.log(result);
    

    解答:

    注意:+undefined => NaN
    100 + true => 101 + 21.2 => 122.2 + null => 122.2 + undefined => NaN + "Tencent" => "NaNTencent" + [] => "NaNTencent" + null => "NaNTencentnull" + 9 => "NaNTencent09" + false = "NaNTencentnull9false"

    第三题:

    var a = ?
    if (a == 1 && a == 2 && a == 3) {
      console.log("OK")
    }
    

    解答:
    思路一:利用 == 的转化机制,来重写 Symbol.toPrimitive 或者 valueOf 或者 toString

    1. 重写 Symbol.toPrimitive
      var a = {
        i: 0
      };
      
      a[Symbol.toPrimitive] = function () {
        return ++this.i
      };
    
      if (a == 1 && a == 2 && a == 3) {
        console.log("OK");
      };
    
    1. 重写 valueOf
      var a = {
        i: 0
      };
    
      a.valueOf = function () {
        return ++this.i
      };
    
      if (a == 1 && a == 2 && a == 3) {
        console.log("OK");
      };
    
    1. 重写 toString
      var a = {
        i: 0
      };
    
      a.toString = function () {
        return ++this.i
      };
    
      if (a == 1 && a == 2 && a == 3) {
        console.log("OK");
      };
    

    思路二:利用数组的 shift 方法的特性

      var a = [1, 2, 3]
    
      // a[Symbol.toPrimitive] = a.shift;
      // a.valueOf = a.shift;
      a.toString = a.shift;
    
      if (a == 1 && a == 2 && a == 3) {
        console.log("OK");
      };
    

    思路三:数据劫持

      Object.defineProperty(window, 'a', {
        get: function () { // 读取 window.a 属性时会触发 get 方法
          this.xxx ? this.xxx++ : this.xxx = 1;
          return this.xxx; // return 后面是给 window.a 赋的值
        },
        set(val) { // 给 window.a 赋值时会触发 set 方法
          // val 是给 window.a 赋值时的那个值 
        }
      });
      if (a == 1 && a == 2 && a == 3) {
        console.log("OK");
      };
    
  • 相关阅读:
    Linux下Tomcat日志分割
    adb logcat 命令使用说明
    linux系统下安装两个或多个tomcat
    架构师小跟班:SSL证书免费申请及部署,解决页面样式错乱问题完整攻略
    springboot获取七牛云空间文件列表及下载功能
    Java使用ganymed工具包执行LINUX命令教程
    Java学生信息管理系统源码
    数据库SQL语句性能优化
    Java开发环境系列:一篇能解决你99%问题的排雷日记
    架构师小跟班:教你从零开始申请和配置七牛云免费OSS对象存储(不能再详细了)
  • 原文地址:https://www.cnblogs.com/MrZhujl/p/14646846.html
Copyright © 2011-2022 走看看