在使用JS进行程序设计时,偶尔会遇到最终的结果与预期不同的情形,我将导致这种情况的因素称为坑点。
1、Array.prototype.sort([fn])
需要对数组原地稳定排序时,可以使用 arr.sort(fn), 但别忘了指定排序规则函数fn(a,b),否则会按元素的unicode码大小升序排序,如[1,2,10] => [1,10,2]
2、Number类型运算
js中无整数,只有IEEE754标准的双精度64位浮点数,因此做加减乘除运算的时候会很坑:
// 第一个坑点
0.1 + 0.2 不等于 0.3 // 0.30000000000000004
0.3 -0.1 不等于 0.2 // 0.19999999999999998
0.1 * 0.2 不等于0.02 //0.020000000000000004
解决办法1是 parseFloat((5/3).toPrecision(12)) 来解决绝大部分的0.0..01和0.99.。99
解决办法2是 将小数转化成整数,再运算,再转换成小数。
当需要求一个被除数与除数的商和余数时,千万别套用C++的那一套
// C++
int a = 5/3; // 1
int b = 5%3; // 2
// JS
let a = 5/3 // 1.6666666666666667
let b = 5%3 // 2
解决办法是向下取整 let a = Math.floor(5/3); // 适用于求商,其他运算以此类推
3、引用类型
拿数组举例,当回溯过程中需要用到数组来返回结果时:
function ruc(arr, res) {
if(end_situation) res.push(arr);
...
ruc(arr);
...
}
如果这样写的话,最后结果毫无意外一定是
[[],[],[],...,[]]
或者
[[same],[same],...,[same]]
所以需要在传递前做一次 仅适合于一维数组的深拷贝
JSON.parse(JSON.stringify(arr));
or
arr.slice();
or
Object.assign([],arr);
4、二维数组
JS是无法像C++一样直接int arr[][] 或 int * arr[] 创建一个二维数组的,会稍微繁琐些,当我想创建一个1到9的3X3矩阵
时:
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
在起初,可能会这样做(错误)
let arr = [], num = 0;
for(let i=0;i<3;i++) {
for(let j=0;j<3;j++) {
arr[i][j] = ++num;
}
}
报错 TypeError: Cannot set property '0' of undefined
很显然,arr[0]是undefined,这种写法不对。
于是改成这样(也是错误)
let arr = [], num = 0;
// -------新增代码段开始-----------
let tmp = [];
for(let i=0;i<3;i++) {
arr.push(tmp);
}
// -------新增代码段结束--------------
for(let i=0;i<3;i++) {
for(let j=0;j<3;j++) {
arr[i][j] = ++num;
}
}
打印结果为[ [ 7, 8, 9 ], [ 7, 8, 9 ], [ 7, 8, 9 ] ]
很显然,又是引用的问题,有两种解决办法
(1)如第四点所说,在arr.push(tmp)
中将tmp深拷贝再传递
(2)改成下面这种写法(正确)
let arr = [], num = 0;
for(let i=0;i<3;i++) {
let tmp = [];
for(let j=0;j<3;j++) {
tmp.push(++num);
}
arr.push(tmp);
}
还可以用(正确)
let arr = new Array(3), num = 0;
for(let i=0;i<3;i++) {
arr[i] = new Array(3);
for(let j=0;j<3;j++) {
arr[i][j]= ++num;
}
}
这种写法和第一种错误写法思路很类似了,使用new Array([size])还能规定长度。
如果想采用ES6来创建,这样写是(错误)的,依旧是引用的问题
let arr = new Array(3).fill(new Array(3));
let num = 0;
for(let i in arr) for(let j in arr) arr[i][j]=++num;
打印 [ [ 7, 8, 9 ], [ 7, 8, 9 ], [ 7, 8, 9 ] ]
,
正确做法如下:
let arr = Array.from(Array(3), () => new Array(3))
let num = 0;
for(let i in arr) for(let j in arr) arr[i][j]=++num;
另外需要留意的是遍历
中会有坑的点:
(1)用arr.length取矩阵的高/行数
, arr[0].length取矩阵的宽/列数
(2)arr[i][j]
表示 第i
个一维数组的第j
个元素; 也表示 第i
行j
列的元素
5、运算符优先级
举个例子: 1与2异或等于3,那么按我们的常规思维来说来说 1^2 > 1 应该是得到布尔值true的,但是实际上返回0
1^2 > 1
等价于
1^(2>1)
这是因为运算符优先级
和左/右关联
导致的,需要时查阅MDN运算符优先级
为了避免运算符这个坑,按自己的思路加小括号()
强制优先运算即可,如(1^2)> 1
6、Array.prototype.splice(start, delete_cnt, item1, item2, item3, ...)
用splice删除或插入元素时,数组长度会变,在搭配循环使用时需要留意索引的回退等等
for(let i=0;i