1.场景,
最近在做微信小程序,在处理一段函数时,发现在函数头部输出的console.log()居然输出了函数中后续处理的函数值,(手动狗头🤣,js同步无了)
2.问题再现
function a(){
let obj = [{}]
console.log(obj)
obj.map(item => {
item.a = '1'
})
console.log(obj)
}
a()
其输出如下,(大家可以贴入浏览器-控制台验证)
此时可以清晰的发现,发现函数的第一个console.log()和函数的第二个console.log()居然输出的是同一个值,😫第一个输出居然不是‘[{}]’,这怎么办呀,难不成和js编译有关?变量提升,赋值?亦或是函数内部的奇怪编译顺序?
,于是乎,个人进行了js编译的相关编译过程的复习,过程中却并没有找出相关问题,发现这和常见的编译过程并没有太大关系
后续经过了解,在《JavaScript异步编程》书中解释如下:
WebKit的
console.log并没有立即拍摄对象快照,相反,它只存储了一个指向对象的引用,然后在代码返回事件队列时才去拍摄快照。
Node的console.log是另一回事,它是严格同步的,因此同样的代码输出却为{}。并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。 尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。
书中建议:
如果遇到这种少见的情况,最好的选择是在JavaScript 调试器中使用断点,而不要依赖控制台输出。次优的方案是把对象序列化到一个字符串中,以强制执行一次“快照”,比如通过JSON.stringify(..)。
此时,按照书中的JSON.stringify(obj)进行输出
function a(){
let obj = [{}]
console.log(JSON.stringify(obj))
obj.map(item => {
item.a = '1'
})
console.log(obj)
}
a()
推论:JSON.stringify(obj)得出的值为[{}]为原猜想值,后续改变是由于,个人点击控制台对象进行展开,浏览器对于该值有一次进行求值,此时内存中数据已被后续改变,故而表现为异步输出。
得出如下结论,
- 在WebKit运行环境下,对引用型数据建议不要使用console.log()进行调试,浏览器会表现为异步输出
- 基本数据类型不存在该问题,
- node环境中该问题不存在
3.原场景
为接近原场景,所以贴出测试数据,以及修改代码如下,
let arr ={
company_name: "parent",
company_no: 'demo123',
id_company: "787893298900demo999",
children : [
{
id_company: '999感冒灵',
branch_id: 'aaaa',
id_store: '0789423785345一83475一34与',
shop_name: '蓝湖大街'
},
{
id_company: '777感冒灵',
branch_id: 'bbbb',
id_store: '365gfggggggggggg',
shop_name: '哈兰大姐'
}
]}
let aaa = []
aaa.push(arr)
function handler(item) {
console.log(item)
item.forEach((item) => {
item.children.map((item1, index) => {
if (!item1.isChecked) {
item.children = item.children.splice(index, 1);
}
});
});
console.log(item)
}
handler(aaa)