Javascript 如何避免映射函数中的可变范围问题?

Javascript 如何避免映射函数中的可变范围问题?,javascript,function,scope,dry,Javascript,Function,Scope,Dry,我对这个问题有一个有效的解决方案,但我正试图尽可能使它更干净整洁。我提出了另一个解决方案,在map函数中使用函数。不幸的是,这个版本有一些问题,我只想知道为什么第二个解决方案不起作用。我猜这是一个可变范围的问题。我期待着知道你对此事的看法 我有一个简单的函数,可以打印阵列中的日历日 所以一个问题是,为什么我的代码的第一个版本会得到预期的结果,而第二个版本会打印意外的结果 我试图将let更改为var,我还将计数器和开始索引设置在功能范围之外 解决方案1(有效): const currentFull

我对这个问题有一个有效的解决方案,但我正试图尽可能使它更干净整洁。我提出了另一个解决方案,在map函数中使用函数。不幸的是,这个版本有一些问题,我只想知道为什么第二个解决方案不起作用。我猜这是一个可变范围的问题。我期待着知道你对此事的看法

我有一个简单的函数,可以打印阵列中的日历日

所以一个问题是,为什么我的代码的第一个版本会得到预期的结果,而第二个版本会打印意外的结果

我试图将
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