タグ (スペースで区切られています): apply
コードを書くとき、通常、配列を素早く初期化する必要があります。たとえば、zeros
に似た関数が必要です。ここで必要な場合は、1 日 24 時間を表す 0 ~ 23 の配列を生成します。 zeros
函数,假如这里我们需要生成一个0-23的数组用于表示一天24小时。
最基本的做法如下:
function(){ let hours = []; for(let k = 0; k < 24; k++ ) hours.push(k); return hours; }
下面我们来思考如何用更优雅的方式实现。
考虑使用new Array(24)
+ map
的方法来实现。
代码如下:
Array(24).map((_, h) => h);
注意,这里map
的第二个参数是索引,平时用的少,这里把索引作为数值。
结果与预期并不符合,为啥呢?
简单搜索了一下,发现时因为js里的稀疏数组的逻辑导致的。
我们先看一下下面的代码:
let a = []; a[1000] = 2; console.log(a.length);// 1000a.forEach(x => console.log("hello"));// only one "hello"
js的处理逻辑是,对于没有主动赋值的位置进行“空置”处理,对于这些“空置”未知,迭代器是不会理会的,这么做最主要的目的就是避免不合理的赋值操作导致的bug。
假设没有这种逻辑,我们写下了new Array(Date.now())
,这将导致系统新建一个非常大的数组,而实际上啥也没存。
我们可以吧new Array(len)
干的事情简单理解为下面的过程:
function(len){ let r = []; r.length = len; return r;
这就是为什么对new Array(len)
调用map
或者forEach
的时候跟预期不一致了。
如何解决这一问题呢,除了使用new Array(len)
的形式,我们还可以使用new Array(1,2,3)
这种写法来初始化数组,但是这么写就没法实现我们编写初始化数组的目的了。
这个时候我们想到了apply
,这个函数的第二个参数正好就是一个数组,于是我们写下了下面的代码。
// 借用apply Array.apply(null, Array(24)).map((_, h) => h); // [0, 1, ..., 24]
得到了我们希望的结果。这就说明,Array(24)
在apply
中作为参数的时候是被当做24个值对待的,因为这一点就保证了最后得到的数组长度是24。
既然如此,我们当然同样可以使用apply
的姊妹函数call
。
// 借用call Array.call(null, ...Array(24)).map((_, h) => h); // [0, 1... 23]
这也更确认了一件事,Array(len)
解构会得到len
个参数而非一个参数,当然call
的使用必须在支持解构操作符的环境中。
在熟悉了call
和apply
的原理后,我们可以进一步写出下面的代码:
// Array本身 Array(...Array(24)).map((_, h) => h); // [0, 1, ..., 24]
这种形式已经足够优雅了。
另外,在ES6
中,Array
提供了新方法fill
,借用该方法填充那些“空置”位,进而保证后续的操作能顺利进行。
具体代码如下:
// 推荐 Array(24).fill(null).map((_, h) => h);
现在也比较推荐最后一种写法,这种方法也最为直观。
不过需要注意fill
最も基本的なアプローチは次のとおりです:
// no-fillconsole.time("no-fill");let t = Array(5e7);console.timeEnd("no-fill");// fillconsole.time("fill");let q = Array(5e7).fill(null);console.timeEnd("fill");// => no-fill: 0.240ms// => fill: 3247.921ms
これを実現するには、new Array(24)
+ map
メソッドの使用を検討してください。
map
の 2 番目のパラメータはインデックスですが、ここではインデックスが数値として使用されていることに注意してください。 🎜結果が期待と一致しません。なぜですか? 🎜簡単に検索したところ、js の疎配列のロジックが原因であることがわかりました。 🎜まず次のコードを見てみましょう: 🎜rrreee🎜 js の処理ロジックは、値がアクティブに割り当てられていない位置の「空席」を処理することです。これらの不明な「空席」については、イテレーターは処理しません。最も重要なことは、これを行うことです。目的は、無理な代入操作によって引き起こされるバグを回避することです。 🎜そのようなロジックはなく、new Array(Date.now())
を記述するとします。これにより、システムは非常に大きな配列を作成しますが、実際には何も保存されません。 🎜new Array(len)
が何を行うのかは、次のプロセスのように簡単に理解できます: 🎜rrreee🎜これが、map が <code>new Array(len)
で呼び出される理由です。 /code> または forEach
は期待と一致しません。 🎜この問題を解決するには、new Array(len)
を使用する以外に、new Array(1,2,3)
を使用して配列を初期化することもできます。この方法では、初期化された配列を書き込むという目的は達成されません。 🎜今回は apply
を考えましたが、この関数の 2 番目のパラメータは配列であるため、次のコードを書きました。 🎜rrreee🎜期待通りの結果が得られました。これは、Array(24)
が apply
のパラメータとして使用される場合、24 個の値として扱われることを意味します。これにより、最終的な配列の長さが 24 になることが保証されます。 🎜この場合、もちろん apply
の姉妹関数 call
も使用できます。 🎜rrreee🎜 これは、Array(len)
の分解では 1 つのパラメータではなく len
パラメータを取得することも確認しています。もちろん、call
は必要です構造化演算子をサポートする環境で使用できます。 🎜🎜 call
と apply
の原則に慣れたら、さらに次のコードを書くことができます: 🎜rrreee🎜 このフォームは十分にエレガントです。 🎜さらに、ES6
では、Array
は新しいメソッド fill
を提供します。これを使用して、これらの「空き」ビットを埋めて、その後の動作を保証できます。スムーズに操作を進めることができます。 🎜具体的なコードは次のとおりです: 🎜rrreee🎜 ここで、最も直観的な最後の書き方もお勧めします。 🎜 ただし、fill
メソッドの使用に注意する必要があり、ブラインドフィルは避けるようにしてください。これは、JS の「空」ロジックが設計されているという上記のバグを引き起こすためです。避けるために。 🎜興味がある場合は、次のコードを試してみてください: 🎜rrreee🎜 ブラウザで試してみると、ブラウザは基本的に応答しません。結果が得られないように、より大きな値を設定しませんでした。 int が 4 バイトのメモリを占有すると仮定すると、この fill は 200M のメモリを占有し、5,000 万回ループする必要があります。これは必然的に大量の CPU とメモリを占有することになりますが、これはシングルスレッドの JS では絶対に許可されません。 🎜🎜🎜以上がjsで配列を素早く初期化する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。