From 7b90ddeb227a0977113bbb6ab0393d95159b500c Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Thu, 6 May 2021 02:13:30 +0900 Subject: Web/JavaScript/A_re-introduction_to_JavaScript を更新 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 2021/04/28 時点の英語版に同期 - 原語併記マクロを削除 --- .../a_re-introduction_to_javascript/index.html | 608 +++++++++++---------- 1 file changed, 321 insertions(+), 287 deletions(-) (limited to 'files/ja/web/javascript/a_re-introduction_to_javascript/index.html') diff --git a/files/ja/web/javascript/a_re-introduction_to_javascript/index.html b/files/ja/web/javascript/a_re-introduction_to_javascript/index.html index 78801c66eb..490134dc97 100644 --- a/files/ja/web/javascript/a_re-introduction_to_javascript/index.html +++ b/files/ja/web/javascript/a_re-introduction_to_javascript/index.html @@ -13,205 +13,228 @@ translation_of: Web/JavaScript/A_re-introduction_to_JavaScript ---
{{jsSidebar}}
-

なぜ {{原語併記("「再」入門", "re-introduction")}} なのか? なぜなら {{Glossary("JavaScript")}} は世界で最も誤解されたプログラミング言語としてよく知られているためです。しばしばおもちゃだと馬鹿にされながら、しかしその人を欺くような単純さの下に、強力な言語機能が隠されているのです。JavaScript は数々の高い注目を集めるアプリケーションで使用され、この技術の深い知識がどんなウェブやモバイルの開発者にとっても重要なスキルであることが示されました。

+

なぜ「再」入門なのでしょうか。それは、 JavaScript が誤解されていることで有名だからです。しばしばおもちゃだと馬鹿にされながら、しかしその嘘のような単純さの下に、強力な言語機能が隠されているのです。 JavaScript はとても多くの著名なアプリケーションで使用されるようになっており、この技術の深い知識が、あらゆるウェブやモバイルの開発者にとって重要なスキルであることを示しています。

-

この言語の歴史について理解することから始めるのが役立つでしょう。JavaScript は 1995 年、Netscape の技術者{{原語併記("ブレンダン・アイク", "Brendan Eich")}} によって創られ、1996 年初頭に Netscape 2 で初めてリリースされました。当初は LiveScript と呼ばれる予定でしたが、Sun Microsystems の Java 言語の人気にあやかろうという不運なるマーケティング上の決定により改名されました ― 2 つの言語が共通点をほとんど持っていないにもかかわらず。それ以来、このことは未だに混同の元となっています。

+

まずは、この言語の歴史を振り返ってみましょう。 JavaScript は、 1995 年に Netscape 社のエンジニアだった Brendan Eich 氏によって開発されました。 JavaScript は、 1996 年の初めに Netscape 2 とともにリリースされました。当初は LiveScript と呼ばれる予定でしたが、 Sun Microsystems の Java 言語の人気にあやかろうとしたマーケティング上の不運な決定により、両者にはほとんど共通点がないにもかかわらず改名されました。このことは、いまだに混同の元となっています。

-

Microsoft はその数か月後、IE3 とともに JScript というほとんど互換性のある言語をリリースしました。さらに数か月後、Netscape はこの言語をヨーロッパの標準化団体 Ecma International に提出し、その結果 1997 年に {{Glossary("ECMAScript")}} という標準の第 1 版が生まれました。この標準は重要なアップデートを受けて 1999 年に ECMAScript 第 3 版となり、その後しばらくほとんど安定してきました。言語の複雑化に関する政治的な隔たりから、第 4 版は放棄されたものの、その多くのパーツは 2009 年 12 月に発行された新しい ECMAScript 第 5 版の基礎となりました。そして、標準の第 6 版が 2015 年 6 月に発行されました。

+

その数か月後、 Microsoft は Internet Explorer 3 とともに JScript をリリースしました。これはほぼ互換性のある JavaScript の互換品でした。さらに数か月後、 Netscape はこの言語をヨーロッパの標準化団体である Ecma International に提出し、その結果、同年に ECMAScript という標準の第 1 版が生まれました。この標準は 1999 年に ECMAScript 第 3 版として大幅に更新され、それ以降はほぼ安定しています。第 4 版は、言語の複雑化に関する政治的な見解の相違から放棄されました。第 4 版の多くの部分は、 2009 年 12 月に発行された新しい ECMAScript 第 5 版と、 2015 年 6 月に発行された第 6 版の基礎になりました。

-
なじみ深いため、ここからは ECMAScript を "JavaScript" と呼びます。
+
+

注: より馴染んでいるため、ここからは ECMAScript を "JavaScript" と呼ぶことにします。

+
-

大部分のプログラミング言語と違って、JavaScript という言語には入出力の概念がありません。この言語はあるホスト環境でのスクリプト言語として実行されるよう設計されており、外部の世界とコミュニケーションするための機構はそのホスト環境が提供するものとしているのです。もっとも一般的なホスト環境はブラウザーですが、JavaScript のインタープリターは Adobe Acrobat や Photoshop、SVG 画像、Yahoo! ウィジェットエンジン、さらには Node.js といったサーバーサイド環境、オープンソースの Apache CouchDB といった NoSQL データベース、組み込みコンピューター、GNOME (GNU/Linux オペレーティングシステムでもっとも人気がある GUI 環境のひとつ) のようなデスクトップ環境など、さまざまなところでみられます。

+

大部分のプログラミング言語と違って、 JavaScript という言語には入出力の概念がありません。この言語はあるホスト環境でのスクリプト言語として実行されるよう設計されており、外部の世界とコミュニケーションするための機構はそのホスト環境が提供するものとしているのです。もっとも一般的なホスト環境はブラウザーですが、JavaScript のインタープリターは Adobe Acrobat や Photoshop、SVG 画像、Yahoo! ウィジェットエンジン、さらには Node.js といったサーバーサイド環境、オープンソースの Apache CouchDB のような NoSQL データベース、組み込みコンピューター、GNOME (GNU/Linux オペレーティングシステムでもっとも人気がある GUI 環境のひとつ) のようなデスクトップ環境など、さまざまなところでみられます。

-

概要

+

概要

-

JavaScript はマルチパラダイムの動的言語であり、型や演算子、標準組み込みオブジェクト、メソッドがあります。その構文は Java や C 言語に由来するので、それらの言語の多くの構造が JavaScript にも同様に適用できます。JavaScript は、クラスではなくオブジェクトプロトタイプによるオブジェクト指向プログラミングをサポートします (詳しくは プロトタイプの継承 や ES2015 の classes をご覧ください)。また、JavaScript は関数型プログラミングもサポートします。関数もオブジェクトであり、関数に実行可能なコードを持たせて、他のオブジェクトと同じように受け渡しさせることができます。

+

JavaScript はマルチパラダイムの動的言語であり、型や演算子、標準組み込みオブジェクト、メソッドがあります。その構文は Java や C 言語に由来するので、それらの言語の多くの構造が JavaScript にも同様に適用できます。JavaScript は、クラスの代わりにオブジェクトプロトタイプによるオブジェクト指向プログラミングをサポートします (詳しくは プロトタイプの継承 や ES2015 の classes をご覧ください)。また、JavaScript は関数型プログラミングもサポートします。関数もオブジェクトであり、関数に実行可能なコードを持たせて、他のオブジェクトと同じように受け渡しすることができます。

-

まずはあらゆる言語の構成要素、「型」を見ることから始めましょう。JavaScript のプログラムは値を操作し、それらの値はすべて型に属しています。JavaScript の型は:

+

まずはあらゆる言語の構成要素、「型」を見ることから始めましょう。 JavaScript のプログラムは値を操作し、それらの値はすべて型に属しています。JavaScript の型は次の通りです。

-

...ああ、あと {{jsxref("undefined")}} と {{jsxref("null")}}、これらはちょっと変わっています。そして {{jsxref("Array", "Array (配列)")}}、これは特殊なオブジェクトの一種。さらに {{jsxref("Date")}} と {{jsxref("RegExp", "RegExp (正規表現)")}}、これらは自由に使えるオブジェクトです。あと技術的に正確なことを言うと、関数は単にオブジェクトの特殊な型です。したがってこの型の図はより正確にはこうなります:

+

...ああ、あと undefinednull、これらはちょっと変わっています。そして {{jsxref("Array")}} (配列)、これは特殊なオブジェクトの一種。さらに {{jsxref("Date")}} (日付) と {{jsxref("RegExp")}} (正規表現)、これらは自由に使えるオブジェクトです。あと技術的に正確なことを言うと、関数もオブジェクトの特殊な型です。したがって、この型の図はより正確にはこうなります。

さらにいくつかの組み込み {{jsxref("Error")}} 型もあります。しかし、最初の図のままでいく方が物事はとても簡単になるでしょうから、当面は最初の図で説明します。

-

数値

+

数値

+ +

ECMAScript には 2 つの組み込み数値型があります。 NumberBigInt です。

-

JavaScript における数値は、仕様によると「{{原語併記("倍精度 64 ビットフォーマット IEEE 754 値", "double-precision 64-bit format IEEE 754 values")}}」です。 JavaScript には整数に当たるものがない ( {{jsxref("BigInt")}} を除く) ので、少し気を付ける必要があります。以下の例を見てください。

+

