JavaScriptは、プログラミング言語で期待されるほとんどの値を備えています。ブール値、数値、文字列、配列などです。すべての通常のJavaScriptの値にはプロパティがあります。[9] 各プロパティにはキー(または名前)と値があります。プロパティはレコードのフィールドのように考えることができます。ドット演算子(.)を使用してプロパティにアクセスします。
> var obj = {}; // create an empty object
> obj.foo = 123; // write property
123
> obj.foo // read property
123
> 'abc'.toUpperCase() // call method
'ABC'本章では、JavaScriptの型システムの概要を説明します。
JavaScriptには、ECMAScript言語仕様の第8章に従って、6つの型しかありません。
ECMAScript言語型は、ECMAScript言語を使用してECMAScriptプログラマが直接操作する値に対応します。ECMAScript言語型は次のとおりです。
- Undefined、Null
- Boolean、String、Number、および
- Object
したがって、コンストラクタはインスタンスを持つと言われている場合でも、技術的には新しい型を導入しません。
言語のセマンティクスと型システムのコンテキストでは、「静的」とは通常、「コンパイル時」または「プログラムを実行せずに」を意味し、「動的」とは「実行時」を意味します。
静的に型付けされた言語では、変数、パラメータ、オブジェクトのメンバー(JavaScriptではプロパティと呼びます)には、コンパイラがコンパイル時に認識する型があります。コンパイラはこの情報を使用して型チェックを実行し、コンパイル済みコードを最適化できます。
静的に型付けされた言語でも、変数には動的型(実行時の特定の時点での変数の値の型)もあります。動的型は静的型と異なる場合があります。(Javaの例)
Objectfoo="abc";
fooの静的型はObjectです。動的型はStringです。
JavaScriptは動的に型付けされています。変数の型は通常、コンパイル時には認識されません。
型情報がある場合、演算(関数の呼び出し、演算子の適用など)で使用される値が正しい型かどうかを確認できます。静的に型チェックされた言語は、この種のチェックをコンパイル時に実行し、動的に型チェックされた言語は実行時に実行します。言語は静的に型チェックされ、動的に型チェックされることもできます。チェックが失敗すると、通常はエラーまたは例外が発生します。
JavaScriptは非常に限られた種類の動的型チェックを実行します。
> var foo = null; > foo.prop TypeError: Cannot read property 'prop' of null
ただし、ほとんどの場合、何も起こらずに失敗するか、機能します。たとえば、存在しないプロパティにアクセスすると、undefinedという値が返されます。
> var bar = {};
> bar.prop
undefinedJavaScriptでは、型が合わない値を処理する主な方法は、型強制することです。型強制とは、暗黙的な型変換を意味します。ほとんどのオペランドは型強制されます。
> '3' * '4' 12
JavaScriptの組み込み型変換メカニズムは、Boolean、Number、String、Objectの型のみをサポートしています。あるコンストラクタのインスタンスを別のコンストラクタのインスタンスに変換する標準的な方法はありません。
厳密に型付けされたと弱く型付けされたという用語には、一般的に意味のある定義がありません。これらの用語は使用されますが、通常は誤って使用されています。静的に型付けされた、静的に型チェックされたなどを使用する方が良いでしょう。
JavaScriptはある程度恣意的な値の区別をします。
null、undefinedです。両者の主な違いは比較方法です。各オブジェクトは一意のアイデンティティを持ち、それ自体にのみ(厳密に)等しくなります。
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> var obj3 = obj1;
> obj3 === obj1
trueこれとは対照的に、同じ値をエンコードするすべてのプリミティブ値は同じと見なされます。
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
次の2つのセクションでは、プリミティブ値とオブジェクトを詳しく説明します。
true、false(第10章を参照)1736、1.351(第11章を参照)'abc'、"abc"(第12章を参照)undefined、null(undefinedとnullを参照)プリミティブには、次の特性があります。
「内容」が比較される
> 3 === 3 true > 'abc' === 'abc' true
プロパティは変更、追加、削除できません。
> var str = 'abc'; > str.length = 1; // try to change property `length` > str.length // ⇒ no effect 3 > str.foo = 3; // try to create property `foo` > str.foo // ⇒ no effect, unknown property undefined
(不明なプロパティを読み取ると、常にundefinedが返されます。)
プリミティブでないすべての値はオブジェクトです。最も一般的なオブジェクトの種類は次のとおりです。
プレーンオブジェクト(コンストラクタObject)はオブジェクトリテラルで作成できます(第17章を参照)
{firstName:'Jane',lastName:'Doe'}
上記のオブジェクトには2つのプロパティがあります。プロパティfirstNameの値は'Jane'、プロパティlastNameの値は'Doe'です。
配列(コンストラクタArray)は 配列リテラルで作成できます(第18章を参照)
['apple','banana','cherry']
上記の配列には、数値インデックスを使用してアクセスできる3つの要素があります。たとえば、'apple'のインデックスは0です。
正規表現(コンストラクタRegExp)は正規表現リテラルで作成できます(第19章を参照)
/^a+b+$/オブジェクトには次の特性があります。
アイデンティティが比較されます。すべてのオブジェクトには独自のアイデンティティがあります。
> ({} === {}) // two different empty objects
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true通常、プロパティを自由に変更、追加、削除できます(ドット演算子(.):固定キーによるプロパティへのアクセスを参照)
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123JavaScriptには、不足している情報を示す2つの「非値」、undefinedとnullがあります。
undefinedは「値なし」を意味します(プリミティブでもオブジェクトでもありません)。初期化されていない変数、不足しているパラメータ、不足しているプロパティには、その非値があります。また、明示的に何も返されていない場合、関数は暗黙的にそれを返します。nullは「オブジェクトなし」を意味します。オブジェクトが期待される場合(パラメータとして、オブジェクトのチェーンのメンバーとしてなど)に非値として使用されます。undefinedとnullは、プロパティアクセスによって例外が発生する唯一の値です。
> function returnFoo(x) { return x.foo }
> returnFoo(true)
undefined
> returnFoo(0)
undefined
> returnFoo(null)
TypeError: Cannot read property 'foo' of null
> returnFoo(undefined)
TypeError: Cannot read property 'foo' of undefinedundefinedは、存在しないことを示すメタ値として使用されることもあります。これとは対照的に、nullは空を示します。たとえば、JSONノードビジター(データの変換:ノードビジターによるを参照)は、
undefinedを返し、nullに設定するためにnullを返します。ここでは、undefinedとnullが発生するさまざまなシナリオを確認します。
初期化されていない変数はundefinedです。
> var foo; > foo undefined
不足しているパラメータはundefinedです。
> function f(x) { return x }
> f()
undefined存在しないプロパティを読み取ると、undefinedが返されます。
> var obj = {}; // empty object
> obj.foo
undefinedそして、関数は、明示的に何も返されていない場合、暗黙的にundefinedを返します。
> function f() {}
> f()
undefined
> function g() { return; }
> g()
undefined
nullは、プロトタイプチェーンの最後の要素です(オブジェクトのチェーン。 レイヤー2:オブジェクト間のプロトタイプ関係を参照)
> Object.getPrototypeOf(Object.prototype) null
nullは、文字列に正規表現が一致しなかった場合にRegExp.prototype.exec()によって返されます。
> /x/.exec('aaa')
null次のセクションでは、undefinedとnullを個別にチェックする方法、またはいずれかが存在するかどうかをチェックする方法を確認します。
厳密な等価演算子(===)は、undefinedをチェックするための標準的な方法です。
if(x===undefined)...
typeof演算子(typeof:プリミティブの分類)を使用してundefinedをチェックすることもできますが、通常は前述の方法を使用する必要があります。
ほとんどの関数では、undefinedまたはnullのいずれかを使用して欠損値を示すことができます。これら両方をチェックする1つの方法は、明示的な比較です。
// Does x have a value?if(x!==undefined&&x!==null){...}// Is x a non-value?if(x===undefined||x===null){...}
もう1つの方法は、undefinedとnullの両方がfalseとみなされるという事実を利用することです(Truthy and Falsy Valuesを参照)。
// Does x have a value (is it truthy)?if(x){...}// Is x falsy?if(!x){...}
false、0、NaN、および''もfalseとみなされます。
単一の非値が、undefinedとnullの両方の役割を果たすことができます。なぜJavaScriptにはそのような値が2つ存在するのでしょうか?その理由は歴史的なものです。
JavaScriptは、Javaのアプローチを採用し、値をプリミティブとオブジェクトに分割しました。また、Javaの「オブジェクトではない」値であるnullも使用しました。C(ただしJavaではない)で設定された前例に従って、nullは数値に強制変換されると0になります。
> Number(null) 0 > 5 + null 5
最初のバージョンのJavaScriptには例外処理がありませんでした。そのため、初期化されていない変数や欠損プロパティなどの例外的なケースは、値によって示す必要がありました。nullは良い選択肢だったでしょうが、Brendan Eichは当時2つのことを避けたいと考えていました。
その結果、Eichはundefinedを言語に追加の非値として追加しました。これはNaNに強制変換されます。
> Number(undefined) NaN > 5 + undefined NaN
undefinedはグローバルオブジェクトのプロパティです(したがってグローバル変数です。The Global Objectを参照)。ECMAScript 3では、undefinedを読み取る際には注意が必要でした。なぜなら、その値を誤って変更することが簡単だったからです。ECMAScript 5では、undefinedは読み取り専用であるため、それは必要ありません。
変更されたundefinedから保護するために、2つのテクニックが人気でした(それらは古いJavaScriptエンジンにも関連しています)。
グローバルundefined(間違った値を持っている可能性があります)をシャドウします。
(function(undefined){if(x===undefined)...// safe now}());// don’t hand in a parameter
前のコードでは、undefinedは関数の呼び出しによって値が提供されていないパラメーターであるため、正しい値を持つことが保証されています。
void 0と比較します。これは常に(正しい)undefinedです(The void Operatorを参照)。
if(x===void0)// always safe
3つのプリミティブ型boolean、number、およびstringには、対応するコンストラクタBoolean、Number、Stringがあります。それらのインスタンス(いわゆるラッパーオブジェクト)は、プリミティブ値をラップします。コンストラクタは2つの方法で使用できます。
コンストラクタとして、それらはラップするプリミティブ値と大きく互換性のないオブジェクトを作成します。
> typeof new String('abc')
'object'
> new String('abc') === 'abc'
false関数として、それらは値を対応するプリミティブ型に変換します(Functions for Converting to Boolean, Number, String, and Objectを参照)。これは推奨される変換方法です。
> String(123) '123'
ラッパーオブジェクトを避けることがベストプラクティスと考えられています。通常、それらは必要ありません。オブジェクトができることでプリミティブができないことは(変異するのを除いて)ありません。(これは、JavaScriptがプリミティブとオブジェクトの違いを継承したJavaとは異なります!)
プリミティブ値(例:'abc')は、new String('abc')などのラッパーインスタンスとは根本的に異なります。
> typeof 'abc' // a primitive value
'string'
> typeof new String('abc') // an object
'object'
> 'abc' instanceof String // never true for primitives
false
> 'abc' === new String('abc')
falseラッパーインスタンスはオブジェクトであり、JavaScriptではオブジェクトを比較する方法はありません。寛容な等価演算子==による比較もできません(Equality Operators: === Versus ==を参照)。
> var a = new String('abc');
> var b = new String('abc');
> a == b
falseラッパーオブジェクトを使用するユースケースが1つあります。プリミティブ値にプロパティを追加したい場合です。プリミティブをラップし、ラッパーオブジェクトにプロパティを追加します。値を操作する前に、それをアンラップする必要があります。
ラッパーコンストラクタを呼び出すことでプリミティブをラップします。
newBoolean(true)newNumber(123)newString('abc')
メソッドvalueOf()を呼び出すことでプリミティブをアンラップします。すべてのオブジェクトにはこのメソッドがあります(Conversion to Primitiveで説明)。
> new Boolean(true).valueOf()
true
> new Number(123).valueOf()
123
> new String('abc').valueOf()
'abc'ラッパーオブジェクトをプリミティブに適切に変換すると、数値と文字列は抽出されますが、ブール値は抽出されません。
> Boolean(new Boolean(false)) // does not unwrap
true
> Number(new Number(123)) // unwraps
123
> String(new String('abc')) // unwraps
'abc'その理由は、Converting to Booleanで説明されています。
プリミティブには独自のメソッドがなく、ラッパーから借用します。
>'abc'.charAt===String.prototype.charAttrue
ゆるいモードと厳格モードでは、この借用の処理が異なります。ゆるいモードでは、プリミティブは動的にラッパーに変換されます。
String.prototype.sloppyMethod=function(){console.log(typeofthis);// objectconsole.log(thisinstanceofString);// true};''.sloppyMethod();// call the above method
厳格モードでは、ラッパーのプロトタイプからのメソッドが透過的に使用されます。
String.prototype.strictMethod=function(){'use strict';console.log(typeofthis);// stringconsole.log(thisinstanceofString);// false};''.strictMethod();// call the above method
型強制とは、ある型の値を別の型の値に暗黙的に変換することです。JavaScriptのほとんどの演算子、関数、メソッドは、オペランドと引数を、必要とする型に強制変換します。たとえば、乗算演算子(*)のオペランドは数値に強制変換されます。
> '3' * '4' 12
別の例として、オペランドの1つが文字列の場合、プラス演算子(+)はもう一方を文字列に変換します。
> 3 + ' times' '3 times'
そのため、JavaScriptは値の型が間違っていることをめったに文句を言いません。たとえば、プログラムは通常、ユーザーが入力した数値であっても、オンラインフォームやGUIウィジェットからユーザー入力を文字列として受け取ります。数値としての文字列を数値として扱うと、警告は表示されず、予期しない結果になります。たとえば
varformData={width:'100'};// You think formData.width is a number// and get unexpected resultsvarw=formData.width;varouter=w+20;// You expect outer to be 120, but it’s notconsole.log(outer===120);// falseconsole.log(outer==='10020');// true
このような場合、早い段階で適切な型に変換する必要があります。
varw=Number(formData.width);
次の関数は、値をブール値、数値、文字列、またはオブジェクトに変換するための推奨される方法です。
Boolean()(Converting to Booleanを参照)値をブール値に変換します。次の値はfalseに変換されます。これらはいわゆる「falsy」値です。
undefined、nullfalse
0、NaN''
他のすべての値は「truthy」とみなされ、trueに変換されます(すべてのオブジェクトを含む!)。
Number()(Converting to Numberを参照)undefinedはNaNになります。nullは0になります。falseは0になり、trueは1になります。String()(Converting to Stringを参照)値を文字列に変換します。すべてのプリミティブに対して明らかな結果になります。たとえば、
> String(null) 'null' > String(123.45) '123.45' > String(false) 'false'
Object()(Converting Any Value to an Objectを参照)オブジェクトをそれ自身に、undefinedとnullを空のオブジェクトに、プリミティブをラップされたプリミティブに変換します。たとえば、
> var obj = { foo: 123 };
> Object(obj) === obj
true
> Object(undefined)
{}
> Object('abc') instanceof String
trueBoolean()、Number()、String()、およびObject()は関数として呼び出されます。通常、それらをコンストラクタとして使用することはありません。そうすると、それ自身のインスタンスを作成します(Wrapper Objects for Primitivesを参照)。
値を数値または文字列に変換するには、最初に任意のプリミティブ値に変換してから、最終的な型に変換します(Functions for Converting to Boolean, Number, String, and Objectで説明)。
ECMAScript仕様には、内部関数ToPrimitive()(JavaScriptからはアクセスできません)があり、この変換を実行します。ToPrimitive()を理解することで、オブジェクトが数値と文字列に変換される方法を構成できます。次のシグネチャがあります。
ToPrimitive(input,PreferredType?)
オプションのパラメーターPreferredTypeは、変換の最終的な型を示します。これは、ToPrimitive()の結果が数値に変換されるか文字列に変換されるかによって、NumberまたはStringのいずれかになります。
PreferredTypeがNumberの場合、次の手順を実行します。
inputがプリミティブの場合、それを返します(これ以上やることはありません)。inputはオブジェクトです。input.valueOf()を呼び出します。結果がプリミティブの場合、それを返します。input.toString()を呼び出します。結果がプリミティブの場合、それを返します。TypeErrorをスローします(inputをプリミティブに変換できないことを示します)。PreferredTypeがStringの場合、手順2と3が入れ替えられます。PreferredTypeは省略することもできます。日付の場合はString、それ以外の値の場合はNumberとみなされます。これは、演算子+と==がToPrimitive()を呼び出す方法です。
valueOf()のデフォルトの実装はthisを返し、toString()のデフォルトの実装は型情報を返します。
> var empty = {};
> empty.valueOf() === empty
true
> empty.toString()
'[object Object]'したがって、Number()はvalueOf()をスキップし、toString()の結果を数値に変換します。つまり、'[object Object]'をNaNに変換します。
> Number({})
NaN次のオブジェクトはvalueOf()をカスタマイズしており、Number()に影響を与えますが、String()には何も変更しません。
> var n = { valueOf: function () { return 123 } };
> Number(n)
123
> String(n)
'[object Object]'次のオブジェクトはtoString()をカスタマイズしています。その結果を数値に変換できるため、Number()は数値を返すことができます。
> var s = { toString: function () { return '7'; } };
> String(s)
'7'
> Number(s)
7