この章では、「基本JavaScript」について説明します。これは、私が選んだJavaScriptのサブセットの名前で、できる限り簡潔でありながら、生産性を維持できるように設計されています。JavaScriptの学習を始めたばかりの方は、言語の残りの部分に進む前に、しばらくの間このサブセットでプログラミングすることをお勧めします。そうすれば、すべてを一度に学ぶ必要がなく、混乱せずに済みます。
このセクションでは、JavaScriptがなぜこのようになっているのかを理解するのに役立つ、JavaScriptの背景について少し説明します。
ECMAScriptは、JavaScriptの公式名称です。 JavaScript(もともとはSunが、現在はOracleが所有)に商標があるため、新しい名前が必要になりました。 現在、Mozillaは、以前にライセンスを取得したため、公式にJavaScriptという名前を使用することを許可されている数少ない企業の1つです。 一般的な使用法では、これらの規則が適用されます。
JavaScriptの作成者であるBrendan Eichは、非常に迅速に言語を作成せざるを得ませんでした(さもなければ、より悪いテクノロジーがNetscapeに採用されていたでしょう)。彼はいくつかのプログラミング言語から借用しました。Java(構文、プリミティブ値とオブジェクト)、SchemeとAWK(ファーストクラス関数)、Self(プロトタイプ継承)、PerlとPython(文字列、配列、正規表現)です。
JavaScriptには、ECMAScript 3まで例外処理がありませんでした。そのため、言語が非常に頻繁に値を自動的に変換し、非常に頻繁に暗黙のうちに失敗します。最初は例外をスローできなかったからです。
一方、JavaScriptには癖があり、機能がかなり欠落しています(ブロックスコープの変数、モジュール、サブクラス化のサポートなど)。一方、これらの問題を回避できる強力な機能がいくつかあります。他の言語では、言語機能を学習します。JavaScriptでは、代わりにパターンを学習することがよくあります。
その影響を考えると、JavaScriptが関数型プログラミング(高階関数、組み込みのmap、reduceなど)とオブジェクト指向プログラミング(オブジェクト、継承)の混合であるプログラミングスタイルを可能にするのも不思議ではありません。
このセクションでは、JavaScriptの基本的な構文原則について説明します。
// Two slashes start single-line commentsvarx;// declaring a variablex=3+y;// assigning a value to the variable `x`foo(x,y);// calling function `foo` with parameters `x` and `y`obj.bar(3);// calling method `bar` of object `obj`// A conditional statementif(x===0){// Is `x` equal to zero?x=123;}// Defining function `baz` with parameters `a` and `b`functionbaz(a,b){returna+b;}
=)は、変数に値を代入するために使用されます。===)は、2つの値を比較するために使用されます(等価演算子を参照)。JavaScriptの構文を理解するには、主要な2つの構文カテゴリ、ステートメントと式があることを知っておく必要があります。
ステートメントは「何かを実行」します。プログラムは一連のステートメントです。以下は、変数fooを宣言(作成)するステートメントの例です。
varfoo;
式は値を生成します。式は、関数引数、代入の右辺などです。以下は式の例です。
3*7
ステートメントと式の区別は、JavaScriptにif-then-elseを実行する2つの異なる方法があることからもよくわかります。ステートメントとして:
varx;if(y>=0){x=y;}else{x=-y;}
または式として
varx=y>=0?y:-y;
後者は関数引数として使用できます(ただし前者は使用できません)
myFunction(y>=0?y:-y)
最後に、JavaScriptがステートメントを期待する場所であればどこでも、式を使用することもできます。たとえば
foo(7,1);
行全体がステートメント(いわゆる式ステートメント)ですが、関数呼び出しfoo(7, 1)は式です。
JavaScriptではセミコロンはオプションです。ただし、そうしないとJavaScriptがステートメントの終わりを誤って推測する可能性があるため、常にセミコロンを含めることをお勧めします。詳細については、「自動セミコロン挿入」で説明します。
セミコロンはステートメントを終了しますが、ブロックは終了しません。ブロックの後にセミコロンが表示されるケースが1つあります。関数式はブロックで終わる式です。 そのような式がステートメントの最後に来る場合は、セミコロンが続きます。
// Pattern: var _ = ___;varx=3*7;varf=function(){};// function expr. inside var decl.
varfoo;// declare variable `foo`
識別子は、JavaScriptでさまざまな構文上の役割を果たす名前です。 たとえば、変数の名前は識別子です。識別子は大文字と小文字が区別されます。
大まかに言うと、識別子の最初の文字は、任意のUnicode文字、ドル記号($)、またはアンダースコア(_)にすることができます。後続の文字は、さらに任意のUnicode数字にすることができます。したがって、次のものはすべて有効な識別子です。
arg0_tmp$elemπ
次の識別子は予約語です。これらは構文の一部であり、変数名(関数名やパラメーター名を含む)として使用することはできません。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
次の3つの識別子は予約語ではありませんが、予約語であるかのように扱う必要があります。
|
|
|
最後に、標準のグローバル変数の名前(第23章を参照)も避ける必要があります。 ローカル変数として使用しても何も壊れませんが、コードは依然として混乱します。
JavaScriptには、プログラミング言語に期待される多くの値があります。ブール値、数値、文字列、配列などです。JavaScriptのすべての値にはプロパティがあります。[1] 各プロパティには、キー(または名前)と値があります。プロパティはレコードのフィールドのように考えることができます。ドット(.)演算子を使用してプロパティを読み取ります。
value.propKey
たとえば、文字列'abc'にはlengthプロパティがあります。
> var str = 'abc'; > str.length 3
前述の例は、次のように記述することもできます。
> 'abc'.length 3
ドット演算子は、プロパティに値を代入するために使用することもできます。
> var obj = {}; // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123> 'hello'.toUpperCase() 'HELLO'
上記の例では、値'hello'に対してメソッドtoUpperCase()を呼び出しました。
JavaScriptでは、値の間でやや恣意的な区別が行われます。
null、undefinedです。2つの主な違いは、それらがどのように比較されるかです。各オブジェクトには固有の識別子があり、自分自身と(厳密に)等しいだけです。
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
true対照的に、同じ値をエンコードするすべてのプリミティブ値は同じと見なされます。
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
次の2つのセクションでは、プリミティブ値とオブジェクトについて詳しく説明します。
以下は、すべてのプリミティブ値(または略してプリミティブ)です。
true、false(「ブール値」を参照)1736、1.351(「数値」を参照)'abc'、"abc"(「文字列」を参照)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が返されます。)
すべての非プリミティブ値はオブジェクトです。最も一般的な種類のオブジェクトは次のとおりです。
プレーンオブジェクト。これらはオブジェクトリテラルで作成できます(「単一のオブジェクト」を参照)。
{firstName:'Jane',lastName:'Doe'}
上記のオブジェクトには、2つのプロパティがあります。プロパティfirstNameの値は'Jane'で、プロパティlastNameの値は'Doe'です。
配列。これらは配列リテラルで作成できます(「配列」を参照)。
['apple','banana','cherry']
上記の配列には、数値インデックスを使用してアクセスできる3つの要素があります。たとえば、'apple'のインデックスは0です。
正規表現。これらは正規表現リテラルで作成できます(「正規表現」を参照)。
/^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
123ほとんどのプログラミング言語には、欠落した情報を表す値があります。 JavaScriptには、そのような「非値」が2つあります。それは、undefined と null です。
undefined は「値がない」ことを意味します。初期化されていない変数は undefined です。
> var foo; > foo undefined
欠落したパラメータは undefined です。
> function f(x) { return x }
> f()
undefined存在しないプロパティを読み取ると、undefined が返されます。
> var obj = {}; // empty object
> obj.foo
undefinednull は「オブジェクトがない」ことを意味します。オブジェクトが期待される場合(パラメータ、オブジェクトの連鎖の最後など)に、非値として使用されます。undefined と null にはプロパティがなく、toString() などの標準メソッドさえありません。
関数は通常、undefined または null のいずれかを介して欠落した値を示すことができます。 明示的なチェックによって同じことができます。
if(x===undefined||x===null){...}
また、undefined と null の両方が false と見なされるという事実を利用することもできます。
if(!x){...}
false、0、NaN、および '' も false と見なされます(「Truthy and Falsy」を参照)。
値を分類するための演算子が2つあります。typeof は主にプリミティブ値に使用され、instanceof はオブジェクトに使用されます。
typeof は次のようになります。
typeofvalue
value の「型」を記述する文字列を返します。以下に例をいくつか示します。
> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'次の表は、typeof のすべての結果を示しています。
| オペランド | 結果 |
|
|
|
|
真偽値 |
|
数値 |
|
文字列値 |
|
関数 |
|
その他すべての通常の値 |
|
(エンジンが作成した値) | JavaScriptエンジンは、 |
typeof null が 'object' を返すのはバグであり、既存のコードを破壊するため、修正できません。これは、null がオブジェクトであることを意味しません。
instanceof は次のようになります。
valueinstanceofConstr
value がコンストラクタ Constr によって作成されたオブジェクトの場合、true を返します(「コンストラクタ:オブジェクトのファクトリ」を参照)。以下に例をいくつか示します。
> var b = new Bar(); // object created by constructor Bar
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object // Array is a subconstructor of Object
true
> undefined instanceof Object
false
> null instanceof Object
falseプリミティブな真偽値型 は、true と false の値で構成されます。次の演算子は真偽値を生成します。
&& (And)、|| (Or)! (Not)比較演算子
===、!==、==、!=>、>=、<、<=JavaScriptが真偽値を期待する場合(例えば、ifステートメントの条件)、任意の値を使用できます。それはtrueまたはfalseとして解釈されます。次の値はfalseとして解釈されます。
undefined、nullfalse0、NaN''その他すべての値(すべてのオブジェクトを含む!)は、trueと見なされます。falseとして解釈される値はfalsyと呼ばれ、trueとして解釈される値はtruthyと呼ばれます。関数として呼び出されるBoolean()は、そのパラメータをブール値に変換します。それを使用して、値がどのように解釈されるかをテストできます。
> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
trueJavaScriptの2項論理演算子はショートサーキットです。つまり、最初のオペランドが結果を決定するのに十分な場合、2番目のオペランドは評価されません。 たとえば、次の式では、関数 foo() は決して呼び出されません。
false&&foo()true||foo()
さらに、2項論理演算子は、オペランドのいずれかを返します。これは真偽値である場合もそうでない場合もあります。truthyかどうかのチェックは、どちらを返すかを判断するために使用されます。
&&)最初のオペランドがfalsyの場合、それを返します。それ以外の場合は、2番目のオペランドを返します。
> NaN && 'abc' NaN > 123 && 'abc' 'abc'
||)最初のオペランドがtruthyの場合、それを返します。それ以外の場合は、2番目のオペランドを返します。
> 'abc' || 123 'abc' > '' || 123 123
JavaScriptには、2種類の等価性があります。
== および !==== および !==通常の等価性は、多くの値を等しいと見なします(詳細は、「通常の(寛容な)等価(==、!=)」で説明します)。バグを隠す可能性があるため、常に厳密な等価性を使用することをお勧めします。
JavaScriptのすべての数値は浮動小数点数です。
> 1 === 1.0 true
特殊な数値には、次のものがあります。
NaN(「数値ではない」)エラー値
> Number('xyz') // 'xyz' can’t be converted to a number
NaNInfinity
ほとんどもエラー値です。
> 3 / 0 Infinity > Math.pow(2, 1024) // number too large Infinity
Infinity は、他のどの数値よりも大きい(NaN を除く)。同様に、-Infinity は他のどの数値よりも小さい(NaN を除く)。これにより、これらの数値はデフォルト値として役立ちます(例えば、最小値または最大値を検索する場合)。
JavaScriptには、次の算術演算子があります(「算術演算子」を参照)。
number1 + number2number1 - number2number1 * number2number1 / number2number1 % number2++variable、variable++--variable、variable---value+valueグローバルオブジェクトMath(「Math」を参照)は、関数を介して、より多くの算術演算を提供します。
JavaScriptには、ビット単位の演算用の演算子もあります(例えば、ビット単位のAnd。「ビット単位演算子」を参照)。
文字列は、文字列リテラルを介して直接作成できます。 これらのリテラルは、単一引用符または二重引用符で区切られます。バックスラッシュ(\)は文字をエスケープし、いくつかの制御文字を生成します。以下に例をいくつか示します。
'abc'"abc"'Did she say "Hello"?'"Did she say \"Hello\"?"'That\'s nice!'"That's nice!"'Line 1\nLine 2'// newline'Backlash: \\'
単一文字は角括弧でアクセスされます。
> var str = 'abc'; > str[1] 'b'
プロパティ length は、文字列の文字数をカウントします。
> 'abc'.length 3
すべてのプリミティブと同様に、文字列は不変です。既存の文字列を変更する場合は、新しい文字列を作成する必要があります。
文字列は、プラス(+)演算子を介して連結されます。これは、オペランドのいずれかが文字列の場合、他のオペランドを文字列に変換します。
> var messageCount = 3; > 'You have ' + messageCount + ' messages' 'You have 3 messages'
複数のステップで文字列を連結するには、+= 演算子を使用します。
> var str = ''; > str += 'Multiple '; > str += 'pieces '; > str += 'are concatenated.'; > str 'Multiple pieces are concatenated.'
文字列には多くの便利なメソッドがあります(「String Prototype Methods」を参照)。以下に例をいくつか示します。
> 'abc'.slice(1) // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'
> '\t xyz '.trim() // trim whitespace
'xyz'
> 'mjölnir'.toUpperCase()
'MJÖLNIR'
> 'abc'.indexOf('b') // find a string
1
> 'abc'.indexOf('x')
-1JavaScriptの条件文とループは、次のセクションで紹介します。
if ステートメントには、then 句があり、オプションの else 句があり、ブール条件に応じて実行されます。
if(myvar===0){// then}if(myvar===0){// then}else{// else}if(myvar===0){// then}elseif(myvar===1){// else-if}elseif(myvar===2){// else-if}else{// else}
常に中括弧(0個以上のステートメントのブロックを示します)を使用することをお勧めします。 ただし、句が単一のステートメントのみである場合は、そうする必要はありません(制御フロー ステートメント for および while にも同じことが当てはまります)。
if(x<0)return-x;
以下はswitchステートメントです。fruitの値は、どのcaseが実行されるかを決定します。
switch(fruit){case'banana':// ...break;case'apple':// ...break;default:// all other cases// ...}
case の後の「オペランド」には、任意の式を指定できます。これは switch のパラメータと === で比較されます。
for ループは、次の形式を持ちます。
for(⟦«init»⟧;⟦«condition»⟧;⟦«post_iteration»⟧)«statement»
init はループの最初に実行されます。condition は各ループの反復前にチェックされ、false になるとループは終了します。post_iteration は各ループの反復後に実行されます。
この例では、配列 arr のすべての要素をコンソールに出力します。
for(vari=0;i<arr.length;i++){console.log(arr[i]);}
while ループは、条件が真である間、その本体をループし続けます。
// Same as for loop above:vari=0;while(i<arr.length){console.log(arr[i]);i++;}
do-while ループは、条件が真である間、その本体をループし続けます。条件は本体の後にあるため、本体は常に少なくとも 1 回は実行されます。
do{// ...}while(condition);
すべてのループにおいて:
break はループを終了します。continue は新しいループの反復を開始します。関数を定義する 1 つの方法は、関数宣言 を使用することです。
functionadd(param1,param2){returnparam1+param2;}
上記のコードは、2 つのパラメータ param1 と param2 を持ち、両方のパラメータの合計を返す関数 add を定義します。これがその関数の呼び出し方です。
> add(6, 1)
7
> add('a', 'b')
'ab'add() を定義する別の方法は、関数式 を変数 add に割り当てることです。
varadd=function(param1,param2){returnparam1+param2;};
関数式は値を生成するため、関数を他の関数の引数として直接渡すために使用できます。
someOtherFunction(function(p1,p2){...});
関数宣言は巻き上げ られます。つまり、現在のスコープの先頭に全体が移動されます。これにより、後で宣言された関数を参照できます。
functionfoo(){bar();// OK, bar is hoistedfunctionbar(){...}}
var 宣言も巻き上げられますが(「変数は巻き上げられる」を参照)、それらによる割り当ては巻き上げられないことに注意してください。
functionfoo(){bar();// Not OK, bar is still undefinedvarbar=function(){// ...};}
JavaScript では、任意の数の引数で任意の関数を呼び出すことができます。言語は決して文句を言いません。ただし、特殊変数 arguments を介してすべてのパラメータを利用できるようにします。arguments は配列のように見えますが、配列メソッドは一切持っていません。
> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0] // read element at index 0
'a'JavaScript でパラメータが多すぎる場合、または少なすぎる場合にどのように処理されるかを探るために、次の関数を使用してみましょう(関数 toArray() は「arguments を配列に変換する」に示されています)。
functionf(x,y){console.log(x,y);returntoArray(arguments);}
追加のパラメータは(arguments を除いて)無視されます。
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]不足しているパラメータは値 undefined を取得します。
> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]以下は、パラメータにデフォルト値を割り当てるための一般的なパターンです。
functionpair(x,y){x=x||0;// (1)y=y||0;return[x,y];}
(1)行目では、|| 演算子は、x が truthy (null、undefined などではない) であれば x を返します。それ以外の場合は、2 番目のオペランドを返します。
> pair() [ 0, 0 ] > pair(3) [ 3, 0 ] > pair(3, 5) [ 3, 5 ]
もし引数の数(特定の数のパラメータ)を強制したい場合は、arguments.length を確認できます。
functionpair(x,y){if(arguments.length!==2){thrownewError('Need exactly 2 arguments');}...}
arguments は配列ではなく、単なる 配列のような オブジェクトです(「配列のようなオブジェクトとジェネリックメソッド」を参照)。length プロパティを持ち、角括弧でインデックスを介してその要素にアクセスできます。ただし、要素を削除したり、配列メソッドを呼び出したりすることはできません。したがって、arguments を配列に変換する必要がある場合があります。以下の関数がそれを行います(「配列のようなオブジェクトとジェネリックメソッド」で説明されています)。
functiontoArray(arrayLikeObject){returnArray.prototype.slice.call(arrayLikeObject);}
例外を処理する最も一般的な方法は(「第 14 章」を参照)次のとおりです。
functiongetPerson(id){if(id<0){thrownewError('ID must not be negative: '+id);}return{id:id};// normally: retrieved from database}functiongetPersons(ids){varresult=[];ids.forEach(function(id){try{varperson=getPerson(id);result.push(person);}catch(exception){console.log(exception);}});returnresult;}
try 句は重要なコードを囲み、catch 句は try 句の中で例外がスローされた場合に実行されます。上記のコードを使用すると:
> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]厳格モード(「厳格モード」を参照)では、より多くの警告が有効になり、JavaScript がよりクリーンな言語になります(非厳格モードは「ずさんモード」と呼ばれることがあります)。それを有効にするには、JavaScript ファイルまたは <script> タグの最初に次の行を入力します。
'use strict';
関数ごとに厳格モードを有効にすることもできます。
functionfunctionInStrictMode(){'use strict';}
JavaScript では、変数を使用する前に var で宣言します。
> var x; > x undefined > y ReferenceError: y is not defined
1 つの var ステートメントで複数の変数を宣言および初期化できます。
varx=1,y=2,z=3;
ただし、変数ごとに 1 つのステートメントを使用することをお勧めします(理由は「構文」で説明されています)。したがって、上記のステートメントを次のように書き直します。
varx=1;vary=2;varz=3;
巻き上げのため(「変数は巻き上げられる」を参照)、通常は関数の最初に変数を宣言するのが最善です。
変数のスコープは常に、現在のブロックとは対照的に、完全な関数です。例:
functionfoo(){varx=-512;if(x<0){// (1)vartmp=-x;...}console.log(tmp);// 512}
変数 tmp が (1) 行目から始まるブロックに限定されず、関数の最後まで存在することがわかります。
各変数宣言は巻き上げ られます。つまり、宣言は関数の先頭に移動されますが、そこで行われる割り当てはそのまま残ります。例として、次の関数の (1) 行目の変数宣言について考えてみましょう。
functionfoo(){console.log(tmp);// undefinedif(false){vartmp=3;// (1)}}
内部的には、上記の関数は次のように実行されます。
functionfoo(){vartmp;// hoisted declarationconsole.log(tmp);if(false){tmp=3;// assignment stays put}}
各関数は、作成されたスコープから離れた後でも、その周囲の関数の変数への接続を維持します。例:
functioncreateIncrementor(start){returnfunction(){// (1)start++;returnstart;}}
(1)行目から始まる関数は、作成されたコンテキストから離れますが、start のライブバージョンへの接続を維持します。
> var inc = createIncrementor(5); > inc() 6 > inc() 7 > inc() 8
クロージャ は、関数とその周囲のスコープの変数への接続の組み合わせです。したがって、createIncrementor() が返すものはクロージャです。
グローバルにならないように変数を防ぐなど、新しい変数スコープを導入したい場合があります。JavaScript では、ブロックを使用してこれを行うことはできません。関数を使用する必要があります。ただし、ブロックのような方法で関数を使用するためのパターンがあります。これは IIFE (即時実行関数式、発音は「イフィー」) と呼ばれます。
(function(){// open IIFEvartmp=...;// not a global variable}());// close IIFE
上記の例は、コメントを除いて、正確に示されているとおりに入力してください。IIFE は、定義した直後に呼び出される関数式です。関数内には、新しいスコープが存在し、tmp がグローバルになるのを防ぎます。IIFE の詳細については、「IIFE を介した新しいスコープの導入」を参照してください。
クロージャは、外側の変数への接続を維持しますが、これは場合によっては望ましくないものです。
varresult=[];for(vari=0;i<5;i++){result.push(function(){returni});// (1)}console.log(result[1]());// 5 (not 1)console.log(result[3]());// 5 (not 3)
(1) 行目で返される値は常に i の現在の値であり、関数が作成されたときの値ではありません。ループが完了すると、i は値 5 を持ち、これが配列内のすべての関数がその値を返す理由です。(1) 行目の関数に i の現在の値のスナップショットを受け取らせたい場合は、IIFE を使用できます。
for(vari=0;i<5;i++){(function(){vari2=i;// copy current iresult.push(function(){returni2});}());}
このセクションでは、JavaScript の 2 つの基本的なオブジェクト指向メカニズムである、単一のオブジェクトと、(他の言語のクラスに似たオブジェクトのファクトリである)コンストラクタ について説明します。
すべての値と同様に、オブジェクトにはプロパティがあります。実際、オブジェクトをプロパティの集合と考えることができます。各プロパティは (キー、値) のペアです。キーは文字列であり、値は任意の JavaScript 値です。
JavaScript では、オブジェクトリテラル を使用してプレーンオブジェクトを直接作成できます。
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
上記のオブジェクトには、プロパティ name と describe があります。プロパティの読み取り(「取得」)と書き込み(「設定」)ができます。
> jane.name // get 'Jane' > jane.name = 'John'; // set > jane.newProperty = 'abc'; // property created automatically
describe のような関数値のプロパティは、メソッド と呼ばれます。メソッドは、それらを呼び出すために使用されたオブジェクトを参照するために this を使用します。
> jane.describe() // call method 'Person named John' > jane.name = 'Jane'; > jane.describe() 'Person named Jane'
in 演算子は、プロパティが存在するかどうかを確認します。
> 'newProperty' in jane true > 'foo' in jane false
存在しないプロパティを読み取ると、値 undefined が得られます。したがって、前の 2 つのチェックは、次のようにも実行できます。[2]
> jane.newProperty !== undefined true > jane.foo !== undefined false
delete 演算子はプロパティを削除します。
> delete jane.newProperty true > 'newProperty' in jane false
プロパティキーには任意の文字列を指定できます。これまで、オブジェクトリテラルとドット演算子の後のプロパティキーを見てきました。ただし、それらが識別子である場合にのみ、そのように使用できます(「識別子と変数名」を参照)。他の文字列をキーとして使用する場合は、オブジェクトリテラルでそれらを引用符で囲み、角括弧を使用してプロパティを取得および設定する必要があります。
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;角括弧を使用すると、プロパティのキーを計算することもできます。
> var obj = { hello: 'world' };
> var x = 'hello';
> obj[x]
'world'
> obj['hel'+'lo']
'world'メソッドを抽出すると、オブジェクトとの接続が失われます。関数自体はもうメソッドではなく、this の値は (厳格モードでは) undefined になります。
例として、前のオブジェクト jane に戻りましょう。
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
jane からメソッド describe を抽出し、それを変数 func に入れて呼び出したいとします。ただし、これはうまくいきません。
> var func = jane.describe; > func() TypeError: Cannot read property 'name' of undefined
解決策は、すべての関数が持つメソッド bind() を使用することです。これは、this が常に指定された値を持つ新しい関数を作成します。
> var func2 = jane.describe.bind(jane); > func2() 'Person named Jane'
すべての関数は、独自の特別な変数 this を持っています。メソッド内に関数をネストすると、関数の内部からメソッドの this にアクセスできなくなるため、これは不便です。以下は、配列を反復処理するために関数で forEach を呼び出す例です。
varjane={name:'Jane',friends:['Tarzan','Cheeta'],logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){// `this` is undefined hereconsole.log(this.name+' says hi to '+friend);});}}
logHiToFriends を呼び出すとエラーが発生します。
> jane.logHiToFriends() TypeError: Cannot read property 'name' of undefined
これを修正する2つの方法を見てみましょう。まず、this を別の変数に保存できます。
logHiToFriends:function(){'use strict';varthat=this;this.friends.forEach(function(friend){console.log(that.name+' says hi to '+friend);});}
または、forEach には this の値を指定できる2番目のパラメータがあります。
logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){console.log(this.name+' says hi to '+friend);},this);}
関数式は、JavaScript の関数呼び出しで引数としてよく使用されます。これらの関数式の中から this を参照する場合は、常に注意してください。
これまで、JavaScript オブジェクトは、他の言語のマップ/辞書リテラルのように見える JavaScript のオブジェクトリテラルが示すように、文字列から値への単なるマップであると考えていたかもしれません。しかし、JavaScript オブジェクトは、真にオブジェクト指向な機能である継承もサポートしています。このセクションでは、JavaScript の継承がどのように機能するかを完全に説明するわけではありませんが、入門するための簡単なパターンを示します。詳細を知りたい場合は、第17章を参照してください。
関数は、「本物の」関数およびメソッドであることに加えて、JavaScript では別の役割を果たします。それは、new 演算子を介して呼び出された場合、コンストラクタ、つまりオブジェクトのファクトリになることです。したがって、コンストラクタは他の言語のクラスに大まかに似たものです。慣例として、コンストラクタの名前は大文字で始まります。例:
// Set up instance datafunctionPoint(x,y){this.x=x;this.y=y;}// MethodsPoint.prototype.dist=function(){returnMath.sqrt(this.x*this.x+this.y*this.y);};
コンストラクタには2つの部分があることがわかります。まず、関数 Point はインスタンスデータを設定します。次に、プロパティ Point.prototype にはメソッドを持つオブジェクトが含まれます。前者のデータは各インスタンスに固有であり、後者のデータはすべてのインスタンスで共有されます。
Point を使用するには、new 演算子を介して呼び出します。
> var p = new Point(3, 5); > p.x 3 > p.dist() 5.830951894845301
p は Point のインスタンスです。
> p instanceof Point true
配列は、ゼロから始まる整数インデックスを介してアクセスできる要素のシーケンスです。
配列リテラルは、配列を作成するのに便利です。
> var arr = [ 'a', 'b', 'c' ];
前の配列には、文字列 'a'、'b'、および 'c' の3つの要素があります。これらは整数インデックスを介してアクセスできます。
> arr[0] 'a' > arr[0] = 'x'; > arr [ 'x', 'b', 'c' ]
length プロパティは、配列に含まれる要素の数を示します。これを使用して要素を追加したり、要素を削除したりできます。
> var arr = ['a', 'b']; > arr.length 2 > arr[arr.length] = 'c'; > arr [ 'a', 'b', 'c' ] > arr.length 3 > arr.length = 1; > arr [ 'a' ]
in 演算子は配列にも機能します。
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr // is there an element at index 1? true > 5 in arr // is there an element at index 5? false
配列はオブジェクトであり、したがってオブジェクトプロパティを持つことができることに注意してください。
> var arr = []; > arr.foo = 123; > arr.foo 123
配列には多くのメソッドがあります(配列プロトタイプメソッドを参照)。以下にいくつかの例を示します。
> var arr = [ 'a', 'b', 'c' ];
> arr.slice(1, 2) // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]
> arr.push('x') // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]
> arr.pop() // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]
> arr.shift() // remove first element
'a'
> arr
[ 'b', 'c' ]
> arr.unshift('x') // prepend an element
3
> arr
[ 'x', 'b', 'c' ]
> arr.indexOf('b') // find the index of an element
1
> arr.indexOf('y')
-1
> arr.join('-') // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'要素を反復処理するための配列メソッドがいくつかあります(反復処理(非破壊的)を参照)。最も重要な2つは forEach と map です。
forEach は配列を反復処理し、現在の要素とそのインデックスを関数に渡します。
['a','b','c'].forEach(function(elem,index){// (1)console.log(index+'. '+elem);});
上記のコードは次の出力を生成します。
0. a 1. b 2. c
(1)行の関数は、引数を自由に無視できることに注意してください。たとえば、パラメータ elem のみを持つことができます。
map は、既存の配列の各要素に関数を適用して、新しい配列を作成します。
> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]JavaScript には正規表現の組み込みサポートがあります(第19章では、チュートリアルを参照し、それらがどのように機能するかを詳しく説明しています)。それらはスラッシュで区切られます。
/^abc$//[A-Za-z0-9]+/
> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]返された配列には、インデックス0で完全一致、インデックス1で最初のグループのキャプチャなどが含まれます。このメソッドを繰り返し呼び出してすべての一致を取得する方法があります(RegExp.prototype.exec:キャプチャグループで説明)。
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]') '[a] [bbb]'
replace の最初のパラメータは、/g フラグを持つ正規表現である必要があります。そうでない場合、最初の出現のみが置換されます。置換を計算するために関数を使用する方法もあります(String.prototype.replace:検索と置換で説明)。
Math (第21章を参照) は、算術関数を持つオブジェクトです。以下にいくつかの例を示します。
> Math.abs(-2) 2 > Math.pow(3, 2) // 3 to the power of 2 9 > Math.max(2, -1, 5) 5 > Math.round(1.9) 2 > Math.PI // pre-defined constant for π 3.141592653589793 > Math.cos(Math.PI) // compute the cosine for 180° -1
JavaScript の標準ライブラリは比較的質素ですが、他にも使用できるものがたくさんあります。