Number 型はIEEE 754 倍精度 64 ビットバイナリー値 (-(253 − 1) と 253 − 1 の間の数) です。そして、この記事や他の MDN の記事で「整数」という場合は、ふつう Number 値を使った整数の表現を意味しています。しかし、このような Number 値は本物の整数ではないので、少し注意が必要です。以下の例を見てください。

-
console.log(3 / 2);             // 1.5, not 1
+
console.log(3 / 2);             // 1.5, 1 ではない
 console.log(Math.floor(3 / 2)); // 1
-

したがって、見かけ上の整数は実際には暗黙的に浮動小数点数となります。

+

したがって、見かけ上は整数でも、実際には暗黙的に浮動小数点数となります。

-

また、以下のようなことに注意してください:

+

また、次のようなことに注意してください。

-
0.1 + 0.2 == 0.30000000000000004;
+
0.1 + 0.2 == 0.30000000000000004;
 
-

実のところ、整数値は 32 ビット int 型として扱われます。また一部の実装では、32 ビット整数値ではなく Number で有効な命令の実行を求められるまでの間、32 ビット整数値として保存します。これはビット演算を行う際に重要なことです。

+

実のところ、整数値は 32 ビット整数として扱われます。また一部の実装では、32 ビット整数値ではなく Number で有効な命令の実行を求められるまでの間、32 ビット整数値として保存します。これはビット演算を行う際に重要なことです。

-

足し算、引き算、モジュロ (剰余) など、標準的な 算術演算 がサポートされています。さらに、これは先ほど言及し忘れたのですが、より高度な数学関数や定数を扱う {{jsxref("Math")}} という組み込みオブジェクトもあります:

+

標準的な算術演算子に対応しています。例えば、足し算、引き算、モジュロ (剰余) などです。さらに、これは前に言及していなかったのですが、より高度な数学関数や定数を扱う {{jsxref("Math")}} という組み込みオブジェクトもあります。

-
Math.sin(3.5);
+
Math.sin(3.5);
 var circumference = 2 * Math.PI * r;
 
-

あなたは組み込みの {{jsxref("Global_Objects/parseInt", "parseInt()")}} 関数を使うことで、文字列を整数に変換することができます。この関数は省略可能な第 2 引数として変換の基数を取りますが、あなたは常にこの引数を与えるべきです:

+

組み込みの {{jsxref("Global_Objects/parseInt", "parseInt()")}} 関数を使うことで、文字列を整数に変換することができます。この関数は省略可能な第 2 引数として変換の基数を取りますが、この引数を常に指定してください。

-
parseInt('123', 10); // 123
+
parseInt('123', 10); // 123
 parseInt('010', 10); // 10
 
-

古いブラウザーでは "0" から始まる文字列を 8 進数 (基数 8) とみなしますが、これは 2013 年以降のブラウザーにはあてはまりません。文字列の形式を理解していなければ、古いブラウザーであなたは驚くような結果を得ることでしょう:

+

古いブラウザーでは、 "0" から始まる文字列を 8 進数 (基数 8) とみなしますが、これは 2013 年以降のブラウザーにはあてはまりません。文字列の書式を理解していなければ、古いブラウザーは驚くような結果になることがあります。

-
parseInt('010');  //  8
+
parseInt('010');  //  8
 parseInt('0x10'); // 16
 
-

この例では、{{jsxref("Global_Objects/parseInt", "parseInt()")}} が最初の文字列を先頭の 0 から 8 進数として、また 2 番目の文字列を先頭の "0x" から 16 進数として扱ったことがわかります。16 進数表記は現在も有効です。8 進数のみ削除されました。

+

この例では、{{jsxref("Global_Objects/parseInt", "parseInt()")}} が最初の文字列を先頭の 0 から 8 進数として、また 2 番目の文字列を先頭の "0x" から 16 進数として扱われたことがわかります。16 進数表記は現在も有効です。8 進数のみ削除されました。

-

もし 2 進数を整数に変換したいなら、単純に基数を変えましょう:

+

もし 2 進数を整数に変換したいなら、単純に基数を変えましょう。

-
parseInt('11', 2); // 3
+
parseInt('11', 2); // 3
 
-

