Javascript 如何避免映射函数中的可变范围问题?
我对这个问题有一个有效的解决方案,但我正试图尽可能使它更干净整洁。我提出了另一个解决方案,在map函数中使用函数。不幸的是,这个版本有一些问题,我只想知道为什么第二个解决方案不起作用。我猜这是一个可变范围的问题。我期待着知道你对此事的看法 我有一个简单的函数,可以打印阵列中的日历日 所以一个问题是,为什么我的代码的第一个版本会得到预期的结果,而第二个版本会打印意外的结果 我试图将Javascript 如何避免映射函数中的可变范围问题?,javascript,function,scope,dry,Javascript,Function,Scope,Dry,我对这个问题有一个有效的解决方案,但我正试图尽可能使它更干净整洁。我提出了另一个解决方案,在map函数中使用函数。不幸的是,这个版本有一些问题,我只想知道为什么第二个解决方案不起作用。我猜这是一个可变范围的问题。我期待着知道你对此事的看法 我有一个简单的函数,可以打印阵列中的日历日 所以一个问题是,为什么我的代码的第一个版本会得到预期的结果,而第二个版本会打印意外的结果 我试图将let更改为var,我还将计数器和开始索引设置在功能范围之外 解决方案1(有效): const currentFull
let
更改为var
,我还将计数器
和开始索引
设置在功能范围之外
解决方案1(有效):
const currentFullMonth = {
days_length: 31,
first_day: "Thu",
first_day_index: 4,
last_day: "Sat",
last_day_index: 6,
month: "Aug",
year: 2019
}
const testMonth = [
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]
];
function printMonthCalender(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) => {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
});
});
} // end of Solution #1 <-- this works :)
// start of Solution #2 <-- does not work :(
// im using two functions to make it look more cleaner
//
function printMonthCalender2(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) =>
indexingMonth(counter, startedIdxing, index)
);
});
}
function indexingMonth(counter, startedIdxing, index) {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
}// end of Solution #2
console.log(printMonthCalender(testMonth));
console.log(printMonthCalender2(testMonth));
意外结果如下(第二版):
问题是,当您在
indexingMonth
内部重新分配startedIdxing
时,它是一个局部变量,因此不会在调用函数(printMonthCalender2
)内部进行更改
一个问题是.map
不应该有突变或重新分配作为副作用。虽然您可以调整内容,以便indexingMonth
返回您检查过的内容,然后将其重新分配给startedIdxing
,但我更喜欢另一种方法:创建平面数组,例如
[0, 0, 0, 0, 1, 2, ..., 30, 31, 1, 2, 3]
然后把它切成7块:
const currentFullMonth={
天数:31天,
第一天:“星期四”,
首日指数:4,
最后一天:“星期六”,
最后一天指数:6,
月份:“八月”,
年份:2019年
}
const makeZeroArr=length=>新数组(长度).fill(0);
常量printMonthCalendar=(测试月)=>{
//创建数组:[1,2,3,…,30,31]
const oneMonth=Array.from(
{length:currentFullMonth.days_length},
(u,i)=>i+1
);
//上周创建一个前导零尾随的平面阵列:
// [0, 0, 0, 0, 1, 2, 3, ..., 30, 31, 1, 2, 3, 4, 5, 6, 7]
常量FlatResultar=[
…makeZeroArr(当前整月第一天索引),
…一个月,
…一个月//这包括将被修剪的额外数字
].slice(0,7*6);//7天/周*6周
//将平面阵列分为7块:
常量resultar=[];
for(设i=0;i<7;i++){
结果推(平面结果切片(i*7,(i+1)*7));
}
返回结果器;
};
log(printMonthCalendar())代码>在函数中,数字和布尔值等基本类型是通过值传递的,而不是通过引用传递的。因此,当您在printMonthCalender2
中定义计数器和启动ID
并尝试在索引月份中更改它们时,只要返回到printMonthCalender2
,更改就会丢失
然而,在JavaScript中,对象是通过引用传递的。因此,类似这样的方法会起作用:
function printMonthCalender2(month) {
let obj = { counter: 0, startedIdxing = false };
return month.map(week => {
return week.map((day, index) =>
indexingMonth(obj, index)
);
});
}
function indexingMonth(obj, index) {
if (index === currentFullMonth.first_day_index && !obj.startedIdxing) {
obj.counter++;
obj.startedIdxing = true;
return obj.counter;
} else if (obj.startedIdxing) {
if (currentFullMonth.days_length === obj.counter) {
obj.counter = 0;
}
obj.counter++;
return obj.counter;
} else {
return 0;
}
}// end of Solution #2
像obj.counter++
这样的东西实际上会在printMonthCalender2
中定义的原始对象中保留这些更改
警告:虽然您可以这样做,但如果您使用的是复杂的代码,这通常是不受欢迎的。如果出现问题,这些类型的突变可能很难调试。这是一种合法的编程技术,但不应该被滥用
另外,如果你在一个坚持函数式编程范式的团队中工作,我相信这是一个很大的禁忌
然而,考虑到本例中obj
变量的持续时间很短且范围有限,我个人对此感到非常满意。如果obj
的使用寿命更长,并且在代码中的许多地方都使用过,那么我会对此更加谨慎,并同意@CertainPerformance的评论,即map
语句不应改变事物。更改counter++第二个函数中的代码>不会影响第一个函数中的计数器。非常感谢各位,David先生,您的建议实际上解决了这里的问题,请发布您的答案,以便我可以接受
[0, 0, 0, 0, 1, 2, ..., 30, 31, 1, 2, 3]
function printMonthCalender2(month) {
let obj = { counter: 0, startedIdxing = false };
return month.map(week => {
return week.map((day, index) =>
indexingMonth(obj, index)
);
});
}
function indexingMonth(obj, index) {
if (index === currentFullMonth.first_day_index && !obj.startedIdxing) {
obj.counter++;
obj.startedIdxing = true;
return obj.counter;
} else if (obj.startedIdxing) {
if (currentFullMonth.days_length === obj.counter) {
obj.counter = 0;
}
obj.counter++;
return obj.counter;
} else {
return 0;
}
}// end of Solution #2