JS问题记录:async箭头函数 对 var 变量提升影响
ee ={ qaz:{
mylog:()=>{ console.log(" 1 e.index.mylog.........",ee) ;}
} },
S = async () => {
console.log(" inner1 e.index.mylog.........",ee)
let i = await (async () => {
var ee;
console.log("let i = await (async () => {}.........",ee);
return true;
})();
console.log(" inner2 e.index.mylog.........",ee)
let c =[
{isShow:true,itemName:"xx"},
{isShow:true,itemName:"yy"}
];
for (let ee of c) {
console.log(" for (let e of c).........",ee);
}
console.log(" last e.index.mylog.........",ee)
ee.qaz.mylog();
};
S();
vendor.js? [sm]:1 TypeError: Cannot read property 'mylog' of undefined
晕啊???
出现 TypeError: Cannot read property 'mylog' of undefined
错误的原因同样是变量作用域和变量覆盖问题,下面为你详细分析。
错误原因分析
1. 内层 async
函数中的变量声明
在 S
函数内部的内层 async
函数里,使用 var ee
声明了一个变量 ee
:
javascript
let i = await (async () => {
var ee;
console.log("let i = await (async () => {}.........", ee);
return true;
})();
这里使用 var
声明的 ee
会被提升到该内层函数的顶部,不过在赋值之前其值为 undefined
。不过这并不是导致最终错误的直接原因。
在 JavaScript 里,使用 var
声明的变量会发生 “提升” 现象。这意味着,变量的声明会被提升到其所在作用域的顶部,不过变量的赋值操作不会被提升。在变量声明语句之前访问该变量,其值为 undefined
。
因为let i 函数的 await -async 此时的ee作用域被提升到S() 函数内的顶部??
2. for...of
循环中的变量覆盖
在 for...of
循环中,使用 let ee
声明了一个新的循环变量 ee
:
javascript
for (let ee of c) {
console.log(" for (let e of c).........", ee);
}
let
关键字声明的变量具有块级作用域,for...of
循环中的 ee
仅在该循环块内有效,并且会覆盖掉外部定义的 ee
变量。当循环结束后,外部的 ee
变量已经被循环内的 ee
覆盖,此时外部的 ee
指向的是 c
数组的最后一个元素。
3. 调用 mylog
方法时出错
在循环结束后,执行 ee.qaz.mylog()
语句,由于此时 ee
是 c
数组的最后一个元素,而 c
数组中的元素是 {isShow: true, itemName: "xx"}
这种形式的对象,它们并没有 qaz
属性,所以 ee.qaz
为 undefined
,调用 ee.qaz.mylog()
就会抛出 TypeError: Cannot read property 'mylog' of undefined
错误。
解决方案
为避免变量覆盖问题,你可以给循环变量换一个名称,确保外部定义的 ee
对象不会被覆盖。以下是修正后的代码:
javascript
const ee = {
qaz: {
mylog: () => {
console.log(" ee.qaz.mylog.........", ee);
}
}
};
const S = async () => {
let i = await (async () => {
var ee;
console.log("let i = await (async () => {}.........", ee);
return true;
})();
let c = [
{ isShow: true, itemName: "xx" },
{ isShow: true, itemName: "yy" }
];
// 更换循环变量名称,避免覆盖外部的 ee
for (let item of c) {
console.log(" for (let item of c).........", item);
}
console.log(" ee.qaz.mylog.........", ee);
ee.qaz.mylog();
};
S();
总结
这个错误是由于变量覆盖导致的。在 for...of
循环中使用相同的变量名 ee
覆盖了外部定义的 ee
对象。通过将循环变量名改为 item
,可以避免变量覆盖,保证 ee.qaz.mylog()
能够正常调用。
################
原错误原因回顾
在之前的代码里,错误的根源是 for...of
循环中的变量 ee
覆盖了外部定义的 ee
对象。循环结束后,外部的 ee
指向的是 c
数组的最后一个元素,而该元素并没有 qaz
属性,所以调用 ee.qaz.mylog()
时就会报错。
修改后代码分析
1. 内层函数修改
你把内层的 async
函数调用改成了普通函数调用:
javascript
let i = ( () => {
var ee;
console.log("let i = await (async () => {}.........", ee);
return true;
})();
这里使用 var
声明的 ee
会被提升到该函数的顶部,不过它仅在这个内层函数作用域内有效,不会影响到外部的 ee
对象。
2. for...of
循环
javascript
for (let ee of c) { console.log(" for (let e of c).........", ee);}
此 for...of
循环中使用 let
声明的 ee
具有块级作用域,它只会在循环块内部有效。循环结束后,这个 ee
变量就会超出其作用域,不会影响到外部定义的 ee
对象。
3. 调用 mylog
方法
javascript
console.log(" 1111e.index.mylog.........", ee);
ee.qaz.mylog();
由于循环结束后,外部的 ee
对象没有被覆盖,它仍然指向最初定义的 { qaz: { mylog: () => { console.log("222 e.index.mylog.........", ee) ; } } }
对象,所以 ee.qaz
是有值的,调用 ee.qaz.mylog()
不会出现 Cannot read property 'mylog' of undefined
错误。
总结
修改后的代码不会出现之前的错误,是因为 for...of
循环里的 ee
变量具有块级作用域,在循环结束后就会超出作用域,不会覆盖外部定义的 ee
对象。因此,后续调用 ee.qaz.mylog()
时,ee
仍然是最初定义的对象,能够正常调用 mylog
方法。