同様に浮動小数点数への変換を行う、組み込みの {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 関数があります。こちらは {{jsxref("Global_Objects/parseInt", "parseInt()")}} と異なり、基数は常に 10 が用いられます。

+

同様に浮動小数点数への変換を行う、組み込みの {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 関数があります。 {{jsxref("Global_Objects/parseInt", "parseInt()")}} と異なり、 parseFloat() では基数は常に 10 が用いられます。

-

また、単項演算子 + を使って値を数値に変換することもできます:

+

また、単項演算子 + を使って値を数値に変換することもできます。

-
+ '42';   // 42
+
+ '42';   // 42
 + '010';  // 10
 + '0x10'; // 16
 
-

もし文字列が数でない場合、 {{jsxref("NaN")}} (非数、"Not a Number" の略) と呼ばれる特別な値が返ります:

+

もし文字列が数値でない場合、 {{jsxref("NaN")}} (非数、"Not a Number" の略) と呼ばれる特別な値が返されます。

-
parseInt('hello', 10); // NaN
+
parseInt('hello', 10); // NaN
 
-

NaN には毒性があります: これを入力してどの算術演算に与えても、その結果は同様に NaN になるのです:

+

NaN には毒性があります。これを算術演算に入力として与えても、その結果は同様に NaN になるのです。

-
NaN + 5; // NaN
+
NaN + 5; // NaN
 
-

組み込みの {{jsxref("Global_Objects/isNaN", "isNaN()")}} 関数を使えば、NaN であるかを検査することができます:

+

NaN であるかどうかを確実に確認するには、組み込みの {{jsxref("Number.isNaN", "Number.isNaN()")}} 関数を使用してください。この関数は、名前が示す通りの動作をします。

-
isNaN(NaN); // true
+
Number.isNaN(NaN); // true
+Number.isNaN('hello'); // false
+Number.isNaN('1'); // false
+Number.isNaN(undefined); // false
+Number.isNaN({}); // false
+Number.isNaN([1]) // false
+Number.isNaN([1,2]) // false
 
-

JavaScript はまた、特別な値 {{jsxref("Infinity")}} と -Infinity を持っています:

+

しかし、 NaN であるかどうかをグローバル関数の {{jsxref("Global_Objects/isNaN", "isNaN()")}} で確認しないでください。これは直感的でない動作をします。

+ +
isNaN('hello'); // true
+isNaN('1'); // false
+isNaN(undefined); // true
+isNaN({}); // true
+isNaN([1]) // false
+isNaN([1,2]) // true
+
-
 1 / 0; //  Infinity
+

JavaScript はまた、特別な値 {{jsxref("Infinity")}} と -Infinity を持っています。

+ +
 1 / 0; //  Infinity
 -1 / 0; // -Infinity
 
-

組み込みの {{jsxref("Global_Objects/isFinite", "isFinite()")}} 関数を使えば、Infinity-InfinityNaN であるかを検査することができます:

+

Infinity-InfinityNaN であるかをどうかは、組み込みの {{jsxref("Global_Objects/isFinite", "isFinite()")}} 関数を使用して確認することができます。

-
isFinite(1 / 0); // false
+
isFinite(1 / 0); // false
 isFinite(-Infinity); // false
 isFinite(NaN); // false
 
-
{{jsxref("Global_Objects/parseInt", "parseInt()")}} および {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 関数は文字列を、規定の数値書式に該当しない文字が現れるまで解析し、その箇所までの数値を返します。一方、"+" 演算子は適切でない文字を含む文字列を NaN に変換します。ご自身でコンソールを用いて、文字列 "10.2abc" をそれぞれの方法で解析させるとその違いがよくわかるでしょう。
+

注: {{jsxref("Global_Objects/parseInt", "parseInt()")}} および {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 関数は文字列を、規定の数値書式に該当しない文字が現れるまで解釈し、その箇所までの数値を返します。一方、"+" 演算子は適切でない文字を含む文字列を NaN に変換します。コンソールから、文字列 "10.2abc" をそれぞれの方法で解釈させるとその違いがよくわかるでしょう。

-

文字列

+

文字列

-

JavaScript における文字列は、Unicode 文字 の{{原語併記("連なったもの", "sequences")}} です。これは{{原語併記("国際化", "internationalization")}} に対処しなければならない誰もが歓迎するニュースでしょう。より正確に言えば UTF-16 コード単位の連なったものであり、つまりそれぞれのコード単位は 16 ビットの整数で表されます。また、それぞれの Unicode 文字は 1 または 2 個のコード単位で表します。

+

JavaScript における文字列は、Unicode 文字の並びです。これは、国際化を扱う必要があった誰もが歓迎する知らせでしょう。より正確に言えば UTF-16 コード単位の連なったものであり、つまりそれぞれのコード単位は 16 ビットの整数で表されます。また、それぞれの Unicode 文字は 1 または 2 個のコード単位で表します。

-

もし単一文字を表したいなら、単純に 1 文字で構成された文字列を使います。

+

もし単一の文字を表したいなら、単純に 1 文字で構成された文字列を使います。

-

文字列の長さ (コード単位) を知るには、その文字列の length プロパティにアクセスしましょう:

+

文字列の長さ (コード単位) を知るには、その文字列の length プロパティにアクセスしましょう。

-
'hello'.length; // 5
+
'hello'.length; // 5
 
-

JavaScript のオブジェクトとの初めての接触です! 私、文字列も {{jsxref("Object", "オブジェクト", "", 1)}} であると言いましたっけ? 文字列はまた、文字列の操作や文字列に関する情報へのアクセスを可能にする {{jsxref("String", "メソッド", "#Methods", 1)}} も持っています:

+

ここで初めて JavaScript のオブジェクトに触れました。文字列を{{jsxref("Object", "オブジェクト", "", 1)}}のように使うことができることには、既に触れましたっけ?文字列にも{{jsxref("String", "メソッド", "#instance_methods", 1)}}があり、文字列を操作したり、文字列に関する情報にアクセスしたりすることができます。

-
'hello'.charAt(0); // "h"
-'hello, world'.replace('hello', 'goodbye'); // "goodbye, world"
+
'hello'.charAt(0); // "h"
+'hello, world'.replace('world', 'mars'); // "hello, mars"
 'hello'.toUpperCase(); // "HELLO"
 
-

その他の型

+

その他の型

-

JavaScript は {{jsxref("null")}} と {{jsxref("undefined")}} を区別します。null は、意図的に「値がない」ということを指し示す値です (また、null キーワードによってのみアクセスできます)。対して undefined とは、初期化されていない値 ― すなわち、「まだ値が代入されていない」ということを指し示す undefined 型の値です。変数については後で話しますが、JavaScript では値を代入しないで変数を宣言することができるのです。そのようにした場合、その変数の型は undefined です。実際は、undefined は定数です。

+

JavaScript は {{jsxref("null")}} と {{jsxref("undefined")}} を区別します。 null は、意図的に値がないことを示す値です (また、null キーワードによってのみアクセスできます)。対して undefined とは、初期化されていない値 — すなわち、まだ値が代入されていないことを示す undefined 型の値です。変数については後で話しますが、 JavaScript では値を代入しないで変数を宣言することができるのです。そのようにした場合、その変数の型は undefined です。実際は、undefined は定数です。

-

JavaScript は truefalse (これらはともにキーワードです) を取りうる値とする真偽値型を持っています。どんな値でも以下のルールに基づいて真偽値に変換できます:

+

JavaScript は truefalse (これらはともにキーワードです) を取りうる値とする論理型を持っています。どんな値でも以下の規則に基づいて論理値に変換できます。

    -
  1. false0、空文字列 ("")、NaNnull、および undefined は、すべて false になる
  2. -
  3. その他の値はすべて true になる
  4. +
  5. false0、空文字列 ("")、NaNnullundefined は、すべて false になる。
  6. +
  7. その他の値はすべて true になる。
-

Boolean() 関数を使うことで、明示的にこの変換を行うことができます:

+

Boolean() 関数を使うことで、明示的にこの変換を行うことができます。

-
Boolean('');  // false
+
Boolean('');  // false
 Boolean(234); // true
 
-

しかしながら、これはほとんど必要ありません。なぜなら JavaScript は、if 文 (下記参照) の中といった真偽値が期待されるときに、無言でこの変換を行うからです。このような理由から、私たちは時々、真偽値に変換されるとき true または false になる値という意味で、それぞれ "true values" または "false values" と言うことがあります。あるいは、そのような値はそれぞれ "truthy" または "falsy" と呼ばれます。

+

しかし、これはほとんど必要ありません。 JavaScript は、 if 文の中 (下記参照) のように論理値が期待される場面で、暗黙にこの変換を行うからです。このため、ときどき "true values" または "false values" と言うことがありますが、これは論理値に変換されるときにそれぞれ true または false になる値という意味です。他にも、これらの値はそれぞれ "truthy" または "falsy" と呼ばれることがあります。

-

&& (論理 AND) や || (論理 OR)、! (論理 NOT) などの真偽値演算がサポートされています (下記参照)。

+

&& (論理 AND) や || (論理 OR)、! (論理 NOT) などの論理演算がサポートされています (下記参照)。

-

変数

+

変数

-

JavaScript における新しい変数は letconstvar の 3 つのキーワードのいずれかを使用して宣言します。

+

JavaScript では、新しい変数を宣言するのに letconstvar の 3 つのキーワードのいずれかを使用します。

-

let は、ブロックレベルの変数を宣言できます。宣言した変数は、変数を包含する関数ブロックから使用できます。

+

let は、ブロックレベルの変数を宣言できます。宣言した変数は、変数を包含するブロックから使用できます。

-
let a;
+
let a;
 let name = 'Simon';
 
-

以下の例は、let で宣言した変数のスコープを示しています:

+

以下の例は、let で宣言した変数のスコープを示しています。

-
// ここでは myLetVariable が *見えません*
+
// ここでは myLetVariable が *見えません*
 
 for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
   // ここだけで myLetVariable が見えます
 }
 
 // ここでは myLetVariable が *見えません*
+
 
-

const は、変更を意図しない変数を宣言できます。宣言した変数は、変数を宣言した関数ブロックから使用できます。

+

const は、値を変更することを意図しない変数を宣言することができます。宣言した変数は、変数を宣言したブロックから使用できます。

-
const Pi = 3.14; // 変数 Pi を設定
-Pi = 1; // 不変の変数は変更できないため、エラーが発生します
+
const Pi = 3.14; // 変数 Pi を設定
+Pi = 1; // 定数の変数は変更できないため、エラーが発生します
-

var は、もっとも一般的な宣言キーワードです。こちらは、他の 2 つのキーワードのような制約がありません。これは、伝統的に JavaScript で変数を宣言する唯一の方法であったためです。var キーワードで宣言した変数は、変数を宣言した関数ブロックから使用できます。

+

var は、もっとも一般的な宣言キーワードです。こちらは、他の 2 つのキーワードのような制約がありません。これは、伝統的に JavaScript で変数を宣言する唯一の方法であったためです。 var キーワードで宣言した変数は、変数を宣言した関数から使用できます。

-
var a;
+
var a;
 var name = 'Simon';
-

以下の例は、var で宣言した変数のスコープを示しています:

+

以下の例は、 var で宣言した変数のスコープを示しています。

-
// ここでは myVarVariable が *見えます*
+
// ここでは myVarVariable が *見えます*
 
 for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
   // myVarVariable は関数全体で見えます
@@ -220,67 +243,67 @@ for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
 // ここでは myVarVariable が *見えます*
 
-

もし値を代入しないで変数を宣言すると、その型は undefined になります。

+

値を代入せずに変数を宣言すると、その型は undefined になります。

-

Java など他の言語との重要な相違点は、JavaScript ではブロックがスコープを持たず、関数のみがスコープを持つことです。よって、変数が複合文内 (例えば if 制御構造の内部) で var を用いて定義された場合でも、その変数は関数全体でアクセス可能です。ただし ECMAScript 2015 より、let および const 宣言でブロックスコープの変数を作成できます。

+

Java など他の言語との重要な相違点は、 JavaScript ではブロックがスコープを持たないことです。関数のみがスコープを持ちます。よって、変数が複合文内 (例えば if 制御構造の内部) で var を用いて定義された場合でも、その変数は関数全体でアクセス可能です。ただし ECMAScript 2015 より、let および const 宣言でブロックスコープの変数を作成することができます。

-

演算子

+

演算子

-

JavaScript の算術演算子は、+-*/、そして剰余演算子の % (モジュロと同じです) です。値は = を使って代入されます。また +=-= のような複合代入文もあります。これらは x = x 演算子 y と展開できるものです。

+

JavaScript の算術演算子は、+-*/、そして剰余演算子の % (モジュロと同じです) です。値は = を使って代入されます。また +=-= のような複合代入文もあります。これらは x = x 演算子 y と展開できるものです。

-
x += 5;
+
x += 5;
 x = x + 5;
 

++-- を使ってインクリメントやデクリメントできます。これらは前置あるいは後置演算子として使うことができます。

-

+ 演算子 は文字列の結合もします:

+

+ 演算子は文字列の結合も行います。

-
'hello' + ' world'; // "hello world"
+
'hello' + ' world'; // "hello world"
 
-

文字列を数字 (や他の値) に足すと、すべてのものはまず最初に文字列に変換されます。このことはミスを誘うかもしれません:

+

文字列を数字 (や他の値) に足すと、すべてのものが最初に文字列に変換されます。このことはミスを誘うかもしれません。

-
'3' + 4 + 5;  // "345"
+
'3' + 4 + 5;  // "345"
  3 + 4 + '5'; // "75"
 

空文字列を足すのは、何かを文字列に変換する便利な方法です。

-

JavaScript における 比較 は、<><=>= を使ってすることができます。これらは文字列と数値のどちらでも機能します。等価性はちょっと明快ではありません。二重イコール演算子は、異なる型を与えると{{原語併記("型強制", "type coercion")}} を行います。これは時に面白い結果を返します:

+

JavaScript における 比較 は、<><=>= を使って行うことができます。これらは文字列と数値のどちらでも機能します。等価性はちょっと明快ではありません。二重イコール演算子は、異なる型を与えると型強制を行います。これは時に面白い結果を返します。

-
123 == '123'; // true
+
123 == '123'; // true
 1 == true; // true
 
-

型強制を防いで厳密な比較を行うようにする場合は、三重イコール演算子を使います:

+

型強制を防いで厳密な比較を行うようにする場合は、三重イコール演算子を使います。

-
123 === '123'; // false
+
123 === '123'; // false
 1 === true;    // false
 

!=!== 演算子もあります。

-

JavaScript は ビット演算子 も持っています。使いたいなら、ちゃんとありますよ。

+

JavaScript は ビット演算子 も持っています。使いたいなら、ちゃんとありますよ。

-

制御構造

+

制御構造

-

JavaScript は C 言語ファミリーの他の言語とよく似た制御構造セットを持っています。条件文は ifelse でサポートされています。必要ならこれらを連鎖させることもできます:

+

JavaScript は C 言語ファミリーの他の言語とよく似た制御構造セットを持っています。条件文は ifelse でサポートされています。必要ならこれらを連鎖させることもできます。

-
var name = 'kittens';
-if (name == 'puppies') {
+
var name = 'kittens';
+if (name === 'puppies') {
   name += ' woof';
-} else if (name == 'kittens') {
+} else if (name === 'kittens') {
   name += ' meow';
 } else {
   name += '!';
 }
-name == 'kittens meow';
+name === 'kittens meow';
 
-

JavaScript は while ループと do-while ループを持っています。1 つ目は普通のループ処理に適しており、2 つ目はループの本体が少なくとも 1 回は実行されるようにしたいときのループです:

+

JavaScript は while ループと do-while ループを持っています。1 つ目は普通のループ処理に適しており、2 つ目はループの本体が少なくとも 1 回は実行されるようにしたいときのループです。

-
while (true) {
+
while (true) {
   // 無限ループ!
 }
 
@@ -290,45 +313,45 @@ do {
 } while (inputIsNotValid(input));
 
-

JavaScript の for ループは C や Java のそれと同じです。これはループの制御情報を 1 行で与えることができます。

+

JavaScript の for ループは C や Java のそれと同じです。これはループの制御情報を 1 行で与えることができます。

-
for (var i = 0; i < 5; i++) {
+
for (var i = 0; i < 5; i++) {
   // 5 回実行されます
 }
 
-

JavaScript にはこの他に、特徴的な for ループが 2 つあります。ひとつは for...of です:

+

JavaScript にはこの他に、特徴的な for ループが 2 つあります。ひとつは for...of です。

-
for (let value of array) {
+
for (let value of array) {
   // 値に関する処理
 }
 
-

もうひとつは for...in です:

+

もうひとつは for...in です。

-
for (let property in object) {
+
for (let property in object) {
   // オブジェクトのプロパティに関する処理
 }
 
-

&&|| 演算子は、1 つ目のオペランドによって 2 つ目のオペランドを評価するか否かが決まる{{原語併記("短絡論理", "short-circuit logic")}} を用いています。これはあるオブジェクトの属性にアクセスする前に、それが null オブジェクトかをチェックするのに便利です:

+

&&|| 演算子は、1 つ目のオペランドによって 2 つ目のオペランドを評価するか否かが決まる短絡論理 (short-circuit logic) を採用しています。これはあるオブジェクトの属性にアクセスする前に、それが null オブジェクトかをチェックするのに便利です。

-
var name = o && o.getName();
+
var name = o && o.getName();
 
-

あるいは値の格納にも便利です (falsy な値は無効であるとき):

+

あるいは値の格納にも便利です (falsy な値は無効であるとき)。

-
var name = cachedName || (cachedName = getName());
+
var name = cachedName || (cachedName = getName());
 
-

JavaScript はワンライン条件文のための三項演算子を持っています:

+

JavaScript は条件による式のための三項演算子を持っています。

-
var allowed = (age > 18) ? 'yes' : 'no';
+
var allowed = (age > 18) ? 'yes' : 'no';
 
-

switch 文はある数値や文字列を元にした複数分岐に使われます:

+

switch 文はある数値や文字列を元にした複数分岐に使われます。

-
switch (action) {
+
switch (action) {
   case 'draw':
     drawIt();
     break;
@@ -340,10 +363,10 @@ do {
 }
 
-

もし break 文を入れなければ、処理は次の段階へ{{原語併記("フォールスルー", "fall through")}} します。この動作が望むものであることは非常にまれでしょう ― 事実、もしそれが本当に意図するものならば、デバッグの補助として故意のフォールスルーをコメントで明確にラベリングするだけの価値があるでしょう:

+

もし break 文を入れなければ、処理は次の段階へ「落下」 (fall through) します。この動作が望むものであることはまれでしょう — 実際にそれが意図的な落下であれば、デバッグの助けになるようにコメントでラベル付しておくといいでしょう。

-
switch (a) {
-  case 1: // フォールスルー
+
switch (a) {
+  case 1: // 落下
   case 2:
     eatIt();
     break;
@@ -352,9 +375,9 @@ do {
 }
 
-

default 節は省略できます。必要ならば、switch 部と case 部のどちらにも式を置くことができます。比較はこれら 2 つの間で === 演算子を使って行われます:

+

default 節は省略できます。必要ならば、switch 部と case のどちらにも式を置くことができます。比較はこれら 2 つの間で === 演算子を使って行われます。

-
switch (1 + 3) {
+
switch (1 + 3) {
   case 2 + 2:
     yay();
     break;
@@ -363,9 +386,9 @@ do {
 }
 
-

オブジェクト

+

オブジェクト

-

JavaScript のオブジェクトは、名前と値のペアの単純なコレクションであると考えることができます。これは以下のものに似ています:

+

JavaScript のオブジェクトは、名前と値のペアの単純なコレクションであると考えることができます。これは以下のものに似ています。

  • Python の辞書型
  • @@ -375,27 +398,27 @@ do {
  • PHP の連想配列
-

このデータ構造が幅広く使われているという事実は、この構造の万能性の証拠でしょう。JavaScript において (コアデータ型を除いた) すべてのものはオブジェクトなので、どんな JavaScript プログラムも自然と非常に多くのハッシュテーブルのルックアップ (検索) を伴います。良いことにそれがとても速いのです!

+

このデータ構造がこれほど広く使われているのは、その汎用性の高さを物語っています。 JavaScript では (コアデータ型を除いて) すべてのものはオブジェクトなので、どんな JavaScript のプログラムでは当然、ハッシュテーブルの参照が大量に行われます。ハッシュテーブルが高速なのは良いことですね。

「名前」部は JavaScript における文字列であるのに対し、値は JavaScript のどんな値でも ― さらなるオブジェクトでも ― 構いません。この仕様が任意の複雑なデータ構造を作ることを可能にしています。

-

空のオブジェクトを生成する 2 つの基本的な方法があります:

+

空のオブジェクトを生成する 2 つの基本的な方法があります。

-
var obj = new Object();
+
var obj = new Object();
 
-

そして:

+

そして、

-
var obj = {};
+
var obj = {};
 

これらは意味的に等価です。2 つ目はオブジェクトリテラル構文と呼ばれ、こちらの方がより便利です。オブジェクトリテラル構文は JSON 書式の中核でもあり、こちらを採用するべきです。

-

オブジェクトリテラル構文はオブジェクトをそっくりそのまま初期化するのに使えます:

+

オブジェクトリテラル構文はオブジェクト全体を初期化するのに使えます。

-
var obj = {
+
var obj = {
   name: 'Carrot',
-  for: 'Max', // 'for' は予約語であり、代わりに '_for' を使用します
+  _for: 'Max', // 'for' は予約語であるため、代わりに '_for' を使用します
   details: {
     color: 'orange',
     size: 12
@@ -403,15 +426,15 @@ do {
 };
 
-

属性へのアクセスは同時に連鎖させることができます:

+

属性へのアクセスは同時に連鎖させることができます。

-
obj.details.color; // orange
+
obj.details.color; // orange
 obj['details']['size']; // 12
 
-

以下の例ではオブジェクトのプロトタイプである Person と、プロトタイプのインスタンスである you を生成しています。

+

以下の例ではオブジェクトのプロトタイプ (Person) と、プロトタイプのインスタンス (you) を生成しています。

-
function Person(name, age) {
+
function Person(name, age) {
   this.name = name;
   this.age = age;
 }
@@ -421,94 +444,97 @@ var you = new Person('You', 24);
 // "You" という名前で、年齢が 24 歳の新たな Person を作成しました
 
-

一度作ってしまえば、オブジェクトのプロパティには下記の 2 つの方法のいずれかで再びアクセスすることができます:

+

一度作ってしまえば、オブジェクトのプロパティには下記の 2 つの方法のいずれかで再びアクセスすることができます。

-
// ドット記法
+
// ドット記法
 obj.name = 'Simon';
 var name = obj.name;
 

そして...

-
// 括弧記法
-obj['name'] = 'Simon';
-var name = obj['name'];
-// 変数をキー定義に使用できる
-var user = prompt('what is your key?')
-obj[user] = prompt('what is its value?')
+
// ブラケット記法
+obj['name'] = 'Simon';
+var name = obj['name'];
+// 変数をキー定義に使用できる
+var user = prompt('what is your key?')
+obj[user] = prompt('what is its value?')
+
-

これらもまた意味的に等価です。2 つ目の方法はプロパティの名前を文字列として与えるという利点があり、つまりその名前を実行時に計算できることを意味します。ただ、この方法を用いると JavaScript エンジンや minifier による最適化が適用されなくなります。またこの方法は、予約語 と同じ名前のプロパティを設定したり取得したりするのに使うことができます:

+

これらもまた意味的に等価です。2 つ目の方法はプロパティの名前を文字列として与えるという利点があり、つまりその名前を実行時に計算できることを意味します。ただ、この方法を用いると JavaScript エンジンや minifier による最適化が適用されなくなります。またこの方法は、予約語 と同じ名前のプロパティを設定したり取得したりするのに使うことができます。

-
obj.for = 'Simon'; // 構文エラー。'for' が予約語であるため
+
obj.for = 'Simon'; // 構文エラー。'for' が予約語であるため
 obj['for'] = 'Simon'; // うまく動きます
 
-
ECMAScript 第 5 版より、予約語をオブジェクトのプロパティ名として"そのまま"使用できます。つまりオブジェクトリテラルの定義時に引用符で"括る"必要ありません。ES5 の仕様を確認してください。
+
+

注: ECMAScript 第 5 版より、予約語をオブジェクトのプロパティ名として「そのまま」使用できます。つまりオブジェクトリテラルの定義時に引用符で「括る」必要はありません。 ES5 の仕様を確認してください。

+
-

オブジェクトやプロトタイプについて、詳しくは Object.prototype をご覧ください。オブジェクトプロトタイプやオブジェクトプロトタイプチェーンの説明は、継承とプロトタイプチェーン をご覧ください。

+

オブジェクトやプロトタイプについて、詳しくは Object.prototype をご覧ください。オブジェクトプロトタイプやオブジェクトプロトタイプチェーンの説明は、継承とプロトタイプチェーン をご覧ください。

-

ECMAScript 2015 より、オブジェクトのキーが括弧記法を使った変数で定義できるようになりました。{[phoneType]: 12345} が単に var userPhone = {}; userPhone[phoneType] = 12345 の代わりにできます。

+

ECMAScript 2015 より、オブジェクトのキーがブラケット記法を使った変数で定義できるようになりました。 {[phoneType]: 12345}var userPhone = {}; userPhone[phoneType] = 12345 の代わりにできます。

-

配列

+

配列

-

JavaScript における配列は、実はオブジェクトの特殊型です。普通のオブジェクトとほとんど同じように働きます (数字のプロパティは当然 [] の構文でのみアクセスできます) が、しかし配列は 'length' という魔法のプロパティを持っています。これは常に配列の一番大きな添字より 1 大きい値を取ります。

+

JavaScript における配列は、実はオブジェクトの特殊型です。普通のオブジェクトとほとんど同じように働きます (数値のプロパティは当然 [] の構文でのみアクセスできます) が、しかし配列は 'length' という魔法のプロパティを持っています。これは常に配列の一番大きな添字より 1 大きい値を取ります。

-

配列を生成する方法のひとつは以下の通り:

+

配列を生成する方法のひとつは以下の通りです。

-
var a = new Array();
+
var a = new Array();
 a[0] = 'dog';
 a[1] = 'cat';
 a[2] = 'hen';
 a.length; // 3
 
-

より便利な書き方は配列リテラルを使うことです:

+

より便利な書き方は配列リテラルを使うことです。

-
var a = ['dog', 'cat', 'hen'];
+
var a = ['dog', 'cat', 'hen'];
 a.length; // 3
 
-

array.length は必ずしも配列中の要素の数ではないことに注意してください。以下の例を考えてみましょう:

+

array.length は必ずしも配列中の要素の数ではないことに注意してください。以下の例を考えてみましょう。

-
var a = ['dog', 'cat', 'hen'];
+
var a = ['dog', 'cat', 'hen'];
 a[100] = 'fox';
 a.length; // 101
 

思い出してください ― 配列の長さは一番大きな添字より 1 大きい値です。

-

もし存在しない配列の添字を要求すると、undefined が得られます:

+

もし存在しない配列の添字を要求すると、undefined が得られます。

-
typeof a[90]; // undefined
+
typeof a[90]; // undefined
 
-

上記の []length を考慮に入れれば、以下の for ループを使って配列を繰り返すことができます:

+

上記の []length を考慮に入れれば、以下の for ループを使って配列を反復処理することができます。

-
for (var i = 0; i < a.length; i++) {
+
for (var i = 0; i < a.length; i++) {
   // a[i] について何かする
 }
 
-

ES2015では配列のような iterable オブジェクト用に、より正確な for...of ループが導入されました:

+

ES2015 では配列のような反復可能オブジェクト向けに、より正確な for...of ループが導入されました。

-
for (const currentValue of a) {
-  // currentValue(現在の値)で何かをする
-}
+
for (const currentValue of a) {
+  // currentValue (現在の値) で何かをする
+}
-

for...in ループを使用して配列を繰り返すすることもできます。ただし、もし誰かが Array.prototype に新しいプロパティを追加していたら、それらもこのループで繰り返されてしまうので注意してください。よって、この方法は配列に対しては "推奨しません"。

+

for...in ループを使用して配列を反復処理することもできます。ただし、もし誰かが Array.prototype に新しいプロパティを追加していたら、このループでそれらに対しても反復処理されてしまうので注意してください。よって、この方法は配列に対しては推奨しません。

-

配列を繰り返すもうひとつの方法が、 ECMAScript 5 で追加された forEach() です:

+

配列を繰り返すもうひとつの方法が、 ECMAScript 5 で追加された forEach() です。

-
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
-  // currentValue(現在の値) または array[index] について何かする
+
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
+  // currentValue (現在の値) または array[index] について何かする
 });
 
-

配列に要素を追加したいなら、このようにするのがもっともシンプルです:

+

配列に要素を追加したいなら、このようにするのがもっともシンプルです。

-
a.push(item);
+
a.push(item);

配列には多くのメソッドがついてきます。配列のメソッドに関するドキュメント もご覧ください。

@@ -571,33 +597,33 @@ a.length; // 101 -

関数

+

関数

-

オブジェクトとともに、関数は JavaScript を理解するうえで核となる構成要素です。もっとも基本的な関数は極めてシンプルです:

+

オブジェクトとともに、関数は JavaScript を理解するうえで核となる構成要素です。もっとも基本的な関数は極めてシンプルです。

-
function add(x, y) {
+
function add(x, y) {
   var total = x + y;
   return total;
 }
 
-

これは基本的な関数を例示しています。JavaScript の関数は 0 以上の名前のついた引数を取ることができます。関数の本体は好きなだけたくさんの文を含ませることができ、またその関数内で局所的な変数を宣言することができます。return 文は好きなときに関数を終了し値を返すために使うことができます。もし return 文が使われなかったら (あるいは値をつけない空の return が使われたら)、JavaScript は undefined を返します。

+

これは基本的な関数を例示しています。JavaScript の関数は 0 以上の名前のついた引数を取ることができます。関数の本体は好きなだけたくさんの文を含ませることができ、またその関数内で局所的な変数を宣言することができます。return 文は好きなときに関数を終了し値を返すために使うことができます。もし return 文が使われなかったら (あるいは値を持たない空の return が使われたら)、JavaScript は undefined を返します。

-

実のところ、名前のついた引数はガイドラインのようなもの以外の何物でもありません。あなたは期待された引数を渡さずに関数を呼ぶことができます。その場合引数には undefined がセットされます。

+

名前のついた引数は、何よりもガイドラインのようなものです。期待された引数を渡さずに関数を呼び出すことができます。その場合、引数には undefined が設定されます。

-
add(); // NaN
+
add(); // NaN
 // undefined では加算を実行できない
 
-

あなたはまた、関数が期待しているより多くの引数を渡すこともできます:

+

また、関数が期待するより多くの引数を渡すこともできます。

-
add(2, 3, 4); // 5
+
add(2, 3, 4); // 5
 // 第 1、第 2 引数を加算。4 は無視される
 
-

これは少し馬鹿げているように見えるかもしれませんが、関数はその本体の中で arguments と呼ばれる追加の変数を利用することができます。これはその関数へ渡されたすべての値を保持する配列のようなオブジェクトです。さあ、add 関数を好きなだけたくさんの値をとれるよう書き直してみましょう:

+

これは少し馬鹿げているように見えるかもしれませんが、関数はその本体の中で arguments と呼ばれる追加の変数を利用することができます。これはその関数へ渡されたすべての値を保持する配列のようなオブジェクトです。 add 関数を好きなだけたくさんの値をとれるよう書き直してみましょう。

-
function add() {
+
function add() {
   var sum = 0;
   for (var i = 0, j = arguments.length; i < j; i++) {
     sum += arguments[i];
@@ -608,9 +634,9 @@ a.length; // 101
 add(2, 3, 4, 5); // 14
 
-

しかしこれは 2 + 3 + 4 + 5 と書くより使い勝手がいいものではまったくありません。平均を取る関数を作ってみましょう:

+

しかしこれは 2 + 3 + 4 + 5 と書くより使い勝手がいいものではまったくありません。平均を取る関数を作ってみましょう。

-
function avg() {
+
function avg() {
   var sum = 0;
   for (var i = 0, j = arguments.length; i < j; i++) {
     sum += arguments[i];
@@ -621,28 +647,24 @@ add(2, 3, 4, 5); // 14
 avg(2, 3, 4, 5); // 3.5
 
-

この関数はかなり便利ですが、やや冗長にみえます。コードを減らすために、 Rest パラメーター記法で引数配列の使用を置き換えることを検討できます。この方法では、コードを最小限にしながら任意の数の引数を関数に渡すことができます。Restパラメーター演算子...変数の形式で、関数宣言内で使用します。また、これは変数内に、関数を呼び出したときに未取得の引数すべてのリストが含まれます。また、変数内の値を返すための for ループを for...of ループに置き換えます。

+

これはとても便利ですが、少し冗長な印象を受けます。このコードをもう少し減らすために、引数配列の代わりに残余引数構文を使ってみましょう。この方法では、コードを最小限に保ちながら、関数に任意の数の引数を渡すことができます。残余引数演算子は、関数の引数リストで ...変数 という書式で使用され、その変数の中に、関数が呼び出されたときに取得されなかった引数のリストをすべて含めることができます。また、 for ループを for...of ループに置き換えて、変数内の値を返すようにします。

-
function avg(...args) {
-  var sum = 0;
-  for (let value of args) {
-    sum += value;
-  }
-  return sum / args.length;
+
function avg(...args) {
+  var sum = 0;
+  for (let value of args) {
+    sum += value;
+  }
+  return sum / args.length;
 }
 
 avg(2, 3, 4, 5); // 3.5
 
-
-
上記のコードで変数 args は、関数に渡されたすべての値を保持します。
-
-関数宣言に rest パラメーターを置くと常に、その前方ではなく後方にある、すべての引数を保存することへの注意が重要です。
+

上記のコードで変数 args は、関数に渡されたすべての値が格納されています。

-
つまり例えば function avg(firstValue, ...args) は、関数に渡した最初の値が変数 firstValue に、残りの引数が args に保存されます。この関数はかなり便利ですが、新たな問題を提示しています。この avg() 関数はコンマ区切りのリストを引数に取りますが、もし配列の平均を知りたいときにはどうしたらいいでしょう? あなたは単純に関数を以下のように書き直すこともできます:
-
+

注意すべき点は、関数宣言の中に残余引数演算子が置かれている場合、宣言ののすべての引数が格納されますが、宣言の前の引数は格納されないということです。すなわち、 function avg(firstValue, ...args) では、関数に渡された最初の値が変数 firstValue に格納され、残りの引数が args に格納されます。これも便利な言語機能のひとつですが、新たな問題が発生しました。 avg() 関数はカンマで区切られた引数のリストを受け取りますが、配列の平均値を求めたい場合はどうすればよいのでしょうか。この関数を次のように書き換えればよいのです。

-
function avgArray(arr) {
+
function avgArray(arr) {
   var sum = 0;
   for (var i = 0, j = arr.length; i < j; i++) {
     sum += arr[i];
@@ -653,22 +675,22 @@ avg(2, 3, 4, 5); // 3.5
 avgArray([2, 3, 4, 5]); // 3.5
 
-

しかし私たちがすでに作った関数を再利用できた方がいいですよね。幸運なことに、JavaScript は関数オブジェクトの {{jsxref("Function.apply", "apply()")}} メソッドを使うことで、引数に任意の配列をつけて呼ぶことができます。

+

しかし、せっかく作った関数を再利用できたらいいですよね。幸い JavaScript では、任意の関数オブジェクトの {{jsxref("Function.apply", "apply()")}} メソッドを使って、任意の配列を引数として関数を呼び出すことができます。

-
avg.apply(null, [2, 3, 4, 5]); // 3.5
+
avg.apply(null, [2, 3, 4, 5]); // 3.5
 

apply() の第 2 引数は引数として使う配列です。第 1 引数は後で論じます。このようなことは関数もまたオブジェクトであるという事実を強調します。

-
-

同じ結果を関数呼び出しのスプレッド演算子でも達成できます。

+

関数呼び出しでスプレッド演算子を使用しても同じ結果を実現できます。

-

例えば: avg(...numbers)

-
+

例: avg(...numbers)

+ +

無名関数

-

JavaScript では{{原語併記("無名関数", "anonymous functions")}} を作ることができます。

+

JavaScript では無名関数 (anonymous function) — すなわち、名前のない関数を作ることができます。

-
var avg = function() {
+
function() {
   var sum = 0;
   for (var i = 0, j = arguments.length; i < j; i++) {
     sum += arguments[i];
@@ -677,23 +699,33 @@ avgArray([2, 3, 4, 5]); // 3.5
 };
 
-

これは意味的には function avg() 形式と等価です。これは非常に強力です。あなたは普通は式を置くところならどこにでも完全な関数定義を置くことができるのです。これはあらゆる巧妙なトリックを可能にしています。ここではいくつかの局所変数を ― C のブロックスコープのように ― 「隠す」方法を示します:

+

しかし、このような無名関数は単独では役に立ちません。 — 名前がなければ、その関数を呼び出す方法がないからです。そのため、実際には、無名関数は他の関数の引数として使われたり、関数を呼び出すために使用することができる変数に直接代入して呼び出し可能にしたりするのが一般的です。

-
var a = 1;
-var b = 2;
+
var avg = function() {
+  var sum = 0;
+  for (var i = 0, j = arguments.length; i < j; i++) {
+    sum += arguments[i];
+  }
+  return sum / arguments.length;
+};
+
-(function() { - var b = 3; - a += b; -})(); +

これによって、無名関数を avg() に引数を付けて呼ぶことで呼び出すことが可能になります。 — すなわち、意味的には function avg() という名前付き関数の形を使用して関数を宣言するのと同等になります。

+ +

しかし、変数に代入したり、他の関数の引数として渡されたりしなくても、無名関数が役に立つ方法があります。 JavaScript には、 1 つの式で関数の宣言と起動を同時に行う仕組みがあります。これは IIFE (Immediately invoked function expression) と呼ばれ、無名関数で使用する場合の構文は次のようになります。

-a; // 4 -b; // 2 +
(function() {
+  // …
+})();
 
+

IIFE の詳細については、この入門記事の範囲を越えますが、どのように役立つかについては、クロージャの記事のクロージャでプライベートメソッドを模倣するの節に良い例があります。

+ +

再帰関数

+

JavaScript では関数を再帰的に呼び出すことができます。これは特にブラウザーの DOM などにみられる木構造を取り扱うときに便利でしょう。

-
function countChars(elm) {
+
function countChars(elm) {
   if (elm.nodeType == 3) { // TEXT_NODE
     return elm.nodeValue.length;
   }
@@ -705,9 +737,9 @@ b; // 2
 }
 
-

この例は無名関数に関するある潜在的な問題を際立たせます。名前を持たない関数を再帰呼び出しさせるにはどうしたらよいのでしょう? JavaScript では、名前付き関数式で実現できます。以下のように、名前付き IIFEs (Immediately Invoked Function Expressions) を使用できます:

+

この例は無名関数に関するある潜在的な問題を際立たせます。名前を持たない関数を再帰呼び出しさせるにはどうしたらよいのでしょう? JavaScript では、名前付き関数式で実現できます。以下のように、名前付き IIFE (Immediately Invoked Function Expressions) を使用できます。

-
var charsInBody = (function counter(elm) {
+
var charsInBody = (function counter(elm) {
   if (elm.nodeType == 3) { // TEXT_NODE
     return elm.nodeValue.length;
   }
@@ -721,15 +753,15 @@ b; // 2
 
 

上記のように関数式に与えられた名前は、関数自身のスコープ内でのみ有効です。これはエンジンによる高度な最適化を実現して、結果的に可読性が高いコードになります。この名前はデバッガーやスタックトレースにも表示されますので、デバッグにかかる時間を節約できます。

-

JavaScript の関数自体が (他のものすべてと同様に) オブジェクトですので、オブジェクトの章で見てきたとおり、プロパティの追加や変更が可能です。

+

JavaScript の関数自体が (他のものすべてと同様に) オブジェクトですので、オブジェクトの節で見てきたとおり、プロパティの追加や変更が可能です。

-

カスタムオブジェクト

+

カスタムオブジェクト

-
JavaScript でのオブジェクト指向プログラミングの詳細な論考については、オブジェクト指向 JavaScript 入門 をご覧ください。
+
JavaScript でのオブジェクト指向プログラミングの詳細な論考については、オブジェクト指向 JavaScript 入門 をご覧ください。
-

古典的なオブジェクト指向プログラミングにおいて、オブジェクトとはデータとそのデータを操作するメソッドの集まりです。JavaScript は、C++ や Java に見られる class 文を持たない、プロトタイプベースの言語です。(これは、class 文を持つ言語に慣れたプログラマーを混乱させることでしょう) 代わりに、JavaScript は関数をクラスとして用います。ファーストネームとラストネームのフィールドを持つ person オブジェクトを考えてみましょう。その名前を表示させる方法には 2 種類が考えられます: "first last" と "last, first" です。ここまでで論じた関数とオブジェクトを使ってみると、以下のようにデータを表示できるでしょう:

+

古典的なオブジェクト指向プログラミングにおいて、オブジェクトとはデータとそのデータを操作するメソッドの集まりです。JavaScript は、C++ や Java に見られる class 文を持たない、プロトタイプベースの言語です (これは、 class 文を持つ言語に慣れたプログラマーを混乱させることがあります)。代わりに、 JavaScript は関数をクラスとして用います。ファーストネームとラストネームのフィールドを持つ person オブジェクトを考えてみましょう。その名前を表示させる方法には、 "first last" と "last, first" の 2 種類が考えられます。ここまでで論じた関数とオブジェクトを使ってみると、以下のようにデータを表示できるでしょう。

-
function makePerson(first, last) {
+
function makePerson(first, last) {
   return {
     first: first,
     last: last
@@ -742,14 +774,14 @@ function personFullNameReversed(person) {
   return person.last + ', ' + person.first;
 }
 
-s = makePerson('Simon', 'Willison');
+var s = makePerson('Simon', 'Willison');
 personFullName(s); // "Simon Willison"
 personFullNameReversed(s); // "Willison, Simon"
 
-

これはこれでうまく行きますが、かなり見苦しいですね。グローバルな名前空間にいくつもの関数を作ることになってしまいます。本当にしたいことは関数をオブジェクトにくっつけることです。関数はオブジェクトなので、簡単にできます:

+

これはこれでうまく行きますが、かなり見苦しいですね。グローバルな名前空間にいくつもの関数を作ることになってしまいます。本当にしたいことは関数をオブジェクトにくっつけることです。関数はオブジェクトなので、簡単にできます。

-
function makePerson(first, last) {
+
function makePerson(first, last) {
   return {
     first: first,
     last: last,
@@ -762,25 +794,25 @@ personFullNameReversed(s); // "Willison, Simon"
   };
 }
 
-s = makePerson('Simon', 'Willison');
+var s = makePerson('Simon', 'Willison');
 s.fullName(); // "Simon Willison"
 s.fullNameReversed(); // "Willison, Simon"
 
-

おや、まだ見たことがないものがありますね。this キーワードです。関数内で使われると、this は現在のオブジェクトを参照します。実際に意味するところは関数の呼ばれ方によります。オブジェクト上で ドットの記法や角カッコの記法 を使って呼び出すと、そのオブジェクトが this になります。ドット記法を使わずに呼び出すと、this はグローバルオブジェクトを参照します。

+

this キーワードに注意してください。関数内で使われると、this は現在のオブジェクトを参照します。実際に意味するところは関数の呼び出し方によります。オブジェクト上でドット記法やブラケット記法を使って呼び出すと、そのオブジェクトが this になります。ドット記法を使わずに呼び出すと、 this はグローバルオブジェクトを参照します。

-

this は失敗の原因になることがよくありますので注意してください。例えば:

+

this は次のように、失敗の原因になることがよくありますので注意してください。

-
s = makePerson('Simon', 'Willison');
+
var s = makePerson('Simon', 'Willison');
 var fullName = s.fullName;
 fullName(); // undefined undefined
 

s.fullName() とせずに fullName() を単独で呼び出すと、this はグローバルオブジェクトに結び付けられます。firstlast というグローバル変数はありませんので、それぞれに対して undefined が得られます。

-

この this キーワードを活用することで、makePerson 関数を改良することができます:

+

この this キーワードを活用することで、makePerson 関数を改良することができます。

-
function Person(first, last) {
+
function Person(first, last) {
   this.first = first;
   this.last = last;
   this.fullName = function() {
@@ -793,13 +825,13 @@ fullName(); // undefined undefined
 var s = new Person('Simon', 'Willison');
 
-

もう 1 つのキーワード new が出てきました。newthis と強い関連があります。これは新しい空のオブジェクトを作り、this にその新しいオブジェクトをセットして、後に続く関数を呼びます。this に指定された関数は値を返しているのではなく、単に this オブジェクトを変更していることに注意してください。this オブジェクトを呼び出し元に返しているのは new です。new によって呼ばれるよう設計された関数はコンストラクター関数と呼ばれます。new によって呼ばれるということがわかるよう、先頭を大文字にすることがよく行われています。

+

もう 1 つのキーワード new が出てきました。 newthis と強い関連があります。これは新しい空のオブジェクトを作り、 this にその新しいオブジェクトを設定して、後に続く関数を呼びます。this に指定された関数は値を返しているのではなく、単に this オブジェクトを変更していることに注意してください。 this オブジェクトを呼び出し元に返しているのは new です。new によって呼ばれるよう設計された関数は、コンストラクター関数と呼ばれます。一般的には、これらの関数の先頭を大文字にして、 new で呼び出すように注意します。

改良した関数でも、fullName() を単独で呼び出すときの落とし穴がまだあります。

-

person オブジェクトはだいぶ良くなりましたが、まだ改善の余地があります。person オブジェクトを作るたびに、その中に 2 つの新しい関数オブジェクトを作っています。関数のコードは共有されたほうがいいですよね?

+

person オブジェクトはだいぶ良くなりましたが、まだ改善の余地があります。person オブジェクトを作るたびに、その中に 2 つの新しい関数オブジェクトを作っています。関数のコードは共有されたほうがいいですよね。

-
function personFullName() {
+
function personFullName() {
   return this.first + ' ' + this.last;
 }
 function personFullNameReversed() {
@@ -813,9 +845,9 @@ function Person(first, last) {
 }
 
-

このほうが良いですね。メソッド関数を一度だけ作って、コンストラクターの中でそれへの参照を代入しています。もっとよくなりませんかね? 答えは yes です:

+

このほうが良いですね。メソッド関数を一度だけ作って、コンストラクターの中でそれへの参照を代入しています。もっとよくならないでしょうか。答えは yes です。

-
function Person(first, last) {
+
function Person(first, last) {
   this.first = first;
   this.last = last;
 }
@@ -827,24 +859,25 @@ Person.prototype.fullNameReversed = function() {
 };
 
-

Person.prototypePerson のすべてのインスタンスで共有されるオブジェクトです。これは (「プロトタイプチェーン」という特別な名前を持った) ルックアップチェーンの一部を構成します。Person の何もセットされていないプロパティにアクセスしようとするときはいつでも、JavaScript は Person.prototype が代わりのプロパティを持っているか確認します。結果として、Person.prototype に割り当てられたプロパティはすべて this オブジェクトを通じてコンストラクターのすべてのインスタンスで利用できるようになります。

+

Person.prototypePerson のすべてのインスタンスで共有されるオブジェクトです。これは (「プロトタイプチェーン」という特別な名前を持った) ルックアップチェーンの一部を構成します。Person の何もセットされていないプロパティにアクセスしようとするときはいつでも、 JavaScript は Person.prototype が代わりのプロパティを持っているか確認します。結果として、Person.prototype に割り当てられたプロパティはすべて this オブジェクトを通じてコンストラクターのすべてのインスタンスで利用できるようになります。

-

これはとても強力です。JavaScript では、プログラム上でいつでもどれかのプロトタイプを変更することができます。ということは、実行時に既存のオブジェクトに対して追加のメソッドを加えることができるのです:

+

これはとても強力です。 JavaScript では、プログラム上でいつでもどれかのプロトタイプを変更することができます。ということは、実行時に既存のオブジェクトに対して追加のメソッドを加えることができるのです。

-
s = new Person('Simon', 'Willison');
-s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function
+
var s = new Person('Simon', 'Willison');
+s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function
 
-Person.prototype.firstNameCaps = function() {
-  return this.first.toUpperCase();
-};
-s.firstNameCaps(); // "SIMON"
+Person.prototype.firstNameCaps = function() { + return this.first.toUpperCase(); +}; +s.firstNameCaps(); // "SIMON" +
-

興味深いことに、JavaScript の組み込みオブジェクトのプロトタイプにも差し込むことができます。String オブジェクトに文字列を逆さにして返すメソッドを加えてみましょう:

+

興味深いことに、JavaScript の組み込みオブジェクトのプロトタイプにも差し込むことができます。String オブジェクトに文字列を逆さにして返すメソッドを加えてみましょう。

-
var s = 'Simon';
+
var s = 'Simon';
 s.reversed(); // TypeError on line 1: s.reversed is not a function
 
-String.prototype.reversed = function reversed() {
+String.prototype.reversed = function() {
   var r = '';
   for (var i = this.length - 1; i >= 0; i--) {
     r += this[i];
@@ -857,13 +890,13 @@ s.reversed(); // nomiS
 
 

私たちの新しいメソッドは文字列リテラル上でさえも動きます!

-
'This can now be reversed'.reversed(); // desrever eb won nac sihT
+
'This can now be reversed'.reversed(); // desrever eb won nac sihT
 
-

前にも言ったように、プロトタイプはチェーンの一部を構成します。チェーンの根は Object.prototype であり、toString() メソッドを含んでいます。これはオブジェクトを文字列で表そうとするときに呼ばれるメソッドです。これは Person オブジェクトをデバッグするときに役立ちます:

+

前述の通り、プロトタイプはチェーンの一部を構成します。チェーンの根は Object.prototype であり、 toString() メソッドを含んでいます。これはオブジェクトを文字列で表そうとするときに呼ばれるメソッドです。これは Person オブジェクトをデバッグするときに役立ちます。

-
var s = new Person('Simon', 'Willison');
-s; // [object Object]
+
var s = new Person('Simon', 'Willison');
+s.toString(); // [object Object]
 
 Person.prototype.toString = function() {
   return '<Person: ' + this.fullName() + '>';
@@ -872,26 +905,26 @@ Person.prototype.toString = function() {
 s.toString(); // "<Person: Simon Willison>"
 
-

avg.apply() の第 1 引数が null であったことを覚えていますか? もう一度見てみましょう。apply() の第 1 引数は 'this' として扱われるオブジェクトです。例えば、これは new のありふれた実装です:

+

avg.apply() の第 1 引数が null であったことを覚えていますか? もう一度見てみましょう。apply() の第 1 引数は 'this' として扱われるオブジェクトです。例えば、これは new のありふれた実装です。

-
function trivialNew(constructor, ...args) {
+
function trivialNew(constructor, ...args) {
   var o = {}; // オブジェクトを作成
   constructor.apply(o, args);
   return o;
 }
 
-

プロトタイプチェーンを設定しないので (これを説明するのは困難です)、trivialNew()new の完全な複製ではありません。よく使うことはないでしょうが、知っていると役に立ちます。このスニペットで、...args (省略符号を含む) は "残余引数 (rest arguments)" と呼ばれます。名前が示唆するとおり、これは残りの引数が含まれます。

+

プロトタイプチェーンを設定しないので (これを説明するのは困難です)、trivialNew()new の完全な複製ではありません。よく使うことはないでしょうが、知っていると役に立ちます。このスニペットで、...args (省略符号を含む) は「残余引数 (rest arguments)」と呼ばれます。名前が示唆するとおり、これは残りの引数が含まれます。

以下の 2 つの呼び出しはほぼ等価です。

-
var bill = trivialNew(Person, 'William', 'Orange');
+
var bill = trivialNew(Person, 'William', 'Orange');
-
var bill = new Person('William', 'Orange');
+
var bill = new Person('William', 'Orange');
-

apply() には姉妹関数 call があります。this を設定できる点は同じですが、引数に配列ではなく展開された値のリストをとります。

+

apply() には call という姉妹関数があります。 this を設定できる点は同じですが、引数に配列ではなく展開された値のリストをとります。

-
function lastNameCaps() {
+
function lastNameCaps() {
   return this.last.toUpperCase();
 }
 var s = new Person('Simon', 'Willison');
@@ -901,51 +934,52 @@ s.lastNameCaps = lastNameCaps;
 s.lastNameCaps(); // WILLISON
 
-

内部関数

+

内部関数

-

JavaScript での関数宣言は他の関数内でも行えます。これは初期の makePerson() 関数で見ています。大事なことは内部関数内で親関数スコープの変数にアクセスできることです:

+

JavaScript での関数宣言は他の関数内でも行うことができます。これは最初のころの makePerson() 関数で見ています。 JavaScript で関数を入れ子にすることの重要なことは、内部関数内で親関数スコープの変数にアクセスできることです。

-
function parentFunc() {
-  var a = 1;
+
function parentFunc() {
+  var a = 1;
 
-  function nestedFunc() {
-    var b = 4; // parentFunc はこれを使用できません
-    return a + b;
-  }
-  return nestedFunc(); // 5
-}
+ function nestedFunc() { + var b = 4; // parentFunc はこれを使用できない +  return a + b; + } + return nestedFunc(); // 5 +} +

内部関数は保守しやすいコードを書くときに多大な利便性をもたらします。ある関数が他の部分のコードでは役立たない関数を 1 つか 2 つ使っているなら、これらのユーティリティ関数を他から呼び出される関数の入れ子にすることができます。内部関数はグローバルスコープでなくなるので、いいことです。

-

内部関数はグローバル変数を使うという誘惑に対する対抗措置です。複雑なコードを書くとき、複数の関数間で値を共有するためにグローバル変数を使いたくなります。しかし、これでは保守がしづらくなります。内部関数は親関数の変数を共有できるので、グローバルな名前空間を汚染せずに複数の関数をまとめる (いわば 'ローカルなグローバル') ことができます。この仕組みは注意して使用する必要がありますが、便利です。

+

内部関数はグローバル変数を使うという誘惑に対する対抗措置です。複雑なコードを書くとき、複数の関数間で値を共有するためにグローバル変数を使いたくなります。しかし、これでは保守がしづらくなります。内部関数は親関数の変数を共有できるので、グローバルな名前空間を汚染せずに (いわば「ローカルなグローバル」) 複数の関数をまとめることができます。この仕組みは注意して使用する必要がありますが、便利です。

-

クロージャ

+

クロージャ

-

ここでは JavaScript が持つもっとも強力な、しかしもっともわかりにくいとも思われる概念のひとつを紹介します。これは何をしているのでしょうか?

+

ここでは JavaScript が持つもっとも強力な、しかしもっともわかりにくいとも思われる概念のひとつを紹介します。これは何をしているのでしょうか。

-
function makeAdder(a) {
+
function makeAdder(a) {
   return function(b) {
     return a + b;
   };
 }
-var x = makeAdder(5);
-var y = makeAdder(20);
-x(6); // ?
-y(7); // ?
+var add5 = makeAdder(5);
+var add20 = makeAdder(20);
+add5(6); // ?
+add20(7); // ?
 
-

makeAdder() 関数がクロージャの正体を明らかにします。この関数はそれぞれ、1 つの引数とともに呼び出されたときに、その値と自身が生成されたときの引数の値とを加算する、新たな '加算' 関数を生成しています。

+

makeAdder() 関数がクロージャの正体を明らかにします。この関数はそれぞれ、1 つの引数とともに呼び出されたときに、その値と自身が生成されたときの引数の値とを加算する、新たな「加算」関数を生成しています。

-

ここで起きていることは、前出の内部関数で起きていることとほとんど同じです。つまり、ある関数の内部で定義された関数は、外側の関数が持つ変数にアクセスすることができます。唯一の違いは外側の関数が値を返していることであり、それゆえ一般的な考えではローカル変数は存在しなくなると考えられます。しかし、ここではローカル変数が残り続けます。そうでなければ、加算関数は動作しないでしょう。さらに、makeAdder() のローカル変数には異なる 2 つの "複製" が存在します。一方の a は 5、もう一方の a は 20 です。よって、これらの関数を呼び出した結果は以下のようになります:

+

ここで起きていることは、前出の内部関数で起きていることとほとんど同じです。つまり、ある関数の内部で定義された関数は、外側の関数が持つ変数にアクセスすることができます。唯一の違いは外側の関数が値を返していることであり、それゆえ一般的な考えではローカル変数は存在しなくなると考えられます。しかし、ここではローカル変数が残り続けます。そうでなければ、加算関数は動作しないでしょう。さらに、 makeAdder() のローカル変数には異なる 2 つの「複製」が存在します。一方の a は 5、もう一方の a は 20 です。よって、これらの関数を呼び出した結果は以下のようになります。

-
x(6); // 11 を返す
-y(7); // 27 を返す
+
add5(6); // 11 を返す
+add20(7); // 27 を返す
 
-

これは実際に起きていることです。JavaScript で関数を実行するときは必ず、その関数内で作成されたローカル変数を保持する 'scope' オブジェクトが作成されます。それは関数の引数として渡された変数とともに初期化されます。これはすべてのグローバル変数やグローバル関数が存在している global オブジェクトに似ていますが、2 つの重要な違いがあります。ひとつは、関数を実行し始めるたびに新たな scope オブジェクトが生成されること、もうひとつは、global オブジェクト (this としてアクセスでき、またブラウザーでは window として存在する) とは異なり、これらの scope オブジェクトに JavaScript のコードから直接アクセスできないことです。例えば、現存する scope オブジェクトのプロパティをたどる仕組みはありません。

+

これは実際に起きていることです。 JavaScript で関数を実行するときは必ず、その関数内で作成されたローカル変数を保持する 'scope' オブジェクトが作成されます。それは関数の引数として渡された変数とともに初期化されます。これはすべてのグローバル変数やグローバル関数が存在している global オブジェクトに似ていますが、2 つの重要な違いがあります。ひとつは、関数を実行し始めるたびに新たな scope オブジェクトが生成されること、もうひとつは、global オブジェクト (this としてアクセスでき、またブラウザーでは window として存在する) とは異なり、これらの scope オブジェクトに JavaScript のコードから直接アクセスできないことです。例えば、現存する scope オブジェクトのプロパティをたどる仕組みはありません。

よって makeAdder() が呼び出されたときは、1 個のプロパティを持つ scope オブジェクトが生成されます。そのプロパティとは、makeAdder() 関数に渡される引数の a です。そして makeAdder() は、新たに生成された関数を返します。通常 JavaScript のガベージコレクターは、この時点で makeAdder() のために生成された scope オブジェクトを破棄しますが、返された関数は scope オブジェクトへの参照を維持しています。その結果、scope オブジェクトは makeAdder() が返した関数オブジェクトへの参照がなくなるまでの間、ガベージコレクションの対象になりません。

JavaScript のオブジェクトシステムが利用するプロトタイプチェーンと同様に、scope オブジェクトはスコープチェーンと呼ばれるチェーンを構成します。

-

クロージャは、関数と関数が生成した scope オブジェクトを組み合わせたものです。クロージャは状態を保存します。従って、オブジェクトの代わりとしてよく使用されます。クロージャのすばらしい解説 を読むことができます。

+

クロージャは、関数と関数が生成した scope オブジェクトを組み合わせたものです。クロージャは状態を保存します。従って、オブジェクトの代わりとしてよく使用されます。クロージャのすばらしい解説を読むことができます。

-- cgit v1.2.3-54-g00ecf