博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js类型转换的各种玩法
阅读量:5909 次
发布时间:2019-06-19

本文共 4234 字,大约阅读时间需要 14 分钟。

前言

对于object和number、string、boolean之间的转换关系

  • [ ] Object 与Primitive,需要Object转为Primitive
  • [ ] String 与 Boolean,需要两个操作数同时转为Number。
  • [ ] String/Boolean 与 Number,需要String/Boolean转为Number。
  • [ ] undefined 与 null ,和所有其他值比较的结果都是false,他们之间==成立

ToPrimitive是指转换为js内部的原始值,如果是非原始值则转为原始值,调用valueOf()和toString()来实现。valueOf返回对象的值:在控制台,当你定义一个对象按回车,控制台打印的是Object{...},toString()返回对象转字符串的形式,打印的是"[object Object]"

  • [ ] 如果参数是Date对象的实例,那么先toString()如果是原始值则返回,否则再valueOf(),如果是原始值则返回,否则报错。
  • [ ] 如果参数不是Date对象的实例,同理,不过先valueOf再toString()。

1.一些例子

在浏览器控制台输入一些各种运算符的组合,会出现一些有意思的结果:

![] //false; +[]  // 0 +![]  // 0[]+[] // ""{}+{}//"[object Object][object Object]"{}+[]//0{a:0}+1 // 1[]+{}//"[object Object]"[]+![]//"false"{}+[]//0![]+[] // "false"''+{} //"[object Object]"{}+'' //0[]["map"]+[] //"function map() { }"[]["a"]+[] // "undefined"[][[]] + []// "undefined"+!![]+[] //"1"+!![] //11-{} //NaN1-[] //1true-1 //0{}-1 //-1[]==![] //true

2.从[]==![]开始

我们知道,[]!=[],主要是因为他们是引用类型,内存地址不同所以不相等。那么为什么加了一个!就能划上等于号了

符号的优先度

可以参考mdn上的这个汇总表格: 可以看见,[]==![]这个情况下先判断!再判断= 给[]取反,会是布尔值,[]的取反的布尔值就是false

2.1 []的反就是false?

常见的一些转换:

非布尔类型转布尔类型:undefined、null 、0、±0、NaN、0长度的字符串=》false,对象=》true 非数字类型转数字类型:undefined=》NaN,null=》0,true=》1,false=》0,字符串:字符串数字直接转数字类型、字符串非数字=》NaN

回到[]==![]的问题上,[]也是对象类型(typeof [] == "object"),转为布尔类型的![]就是false

2.2 等号两边对比

我们知道,在比较类型的时候,先会进行各种各样的类型转换。 从开头的表格可以看见,他们比较的时候都是先转换为数字类型。右边是布尔值false,左边为一个空数组对象,对于左边,先进行ToPrimitive操作,先执行valueOf([])返回的是[],非原始类型,再 [].toString(),返回的是"",那ToPrimitive操作之后,结果就是""了 最后,左边""和右边false对比,他们再转换为数字,就是0 == 0的问题了

3.更多玩法

3.1 间接获取数组方法

我们知道,数组有自己的一套方法,比如var arr = [1,2];arr.push(1),我们可以写成[1,2].push(1),还可以写成[1,2]['push'](1),那么前面抛出的问题就解决了

[]['push'](1) //[1][]["map"] //function map() { [native code] }[]["map"]+[] // "function map() { [native code] }"

3.2 间接进行下标操作

3.2.1数字的获取

我们可以通过类型转换,获得0和1两个数字,既然能得到这两个数字,那么也可以得到其他的一切数字了: +[] === 0; +!![] === 1 那么,+!![]+!![] ===2,+((+![])+(+!![])+[]+(+![]))===10

那么10-1=9也就来了: +((+![])+(+!![])+[]+(+![]))-!![] ===9 简直就是无所不能

3.2.2 字符串下标

(![]+[])[+[]] //"f"(![]+[])[+!![]] // "a"

(![]+[])是"false",其实(![]+[])[+[]] 就相当于"false"[0],第一个字母,就是f 我们就可以从上面的那些获得单词的字符串获得其中的字母了,比如:(![]+[])[+!![]+!![]+!![]] +([]+{})[+!![]+!![]]

掌握基本套路后,我们可以随心所欲发挥,在浏览器的控制台输入一些符号的组合,然后回车看一下我们写的“密码”会转换成什么

