【js】问题集锦

217 阅读7分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

一、JSON.parse对象转换问题

obj = JSON.parse(data)时候报错:

SyntaxError: Unexpected token . in JSON at position 0

原因:

  • data已经是对象了。
  • JSON.parse():将JSON字符串转换成JSON对象
  • JSON.stringify():将JSON对象转换成JSON字符串

二、ajax发HTTP请求时报错:

[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.

中文翻译:

在主线程同步XMLHttpRequest是不赞成的,因为对最终用户的体验有不利影响。

原因:

以上警告信息在ajax方法执行同步操作(等有了返回值才执行下面的js,会短暂阻断程序继续执行)的时候出现,即:async:false。

如果业务层面允许,推荐异步执行AJAX,即将async属性设置为true。(async缺省下为true)

如果非要使用同步的Ajax(这种情况应该很少见),HTML5中增加了Worker对象,每个Worker运行在一个独立的线程中,Worker线程被阻塞一般是不会影响主线程和浏览器的。

三、console.log打印的问题

其实有时候打印的不是当前值,点开可以看到有个感叹号提示value below was evaluated just now

四、const和let的区别

let声明的变量可以改变,值和类型都可以改变,没有限制。

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

五、forEach究竟能不能改变数组的值

因为forEach效率高选用forEach,结果发现无法修改数组的值,详见

blog.csdn.net/ZhengKehang…

总结:

若原数组oldArr的元素是基本数据类型,则如下:

oldArr.forEach((el)=>{
    el = 2
})

是没法将数组oldArr中的每个值改成2的。

若一定要将原数组oldArr中的数值改为2,则可通过明确数组下标的方式修改,如下即可:

oldArr.forEach((el,index)=>{
    oldArr[index] =2
})

六、定时器的问题

原来有个定时器,这么写:

function MapInit (flag) {
  pageFlag = flag
  svgDoc = null
  if (svgDoc === null) {
    clearTimeout(timeSvgDoc)
    console.log(111)
    timeSvgDoc = setTimeout(MapInit(1), 1000)
  } else {
    ……
  }
}

结果:控制台飞速打印111,直达栈溢出

思考:

  • 1、怀疑有多个定时器存在,于是加上clearTimeout(timeSvgDoc),没用。
  • 2、考虑修改时间间隔,1000改为10000即10s钟,发现没用任何影响,飞速打印111。
  • 3、对比其他正常的定时器,发现写法不对,改为如下即可。
function MapInit (flag) {
  pageFlag = flag
  svgDoc = null
  if (svgDoc === null) {
    clearTimeout(timeSvgDoc)
    timeSvgDoc = setTimeout(() => {
      MapInit(1)
    }, 1000)
  } else {
    ……
  }
}

原因:

问题在于定时器setTimeout的第一个参数要求是一个函数,比如

setTimeout(MapInit,1000)

但是因为函数带了参数时,第一个参数其实是函数的返回值了,如下:

setTimeout(MapInit(1),1000)

所以马上执行了MapInit函数获取其返回值,而返回值定时1s后执行并没有什么效果。

七、JS浮点数运算的误差问题

经典问题:

0.1 + 0.2 === 0.3 // false

同样的系统中碰到:

30-19.47=10.530000000000001

问题原因:

因为浮点数运算的时候,先转化为二进制,用二进制来算,结果再转回十进制。

而浮点数19.47转化为二进制是个无限循环小数,只能取近似值,误差就这么产生了。

解决办法:

用toFixed()四舍五入为指定的小数位数即可。

八、typeof和instanceof的一个问题

判断对象类型时经常出现如下问题

typeof aaa // "string"

aaa instanceof String // false

为什么?怎么办?

首先了解一下typeof和instanceof分别是什么:

typeof

此操作符返回一个字符串,表示未经计算的操作数的类型。

下表总结了typeof常见的返回值

等,详见developer.mozilla.org/zh-CN/docs/…

额外的,我们可以巧用typeof来判断一个变量是否存在,用typeof a !== "undefined"代替if(a)更好,因为如果a为定义if(a)会报错。

instanceof

该运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置,oops有点听不太懂。

详见

developer.mozilla.org/zh-CN/docs/…

里面有提到String对象的一些特殊情况

var simpleStr = "This is a simple string";
simpleStr instanceof String; // 返回 false, 检查原型链会找到 undefined

这不就回答了我常遇见的问题!!

然后,分析如下:

simpleStr和aaa一样,只是一个以string为数据类型的值,并不是String的实例对象。

aaa instanceof String 在只有左侧是右侧类的对象时才会返回ture

aaa instanceof Object 的结果同样是false(所有对象都是Object的实例)

也就是说aaa本身并不是任何一个类的实例,如图

若将这个数据类型转换为String的类对象就会出现如下情况:

var bbb = new String(aaa);
bbb instanceof String //ture

最后可改为如下判断:

typeof aaa // "string"
aaa instanceof String // false
typeof aaa === 'string' // true

九、replaceAll('_', '.')时不时报错replaceAll is not a function

某些浏览器某些版本可以使用replaceAll('_', '.')进行全部替换,某些又报错replaceAll is not a function。

总之少用replaceAll,用.replace(/_/g, '.')即可。

十、foreach是无法跳出循环的

看到有人说 return false 可以,但也只是相当于for循环中的continue,而不是break。

其实,我觉得这个命名就很准确了,我就是来foreach的,for每一个啊,你不要for每一个那你就去用for循环啊。

十一、双层for循环的问题

for (let i = 0; i < aaa.length; i++) {
  for (let j = 0; j < bbb.length; j++) {
    if ((bbb[j].id === aaa[i].epId) && (aaa[i].epId === 1)) {
      // 注意有两层循环
      // 如果if条件想当然的改写为if (bbb[j].id === 1) 是不对的,会进来多次。
    }
  }
}

十二、es6对象定义简写

let aaa = 'a'
原来写法:
let param1 = { aaa: aaa} // { aaa: 'a' }
简写:
let param2 = { aaa } // { aaa: 'a' }

十三、window.localStorage和localStorage

查阅资料后,两者的区别个人理解如下:

localStorage是全局对象window中的一个,调用时可以省略window。

比如alert()方法,window.alert()和alert()也是一样的。

十四、关于空格

&nbsp;
&ensp;
&emsp;

都是空格,但是空格不可累加,无论写多少个,就只有一个空格。

十五、iframe通信(里层>外层)

iframe里层发送信息:

window.top.postMessage(err.data, '*')
  • 第一个参数:向iframe外层传递的信息
  • 第二个参数:要发送到的地址(这里有个问题,我就是拿不到外层的URL地址啊,所以只能配置*,群发。)

iframe外层监听信息:

let that = this
window.addEventListener('message', function (event) { // 监听postMessage传过来的信息
  console.log(event)
}, false)

十六、定时器和异步请求连用时销毁有问题

有如下代码:

为什么多次触发定时器销毁无效.png

timeaaa是一个定时器对象,收到异步接口的返回后,清除上一个定时器,开启下一个定时器。

页面销毁时,清除定时器。

这里有个现象,当多次触发接口调用时,离开页面销毁时,定时器未成功清除。

一开始我始终不解,这里只有一个timeaaa对象,始终将定时器赋值给它,最后清除,为什么还会存在定时器?

同事解惑:

微信图片_20221212172118.png

定时器返回的是一个数值,如果同时生成了多个定时器对象,clearTimeout时,只清除了第二个1816,第一个还是存在的。

所以如果要解决这个问题,需要从外部解决掉这个定时器的漏洞。

可以考虑用请求取消来解决。

十七、数组的指向引用问题

若定义一个对象等于某个数组,此时修改对象属性,原数组也会跟着变动。

let arr = [0,1,2]
let aaa = arr
aaa[0] = 123
console.log(aaa) // [123,1,2]
console.log(arr) // [123,1,2]

因为数组的存储方式导致的正常现象。

所以在诸如选中一行表格内容进行编辑修改时,需要注意特殊处理,让列表的数据不会跟着变化。

十八、数组排序

对象数组arr中根据某个属性值进行升序/降序排序:

ascendSort (arr, key) {
  // 升序排序
  return arr.sort((a, b) => a[key] - b[key])
  // 降序排序 arr.sort((a, b) => b[key] - a[key])
},