あまりやらないであろう初期化法。不思議な事が起こります。演算後にpropertyが消滅します。
var a=new Number, b=new String;// 0, ""
a.a=b.a=1;
console.log(a.a,b.a);
a+=1;b+=1;
console.log(a.a,b.a)
変数がobject型からnumber型、string型になった事が原因です。
以下のように初期化すると、そもそもproperty設定が無効になります。
var a=0,b="";
a.a=b.a=1;
console.log(a.a,b.a);// undefined
a-=1;b-=1;
console.log(a.a,b.a)
new
を省略すると上記同様の結果となります。Number()
はnumber型、String()
はstring型となるためです。これらの型はprimitive値型と呼ばれ、以下の7種類あります。
- 文字列
- 数値
- 長整数
- 論理値
- undefined
- Symbol
- null
全てのprimitive値は不変であり、変更する事が出来ません。
裏技もどき
prototype
を書き替えると、propertyがあたかも最初から存在しているかのように見えます(prototype chain)。
Number.prototype.a=String.prototype.a=1
let a=0,b="";
a++;b++;
console.log(a.a, b.a);
console.log(0..a, "".a)
その状況でもpropertyの値は依然として書き替え不能。
Number.prototype.a=String.prototype.a=1;
let a=0,b="";
a.a=b.a=2;
console.log(a.a, b.a)// 1のまま
ところがArray等では挙動が異なります。以下の例では何が出力されるのでしょうか?
Array.prototype[0]=0;
let a=[];
a[0]++;
console.log(a[0])
undefinedでもNaNでもなく、なんと1となります。これはとってもおいしい挙動です。これを応用すると、あらかじめ任意の初期値を持つ疑似配列を高速かつ大量に召喚できます。以下の通り。
function NxN(){}//配列もどき
let a=256,p=NxN.prototype=Array(a);
for(;p[--a]=a;); //0...255の初期値設定
let A=[]; //2次元配列もどき
for(;a<256;)A[a++]=new NxN;
console.log(A[0].slice(0,100),'\n',A[0].slice(100,200))
昔は巨大配列の初期化に長時間を要したので、この技法が非常に有用でした。まあそんな変態的な事をしていた人はほとんどいないと思いますが…
new Number、new Stringの破壊力
話が少し脱線しますが、とんでもない速度差が出ます
//with new
{
let N=new Number(1),S=new String(1),t=new Date;
for(let c=1e7,d=0;c--;)d-=N;
console.log(new Date-t);
t=new Date;
for(let c=1e7,d=0;c--;)d-=S;
console.log(new Date-t
}
//without new
{
let N=Number(1),S=String(1),t=new Date;
for(let c=1e7,d=0;c--;)d-=N
console.log(new Date-t)
t=new Date;
for(let c=1e7,d=0;c--;)d-=S
console.log(new Date-t)
}
//primitive値
{
let N=1,S="1",t=new Date;
for(let c=1e7,d=0;c--;)d-=N
console.log(new Date-t)
t=new Date;
for(let c=1e7,d=0;c--;)d-=S
console.log(new Date-t)
}
new Number
で初期化された変数は、演算の時にvalueOf()
が召喚されやがるので、その分余計に時間がかかります。
new
なしの場合は残念ながらそこまで低速化できません。object型ではないためvalueOf()
が呼ばれないのです。
以下同様に微妙な振る舞いの違いに気を付けて下さい。まあ2行目と3行目は等価っぽいですが…。
var A0=[], R0=/(?:)/, O0={}, F0=function(){}, B0=false;
var A1=Array(), R1=RegExp(), O1=Object(), F1=Function(), B1=Boolean();
var A2=new Array, R2=new RegExp, O2=new Object, F2=new Function, B2=new Boolean;
以下は微妙な違いとやらの一例。aの値はDate.now()
同様の数値、bの値はNaNとなります。Date()
の戻り値がstring型であるため起こる悲劇です。
let a=+new Date, b=+Date()