([][[]] + [])[(+!![] + [] + [] + +![] ) >> +!![]] +$$('*')[~~[]].nodeName[- ~ -+~[]] +(this + [])[+!![]+[] + +[] - !!{} - !!{}] +([]+![])[+!!{} << +!![] -~[]] +({}+{})[+!![] -~[]]

4. 两个面试题

曾经遇到两个这种类型的面试题:

4.1 (a==1 && a==2 && a==3) 能不能为true

(a==1 && a==2 && a==3)或者(a===1 && a===2 && a===3) 能不能为true? 事实上是可以的,就是因为在==比较的情况下,会进行类型的隐式转换。前面已经说过,如果参数不是Date对象的实例,就会进行类型转换,先valueOfobj.toString() 所以,我们只要改变原生的valueOf或者tostring方法就可以达到效果:

var a = {  num: 0,  valueOf: function() {    return this.num += 1  }};var eq = (a==1 && a==2 && a==3);console.log(eq);//或者改写他的tostring方法 var num = 0;Function.prototype.toString = function(){    return ++num;}function a(){}//还可以改写ES6的symbol类型的toP的方法var  a = {[Symbol.toPrimitive]: (function (i) { return function(){return  ++i } }) (0)};

每一次进行等号的比较,就会调用一次valueOf方法,自增1,所以能成立。 另外,减法也是同理:

var a = {  num: 4,  valueOf: function() {    return this.num -= 1  }};var res = (a==3 && a==2 && a==1);console.log(res);

另外,如果没有类型转换,是 === 的比较,还是可以的。 在vue源码实现双向数据绑定中,就利用了defineProperty方法进行观察数据被改变的时候,触发set。 每一次访问对象中的某一个属性的时候,就会调用这个方法定义的对象里面的get方法。每一次改变对象属性的值,就会访问set方法 在这里,我们自己定义自己的get方法:

var b = 1Object.defineProperty(window, 'a', {  get:function() { return b++; }})var s = (a===1 && a===2 && a === 3 )console.log(s)

每一次访问a属性,a的属性值就会+1,当然还是交换位置就不能为TRUE了

4.2 完善Cash打印三个101

要求只能在class里面增加代码:

class Cash {}const a = new Cash(1)const b = new Cash(100)console.log(`${a.add(b)},${Cash.add(a,b)},${new Cash(a+b)}`) // 101,101,101

首先,三个输出结果是以隐式转换的形式出现的,这是关键之处。 a和b都是new出来的对象,由new Cash(a+b)可以看出构造函数传入的也是两个Cash的实例对象。那么new出来的结果肯定不是简简单单的一个object,不然就是被转换成'[object Object]',但是你又不得不以object类型出现,那就只能魔改隐式转换用到的toString和valueOf

class Cash {    constructor (a) {        this.m = a // 缓存真正的值        this.valueOf = function () {            console.log('value')            return a        }    }    add ($) { // a.add(b)        return this.m + $    }    toString () { // 隐式转换调用        return this.m    }    static add (v1, v2) { //Cash.add        return v1 + v2    }}

最后

然而,实际项目中两个数据作比较的时候,我们尽量不要写甚至完全不要写两个等号,应该写三个等号,而且js也慢慢有向强类型过渡的趋势。

转载地址:http://qmvpx.baihongyu.com/

你可能感兴趣的文章
半天打造小型万能OA办公系统, 靠的就是万能表单提交 + 轻量级工作流审批组件...
查看>>
我的友情链接
查看>>
CSS技巧笔记
查看>>
WP移动设备压缩与解压控件Xceed Zip for .NET Compact Framework控件下载及详细介绍使用方法...
查看>>
在右下角弹出通知窗口
查看>>
proc文件系统探索 之 根目录下的文件[六]
查看>>
搭建ICINGA监控
查看>>
LInux远程文件传输效率工具:lrzsz
查看>>
Java_String的操作
查看>>
如何成为一名“***”?
查看>>
初次使用VS2010基于C++开发项目碰到的问题及解决方法
查看>>
占用CPU时间,调用函数,点亮LED的C语言程序 【原创】
查看>>
redmine和testlink的迁移和集成
查看>>
Roman to Integer leetcode
查看>>
python中使用xlrd、xlwt操作excel表格详解
查看>>
重做日志与归档日志有什么区别
查看>>
Linux的经典shell命令整理
查看>>
克隆的CentOS6.6系统,网卡显示不存在的解决方法
查看>>
Python基础:编码规范(4)
查看>>
Git分支介绍
查看>>