前言
JavaScript中有很多怪异点,这个in运算符后接数组就很奇特(虽然大家很少用到这个)。语义上讲in是个介词,词性prep,表示在什么什么里面。但在js中可能就不太一样了。
问题引出
不慌,先来看看python中的效果。
>>> array = [1,2,3,4]
>>> 0 in array
False
>>> 4 in array
True
>>> 4.0 in array
True
>>> 4n in array
SyntaxError: invalid syntax
显然,此处in就是表示判断左边的元素是否存在与数组中。
>>> 4==4.0
True
>>> array.append('5')
>>> 5 in array
False
挨个匹配的,如果相等则返回True,显然字符串‘5’和数字5并不相等。非常合理是不是。
再来看看浏览器中的情况。
let a = [1,2,3,4];
1 in a;//true 合理
1.0 in a;//true
0 in a;//true 合理吗?
'1' in a;//true
3n in a;//true 离谱
发生甚么事啦。
一起来研究下。
数组的本质
数组Array 时按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
任何类型的数组都可以放入数组。
let array = [a,2,[3,4]];// a 是[1,2,3,4]
array
(3) [Array(4), 2, Array(2)]
数组实际上一种的对象。typeof运算符会返回数组的类型是object
typeof a ;//"object"
Object.keys(a);//(4) ["0", "1", "2", "3"]
这里可以看出来,数组的键全是字符串数组,从0开始。
来看看对象的设定,js中的规定,对象的键都的是字符串。
当访问对象的值是,有点式object.key和括号式object[key]。
let ob = {'0':0,'1':1,'k':'v'};
ob.k ;//"v"
ob["k"];
"v" // 两种都能取值
ob.0 // 报错啦!数字加点,还以为是小数
Uncaught SyntaxError: Unexpected number
ob['0'] //数字字符串没毛病
0
ob[0] //[]内为数字时,也能访问
0
规定:
-
点式不能使用数字,否则识别为小数,报错。
-
键名传入非字符串时,会被转为字符串
正因此,数组才能使用数字索引访问。而数组中的键都是字符串。
in 运算符 与包含
MDN上的解释是这样的:
如果指定的属性在指定的对象或其原型链中,则**in 运算符**返回true。
显然这个in并不是包含关系,而是包含该属性。
"toString" in {}; //true
in右操作数必须是一个对象值,不能是字符串。而左侧需要一个字符串,
当左侧传入不是字符串时,则调用tostring(),转为字符串.
顺带一提,要表达包含关系,可以使用Array.prototype.includes() ,例如:
[1, 2, NaN].includes('1');//false
[1, 2, NaN].includes(1.0);//true
[1, 2, NaN].includes(NaN);//true
1.0.toString ()与(1).toString()
这个问题也是个笔试常考题目。首先是不能写1.toString(),因为有歧义,不知道这个.是小数点,还是调用方法。只要能规避歧义的,都能得到正确的结果。比如:
1 .toString;//'1'
(1).toString();//'1'
1.0.toString();//'1'
现在的问题是为什么1.0.toString()得到的是字符串'1',而不是字符串’1.0‘呢?
1.0 === 1;//true
从存储上来讲,JavaScript数字全部是浮点数。 根据 IEEE 754标准中的64位二进制(binary64), 也称作双精度规范(double precision)来储存。那么1.0===1也合情合理。
在toString ()的实现算法中,是根据数字的大小来将数字解码成字符串的。具体太复杂了,可以看这个
因此1.0 或是1.00,乃至是1.0000000000000001.toString() 都是’1‘ (精度不够了)。
那么问题来了,有没有数tostring()可以得到1.0呢?
俺也不知道
3n是什么?
前文的内容完全解释了1.0 in a 和’1‘ in a得到true的问题,但是最后一个3n 是什么呢。
答案是ES2020新特性:一个用于处理任意精度整数的新数字基元--n
为了更精确地表示没有位数限制的整数,ES2020引入了不同于Number数字类型的BigInt数字类型, 只用来表示整数(大整数),没有位数的限制,任何位数的整数都可以精确表示。为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。
typeof 3n //"bigint"
3n ==3;//true
3n === 3;//false
3n+4n;//7n
3n.toString();//"3"
所以3n in [1,2,3,4] ===true非常合理。
那么1.0n.tostring() 能得到1.0吗?当然,报错了。bigInt只能用于整数。
坐等ES2035修复小数的精度问题。
最后说下for in
for in 可以用与遍历,跟in差不多意思,用于遍历得到的是索引。
遍历数组时,当然得到的是字符串而不是数字。
for (let i in [1,2,3]){console.log(2+i)};
//20
//21
//22
也能遍历到原型链上的属性。
let a = [1,2,3,4];
a.k='v';
Array.prototype.key='val';
for (let i in a){console.log(i)};
0
1
2
3
k
key
ES6的for of,用于遍历数组。
for (let i of a){console.log(i)}
1
2
3
4 //到4就结束了,拿不到属性k
for of 遍历对象或类数组会报错。
综上:for in 适合遍历对象
for of 遍历数组
总结
js中的in不是表示包含关系,而且表示属性存在与否
数组中的属性均为字符串,in运算符判断是存在类型转换