一、一般初始化数组的方法
function initialize(list) {
for(let i = 0; i < list.length; i++) { // forEach也行
list[i] = 0;
}
return list;
}
const list = new Array(100);
initialize(list);
console.log(list); // [0, 0, ... 0];
或
const list = Array(100).map(() => 0);
或
const list = Array.from({length: 100}, () => 0);
const list1 = Array.from({length: 10}, (_, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
二、但注意fill方法用来填充元素的值如果是引用类型,它并不会拷贝这个值,而是直接将引用赋给数组,这也就是说,如果我们企图通过fill来初始化二维数组,是有问题的。
const list = Array(10);
list.fill(0, 0, 5);
list.fill(1, 5);
console.log(list); // [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
三、使用生成器(迭代协议?)
对于更复杂的初始化需求,我们可以构建可复用的生成器。
function *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
yield mapFunc(i, count);
}
}
const list1 = [...initializer(10)];
console.log(list1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const list2 = [...initializer(10, i => 10 + 2 * i)];
console.log(list2); // [10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
在这里,有同学可能会说,我们不用生成器,直接将数组构建出来也是可以的:
function initialize(count, mapFunc = i => i) {
const ret = [];
for(let i = 0; i < count; i++) {
ret.push(mapFunc(i, count));
}
return ret;
}
const list1 = initialize(10);
console.log(list1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const list2 = initialize(10, i => 10 + 2 * i);
console.log(list2); // [10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
这个的确是可以的,不过用生成器将构建迭代器和生成数组的步骤分开,会更灵活。
我们稍微修改一下生成器,构建更复杂的数据:
function *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
const value = mapFunc(i, count);
if(value[Symbol.iterator]) yield* value;
else yield value;
}
}
const mat3 = [...initializer(3, i => initializer(3, j => i === j ? 1 : 0))]
console.log(mat3); // [1, 0, 0, 0, 1, 0, 0, 0, 1]
yield* 可以将一个可迭代对象委托给一个生成器,所以上面的代码判断如果value返回的是一个可迭代对象,那么递归迭代这个对象并返回,所以我们用它来生成一个3x3的初始矩阵,它是一个长度为9的数组,初始值是[1, 0, 0, 0, 1, 0, 0, 0, 1]。
上面的代码有一个问题,就是它会将所有可迭代对象递归展开,这也许不是我们期望的结果,比如:
function *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
const value = mapFunc(i, count);
if(value[Symbol.iterator]) yield* value;
else yield value;
}
}
const list = [...initializer(3, i => [0, 0, 0])];
console.log(list); // [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 *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
const value = mapFunc(i, count);
if(value[Symbol.iterator]) yield* value;
else yield value;
}
}
const list = [...initializer(3, i => [[0, 0, 0]])];
console.log(list); // [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
但是这很容易给使用者造成困扰。因此我们可以修改一下设计,只有mapFunc是生成器函数的时候,才用yield*迭代展开,否则直接yield返回。
function *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
if(mapFunc.constructor.name === 'GeneratorFunction') {
yield* mapFunc(i, count);
} else {
yield mapFunc(i, count);
}
}
}
const list = [...initializer(3, i => [0, 0, 0])];
console.log(list); // [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
const mat3 = [...initializer(3, function *(i) {
yield Number(i === 0);
yield Number(i === 1);
yield Number(i === 2);
})]
console.log(mat3); // [1, 0, 0, 0, 1, 0, 0, 0, 1]
最后,再强调一下,生成器是个好东西,用它来写简洁易读的代码来初始化数据吧,比如下面的代码初始化一副扑克牌:[花色, 点数](不包括大小王):
function *initializer(count, mapFunc = i => i) {
for(let i = 0; i < count; i++) {
if(mapFunc.constructor.name === 'GeneratorFunction') {
yield* mapFunc(i, count);
} else {
yield mapFunc(i, count);
}
}
}
const cards = [...initializer(13, function *(i) {
const p = i + 1;
yield ['♠️', p];
yield ['♣️', p];
yield ['♥️', p];
yield ['♦️', p];
})];
console.log(cards);