配列を任意の値で初期化するには雛型から複製した方が高速です。しかし複製自体を省略する事も可能。objectを作る段階で既に初期化が完了しているという裏技を紹介します。この記事でもちら~っとこっそり触れている内容ですが、今回の記事はもう少し深掘りしていきます。
方法は極めて単純で、関数のprototype
を配列化するだけです。
benchmark
要素が数値限定の速度検証
{// TypedArray雛型の複製。遅い
let a=256, Narray=new Uint8Array(a), t=new Date;
for(;Narray[--a]=a;);//雛型の初期化
for(;a++<256;){
let A=[];
for(let i=0;i<1e4;)A[i++]=Narray.slice();
}
console.log(new Date-t);
}
{// 配列もどき。高速
let a=256, Narray=function(){}, p=Narray.prototype=Array(a), t=new Date;
for(;p[--a]=a;);//雛型の初期化
for(;a++<256;){
let A=[];
for(let i=0;i<1e4;)A[i++]=new Narray;
}
console.log(new Date-t);
}
{// TypedArrayもどき。高速
let a=256, Narray=function(){}, p=Narray.prototype=new Uint8Array(a), t=new Date;
for(;p[--a]=a;);//雛型の初期化
for(;a++<256;){
let A=[];
for(let i=0;i<1e4;)A[i++]=new Narray;
}
console.log(new Date-t);
}
{// TypedArray+配列もどき。超低速
let a=256, Narray=function(){}, p=Narray.prototype=Array(a), t=new Date;
for(;p[--a]=a;);//雛型の初期化
for(;a++<256;){
let A=[];
for(let i=0;i<100;)A[i++]=new Uint8Array(new Narray);//遅過ぎるので少なめ
}
console.log(new Date-t);
}
ちなみに昔はprototype
自体にTypedArrayを指定すると、Error祭りが開催されまくっていたような気がするんですが、今回久々に検証してみたら動作しちゃいました。
不思議な事にTypedArrayもどきはTypedArrayのmethodを全く使えません。lengthすら読めません。
初期化の計算量より配列作成後の要素参照や書き換えの計算量が高つく場合は、素直にTypedArrayの雛型を複製した方が有益です
こんな変態行為が役立つ場面と言えば、同じ構造の木を大量生産する時等でしょう(splay木、huffman木等)。
文字列の配列
任意文字列の量産も高速化可能。
function StrArray(){}; // 配列もどき
StrArray.prototype=[
"土器土器デコ割れそう",
"ドキドキで壊れそう",
"testset",
"read red paper"
];
// 文字配列もどき
let A=[];
for(let a=9;a--;)A[a]=new StrArray; //量産
console.log(A[0][0].slice());
console.log(A[0][1].slice())
object型の配列
prototype
のpropertyにobject型の何かを放り込むと意図しない動作となります。
let Oarray=function(){}, p=Oarray.prototype;
p[0]=[0,11,222,3333];
p[1]={a:"AB", bc:987, def:["CD", 1.4142]}
p[2]=a=>a*a;
let A=new Oarray, B=new Oarray;
A[0][0]=893;
A[1].a="DEF";
console.log(A[0][0], B[0][0], A[1].a, B[1].a)
お分かりになったでしょうか? 異なる変数同士であたかも要素を共有しているかのように振る舞ってしまいます(当たり前)。こんな馬鹿げた事をするくらいなら、馬鹿正直にclass
でも使えという話です。ちなみにclass
の配列化は面倒です。
class c{
[0]=1;
[1]="a";
[2]=[6,28,299792458]
}
let a=new c;
console.log(a[0],"\n",a[1],"\n",a[2])
などといった具合です。ただし上記のような方法はinstance生成が遅い上に、memory消費も多いという欠点があります。まさに本末転倒。
classの注意点
前述の関数と同じように数値配列の雛型を作ってみます。
class c{}
let a=256, p=c.prototype=[];
for(;p[--a]=a;); //要素を格納しているつもり
let b=new c;// instance生成
for(;a<256;)console.log(b[a++]," ")//undefined
おや? 何やら様子がおかしいですね。要素がundefined
になっちゃってます。実はc.prototype=[]
というのがまずいのです。正しくは以下のようにします。
class c{}
let a=256, p=c.prototype; // 配列を設定しない
for(;p[--a]=a;); //要素を格納
let b=new c;// instance生成
for(;a<256;)console.log(b[a++]," ")//0...255