>[success] ## 类型装换 ![](https://img.kancloud.cn/4a/61/4a618de936a2fc10a14bda61232f757e_1127x447.png) >[danger] ##### StringToNumber ~~~ 1.这里指的是用Number 方法将字符串转换成数字,支持十进制、二进制、八进制和十六进制 2.支持正负号科学计数法,可以使用大写或者小写的 e 来 3.去掉首尾空格后的纯数字字符串中含有的数字。如果剩余字符串为空,则转换结果为 0。否则,将会从剩余字符串 中“读取”数字。当类型转换出现 error 时返回 NaN。 ~~~ * 案例 ~~~ console.log(Number('30')) // 十进制 console.log(Number('0b111')) // 二进制 console.log(Number('0o13')) // 八进制 console.log(Number('0xFF')) // 十六进制 console.log(Number('1e3')) // 科学计数法 console.log(Number('-1e-2')) // 科学计数法 打印结果: 30 7 11 255 1000 -0.01 ~~~ >[danger] ##### NumberToString ~~~ 1.只需要注意:当 Number 绝对值较大或者较小时,字符串表示则是使用科学计数法表示的 ~~~ * 案例 ~~~ console.log(String(1000000000000000000000000000000000000000)) 打印结果: 1e+39 ~~~ >[danger] ##### 装箱转换 ~~~ 1.每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换, 正是把基本类型转换为对应的对象 ~~~ * 抛开symbol 看其他类型的装箱转换 ~~~ // Number 1 new Number(1) // String 'aaa' new String('aaa') // Boolean true new Boolean(true) ~~~ * 特殊的Symbol ~~~ 1.由于Symbol 类型是不支持new 来调用的,因此转换我们可以使用一个call来帮助 ~~~ ~~~ var symbolObject = (function(){ return this; }).call(Symbol("a")); console.log(typeof symbolObject); //object console.log(symbolObject instanceof Symbol); //true console.log(symbolObject.constructor == Symbol); //true ~~~ >[danger] ##### 拆箱转换 ~~~ 1.在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。 2.拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在, 或者没有返回基本类型,则会产生类型错误 TypeError。 ~~~ * 数字的拆箱转换 ~~~ 1.当执行o*2 时候由于2 是数字因此先调用valueOf,没有值 就调用toString,也没值就报错 2.如果是 o+'' 就会先调用toString没值,调用valueOf 没值就报错 ~~~ ~~~ var o = { valueOf : () => {console.log("valueOf"); return {}}, toString : () => {console.log("toString"); return {}} } o * 2 // valueOf // toString // TypeError ~~~ * es6允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为 [toPrimitive ](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive) ~~~ var o = { valueOf : () => {console.log("valueOf"); return {}}, toString : () => {console.log("toString"); return {}} } o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"} console.log(o + "") // toPrimitive // hello ~~~ >[info] ## 触发类型转换的一些操作 ~~~ 1.运算相关的操作符包括 +、-、+=、++、* 、/、%、<<、& 等。 2.数据比较相关的操作符包括 >、<、== 、<=、>=、===。 3.逻辑判断相关的操作符包括 &&、!、||、三目运算符。 ~~~ >[danger] ##### 神奇的 + 号 ~~~ 1.'+' 可表示数字计算,字符串拼接 1.1.当加号两边一边是字符串时候为字符串拼接 1.2.当加号两边为数字时候是数字计算 1.3.加号两边有一边是对象的时候先调用"obj[Symbol.toPrimitive]('default')" => '没有这个属性,则再次调用valueOf' => 'valueOf获取的不是原始值,则继续toString,此时获取的结果是字符串,“+”就变为字符串拼接了' ,说明原始 值像const a = {}; a.valueOf() 得到的是{ } 就是非基本类型原始值,则会继续调用toString 2.'+' 只有一边 ,一般都是转换为数字,举个例子 +true 则为 1 ~~~ * 对1.3 说明 ~~~ console.log(10 + [10]); // 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString "10" => "1010" console.log(10 + {}); // 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString "[object Object]" => "10[object Object]" console.log(10 + new Date()); // 调用日期的Symbol.toPrimitive('default') => "10Sun Jul 25 2021 11:28:37 GMT+0800 (中国标准时间)" console.log(10 + new Number(10)); // 没有Symbol.toPrimitive -> valueOf 10 => 20 console.log(10 + new String('10')); // 没有Symbol.toPrimitive -> valueOf "10" => "1010" // -------------------- {}+1 直接在浏览器输入显示结果 为 1 原因是 此时{} 不代表对象而是'块' ,也就是只有 +1有自己含义,即为1 ~~~ >[danger] ##### '==' 和 '===' ~~~ 1.'=='相等,两边数据类型不同,需要先转为相同类型,然后再进行比较 @1 对象==字符串 对象转字符串「Symbol.toPrimitive -> valueOf -> toString」 @2 null==undefined -> true null/undefined和其他任何值都不相等 null===undefined -> false @3 对象==对象 比较的是堆内存地址,地址相同则相等 @4 NaN!==NaN @5 除了以上情况,只要两边类型不一致,剩下的都是转换为数字,然后再进行比较的 2.'==='绝对相等,如果两边类型不同,则直接是false,不会转换数据类型「推荐」 ~~~ >[danger] ##### 求a 满足a == 1 && a == 2 && a == 3 ~~~ 1.首先明确问题 即问题中使用的是双等,当双等时在和数字比较时候需要转换为数字,转换数字的过程会依次调用 1.1.Symbol.toPrimitive 1.2.valueOf 获取的非原始值则继续向下找 1.3.toString 即对这三种方法任意一种重写即可 2.利用对象基于数据劫持完成对应的操作,基于 var/function 声明的变量,其实可以看做 var a=? ==> window.a=?,即只要劫持window 对象中的a 即可,使用劫持'proxy' 或者'Object.defineProperty' 3.总结一个误区,比较时候调用是内部机制默认触发其对应方法行为,在这期间我错误思路记录 我想让a 通过闭包连续调用则出现下面代码 var i = 0 window.a = (function () { return ++this.i })() if (a == 1 && a == 2 && a == 3) { console.log('OK') } ~~~ ~~~ // 解决方案一:利用 == 比较的时候,会把对象转换为数字 Number(a) // + Symbol.toPrimitive // + valueOf 获取的非原始值则继续向下找 // + toString // + 把字符串变为数字 var a = { i: 0, } a[Symbol.toPrimitive] = function toPrimitive() { // this -> a return ++this.i } if (a == 1 && a == 2 && a == 3) { console.log('OK') } var a = [1, 2, 3] a.toString = a.shift if (a == 1 && a == 2 && a == 3) { console.log('OK') } // 解决方案二:在全局上下文中,基于 var/function 声明的变量,并不是给VO(G)设置的全局变量「基于let/const声明的变量才是」,而是给GO(window)全局对象设置的属性 var a=? ==> window.a=? // 我们基于数据劫持完成对应的操作 var i = 0 Object.defineProperty(window, 'a', { get() { return ++i }, }) if (a == 1 && a == 2 && a == 3) { console.log('OK') } ~~~