js身为一种弱类型的语言,不用像c语言那样要定义int、float、double、string等等数据类型,允许变量类型的隐式转换和允许强制类型转换。我们在定义一个变量的时候,就一个var、let、const搞定,不用担心数据的类型。比如常见的字符串拼接,用+号可以实现变量和字符串的拼接。总的来说,一般的规则是
- !后面的字符会被转为换布尔
- +后面的字符会被转换为数值(-也是差不多)
- []+后面的字符会被转换为字符串
对于object和number、string、boolean之间的转换关系
- Object 与Primitive,需要Object转为Primitive
- String 与 Boolean,需要两个操作数同时转为Number。
- String/Boolean 与 Number,需要String/Boolean转为Number。
- undefined 与 null ,和所有其他值比较的结果都是false,他们之间==成立
ToPrimitive是指转换为js内部的原始值,如果是非原始值则转为原始值,调用valueOf()和obj.toString()来实现。valueOf返回对象的值:在控制台,当你定义一个对象按回车,控制台打印的是Object{...},obj.toString()返回对象转字符串的形式,打印的是"[object Object]"
- 如果参数是Date对象的实例,那么先toString()如果是原始值则返回,否则再valueOf(),如果是原始值则返回,否则报错。
- 如果参数不是Date对象的实例,同理,不过先valueOf再obj.toString()。
1. 一些例子- {}+{}//"[object Object][object Object]"
复制代码- ''+{} //"[object Object]"
复制代码- []["map"]+[] //"function map() { }"
复制代码- []["a"]+[] // "undefined"
复制代码- [][[]] + []// "undefined"
复制代码 2. 从[]==![]开始大家也可能听说过[]!=[],主要是因为他们是引用类型,内存地址不同所以不相等。那么为什么加了一个!就能等于了?不是内存地址还是不一样吗?这又引出一个问题,符号的优先度
1. [] ()2++ — ~ !3* / %4+ - +5>4+ - +5< >=4+ - +6== != === !==可以看见,!优先度是第二,所以先判断!再判断=
给[]取反,会是布尔值,[]的取反的布尔值就是false
[h1][/h1][h1]2.1 ![]===false[/h1][h2]规范:[/h2]非布尔类型转布尔类型:undefined、null 、0、±0、NaN、0长度的字符串=》false,对象=》true
非数字类型转数字类型:undefined=》NaN,null=》0,true=》1,false=》0,字符串:字符串数字直接转数字类型、字符串非数字=》NaN
[]也是object类型(typeof [] == "object"),转为布尔类型的![]就是false
[h1][/h1][h1]2.2 等号两边对比[/h1]我们知道,在比较类型的时候,先会进行各种各样的类型转换。从开头的表格可以看见,他们比较的时候都是先转换为数字类型。右边是布尔值false,左边为一个空数组对象,对于左边,先进行P操作(ToPrimitive([])),先执行valueOf([])返回的是[],非原始类型,再
[].toString(),返回的是"",那P操作之后,结果就是""了
最后,左边""和右边false对比,他们再转换为数字,就是0==0的问题了
3.从已有的得到想不到的[h1]3.1 间接获取数组方法[/h1]我们知道,数组有自己的一套方法,比如
- var arr = [1,2];arr.push(1)
复制代码 我们可以写成,还可以写成,那么前面抛出的问题就解决了
- []["map"] //function map() { [native code] }
复制代码- []["map"]+[] // "function map() { [native code] }"
复制代码 [h1][/h1][h1]3.2 间接进行下标操作[/h1]
[h2]3.2.1数字的获取[/h2]我们可以通过类型转换,获得0和1两个数字,既然能得到这两个数字,那么也可以得到其他的一切数字了:
那么
- +((+![])+(+!![])+[]+(+![]))=== 10
复制代码 注意:中间没[]的话,就是数字的1+0,结果就是1了,有的话就是'1'+''+'0'
- +((+![])+(+!![])+[]+(+![]))-!![] === 9
复制代码 [h2][/h2][h2]3.2.2 字符串下标[/h2](![]+[])是"false",其实(![]+[])[+[]] 就相当于"false"[0],第一个字母,就是f
我们就可以从上面的那些获得单词的字符串获得其中的字母了
好了,说到这里,看看这个
- (![]+[])[+!![]+!![]+!![]] +([]+{})[+!![]+!![]]
复制代码 4.关于(a==1 && a==2 && a==3)[h1]4.1 ==[/h1](a==1 && a==2 && a==3) 或者(a===1 && a===2 && a===3) 能不能为true?事实上是可以的,就是因为在==比较的情况下,会进行类型的隐式转换。前面已经说过,如果参数不是Date对象的实例,就会进行类型转换,先valueOf再obj.toString()
所以,我们只要改变原生的valueOf或者tostring方法就可以达到效果:- [/code][list][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][/list][code]var a = {
复制代码- var eq = (a==1 && a==2 && a==3);
复制代码- [/code][code]//或者改写他的tostring方法
复制代码- Function.prototype.toString = function(){
复制代码- [/code][code]//还可以改写ES6的symbol类型的toPrimitive的方法
复制代码- var a = {[Symbol.toPrimitive]: (function (i) { return function(){return ++i } }) (0)};
复制代码 每一次进行等号的比较,就会调用一次valueOf方法,自增1,所以能成立。当然,如果换个位置就不满足了。
减法也是同理:- [/code][list][*][*][*][*][*][*][*][*][/list][code]var a = {
复制代码- var eq = (a==3 && a==2 && a==1);
复制代码 4.2 ===如果没有类型转换,===的情况,还是可以的。在vue源码实现双向数据绑定中,就利用了defineProperty方法进行观察,观察到数据的变化并实时反映到视图层。每一次访问对象中的某一个属性的时候,就会调用这个方法定义的对象里面的get方法。每一次改变对象属性的值,就会访问set方法
在这里,我们自己定义自己的get方法:- [/code][list][*][*][*][*][*][*][/list][code]var b = 1
复制代码- Object.defineProperty(window, 'a', {
复制代码- get:function() { return b++; }
复制代码- var s = (a===1 && a===2 && a === 3 )
复制代码 每一次访问a属性,a的属性值就会+1。proxy也是类似的方法,都可以实现
|
|