概要



まずはあらゆる言語の構成要素、「型」を見ることから始めましょう。JavaScript のプログラムは値を操作し、それらの値はすべて型に属しています。JavaScript の型は:
*数値
*文字列
*真偽値
*関数
*オブジェクト
*Symbol (ES2015 の新オブジェクト)

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

*null
*undefined

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

数値

JavaScript における数値は、仕様によると「倍精度 64 ビットフォーマット IEEE 754 値 (double-precision 64-bit format IEEE 754 values)」です。このことはいくつかの興味深い結果をもたらします。JavaScript には整数に当たるものがないので、あなたが C や Java の数学に慣れている場合は、数値計算に少し気を付ける必要があります。

また、以下のようなことに注意してください:
0.1 + 0.2 == 0.30000000000000004;


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

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


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


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


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

もし 2 進数を整数に変換したいなら、単純に基数を変えましょう:
parseInt('11', 2); // 3


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

また、単項演算子 + を使って値を数値に変換することもできます:
+ '42'; // 42
+ '010'; // 10
+ '0x10'; // 16


もし文字列が数でない場合、 NaN (非数、"Not a Number" の略) と呼ばれる特別な値が返ります:
parseInt('hello', 10); // NaN


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


組み込みの isNaN() 関数を使えば、NaN であるかを検査することができます:
isNaN(NaN); // true


JavaScript はまた、特別な値 Infinity と -Infinity を持っています:
1 / 0; // Infinity
-1 / 0; // -Infinity


組み込みの isFinite() 関数を使えば、Infinity、-Infinity、NaN であるかを検査することができます:
isFinite(1 / 0); // false
isFinite(-Infinity); // false
isFinite(NaN); // false


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

文字列

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

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

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


JavaScript のオブジェクトとの初めての接触です! 私、文字列も オブジェクト であると言いましたっけ? 文字列はまた、文字列の操作や文字列に関する情報へのアクセスを可能にする メソッド も持っています:
'hello'.charAt(0); // "h"
'hello, world'.replace('hello', 'goodbye'); // "goodbye, world"
'hello'.toUpperCase(); // "HELLO"


その他の型

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

JavaScript は true と false (これらはともにキーワードです) を取りうる値とする真偽値型を持っています。どんな値でも以下のルールに基づいて真偽値に変換できます:
1.false、0、空文字列 ("")、NaN、null、および undefined は、すべて false になる
2.その他の値はすべて true になる

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


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

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

変数

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

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


以下の例は、let で宣言した変数のスコープを示しています:
// ここでは myLetVariable が *見えません*

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

// ここでは myLetVariable が *見えません*


const は、変更を意図しない変数を宣言できます。宣言した変数は、変数を宣言した関数ブロックから使用できます。
const Pi = 3.14; // 変数 Pi を設定
Pi = 1; // 不変の変数は変更できないため、エラーが発生します

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

以下の例は、var: で宣言した変数のスコープを示しています:
// ここでは myVarVariable が *見えます*

for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
// myVarVariable は関数全体で見えます
}

// ここでは myVarVariable が *見えます*


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

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

演算子

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


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

+ 演算子 は文字列の結合もします:
'hello' + ' world'; // "hello world"


文字列を数字 (や他の値) に足すと、すべてのものはまず最初に文字列に変換されます。このことはミスを誘うかもしれません:
'3' + 4 + 5; // "345"
3 + 4 + '5'; // "75"


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

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


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


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

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

制御構造

JavaScript は C 言語ファミリーの他の言語とよく似た制御構造セットを持っています。条件文は if と else でサポートされています。必要ならこれらを連鎖させることもできます:
var name = 'kittens';
if (name == 'puppies') {
name += ' woof';
} else if (name == 'kittens') {
name += ' meow';
} else {
name += '!';
}
name == 'kittens meow';


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

var input;
do {
input = get_input();
} while (inputIsNotValid(input));


JavaScript の for ループは C や Java のそれと同じです。これはループの制御情報を 1 行で与えることができます。
for (var i = 0; i < 5; i++) {
// 5 回実行されます
}


JavaScript にはこの他に、特徴的な for ループが 2 つあります。ひとつは for...of です:
for (let value of array) {
// 値に関する処理
}


もうひとつは for...in です:
for (let property in object) {
// オブジェクトのプロパティに関する処理
}


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


あるいは値の格納にも便利です (falsy な値は無効であるとき):
var name = cachedName || (cachedName = getName());


JavaScript はワンライン条件文のための三項演算子を持っています:
var allowed = (age > 18) ? 'yes' : 'no';


switch 文はある数値や文字列を元にした複数分岐に使われます:
switch (action) {
case 'draw':
drawIt();
break;
case 'eat':
eatIt();
break;
default:
doNothing();
}


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


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


オブジェクト

JavaScript のオブジェクトは、名前と値のペアの単純なコレクションであると考えることができます。これは以下のものに似ています:
*Python の辞書型
*Perl や Ruby のハッシュ
*C や C++ のハッシュテーブル
*Java の HashMap クラス
*PHP の連想配列

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

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

空のオブジェクトを生成する 2 つの基本的な方法があります:
var obj = new Object();


そして:
var obj = {};


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

オブジェクトリテラル構文はオブジェクトをそっくりそのまま初期化するのに使えます:
var obj = {
name: 'Carrot',
for: 'Max', // 'for' は予約語であり、代わりに '_for' を使用します
details: {
color: 'orange',
size: 12
}
}


属性へのアクセスは同時に連鎖させることができます:
obj.details.color; // orange
obj['details']['size']; // 12


以下の例ではオブジェクトのプロトタイプである Person と、プロトタイプのインスタンスである You を生成しています。
function Person(name, age) {
this.name = name;
this.age = age;
}

// オブジェクトを定義する
var you = new Person('You', 24);
// "You" という名前で、年齢が 24 歳の新たな Person を作成しました


一度作ってしまえば、オブジェクトのプロパティには下記の 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?')123456

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


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

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


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

配列

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

配列を生成する方法のひとつは以下の通り:
var a = new Array();
a[0] = 'dog';
a[1] = 'cat';
a[2] = 'hen';
a.length; // 3


より便利な書き方は配列リテラルを使うことです:
var a = ['dog', 'cat', 'hen'];
a.length; // 3


array.length は必ずしも配列中の要素の数ではないことに注意してください。以下の例を考えてみましょう:
var a = ['dog', 'cat', 'hen'];
a[100] = 'fox';
a.length; // 101


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

もし存在しない配列の添字を要求すると、undefined が得られます:
typeof a[90]; // undefined


上記の [] と length を考慮に入れれば、以下の for ループを使って配列を繰り返すことができます:
for (var i = 0; i < a.length; i++) {
// a[i] について何かする
}


ECMAScript では配列のような iterable オブジェクト用に、より正確な for...of ループが導入されました:
for (const currentValue of a) {
// currentValue(現在の値)で何かをする
}123

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

配列を繰り返すもうひとつの方法が、 ECMAScript 5 で追加された forEach() です:
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
// currentValue(現在の値) または array[index] について何かする
});


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

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


メソッド名

説明


a.toString() 各要素に toString() を適用したものを、カンマ区切りで並べた文字列を返します。
a.toLocaleString() 各要素に toLocaleString() を適用したものを、カンマ区切りで並べた文字列を返します。
a.concat(item1[, item2[, ...[, itemN]]]) 配列に要素を追加した、新しい配列を返します。
a.join(sep) 配列を文字列に変換します。値は引数 sep で区切ります。
a.pop() 最後の要素を取り除いて、その要素を返します。
a.push(item1, ..., itemN) 配列の末尾に要素を追加します。
a.reverse() 配列を反転します。
a.shift() 最初の要素を取り除いて、その要素を返します。
a.slice(start[, end]) 部分配列を返します。
a.sort([cmpfn]) オプションとして比較関数をとります。
a.splice(start, delcount[, item1[, ...[, itemN]]]) ある部分を削除したり他の追加要素に置き換えることで、配列を修正することができます。
a.unshift(item1[, item2[, ...[, itemN]]]) 要素を配列の先頭に挿入します。

関数

オブジェクトとともに、関数は JavaScript を理解するうえで核となる構成要素です。もっとも基本的な関数は極めてシンプルです:
function add(x, y) {
var total = x + y;
return total;
}


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

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


あなたはまた、関数が期待しているより多くの引数を渡すこともできます:
add(2, 3, 4); // 5
// 第 1、第 2 引数を加算。4 は無視される


これは少し馬鹿げているように見えるかもしれませんが、関数はその本体の中で arguments と呼ばれる追加の変数を利用することができます。これはその関数へ渡されたすべての値を保持する配列のようなオブジェクトです。さあ、add 関数を好きなだけたくさんの値をとれるよう書き直してみましょう:
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}

add(2, 3, 4, 5); // 14


しかしこれは 2 + 3 + 4 + 5 と書くより使い勝手がいいものではまったくありません。平均を取る関数を作ってみましょう:
function avg() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
}

avg(2, 3, 4, 5); // 3.5


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

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

avgArray([2, 3, 4, 5]); // 3.5


しかし私たちがすでに作った関数を再利用できた方がいいですよね。幸運なことに、JavaScript は関数オブジェクトの apply() メソッドを使うことで、引数に任意の配列をつけて呼ぶことができます。
avg.apply(null, [2, 3, 4, 5]); // 3.5


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


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

例えば: avg(...numbers)

JavaScript では無名関数 (anonymous functions) を作ることができます。
var avg = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
};


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

(function() {
var b = 3;
a += b;
})();

a; // 4
b; // 2


JavaScript では関数を再帰的に呼び出すことができます。これは特にブラウザーの DOM などにみられる木構造を取り扱うときに便利でしょう。
function countChars(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += countChars(child);
}
return count;
}


この例は無名関数に関するある潜在的な問題を際立たせます。名前を持たない関数を再帰呼び出しさせるにはどうしたらよいのでしょう? JavaScript では、名前付き関数式で実現できます。以下のように、名前付き IIFE (Immediately Invoked Function Expressions) を使用できます:
var charsInBody = (function counter(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += counter(child);
}
return count;
})(document.body);


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

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

カスタムオブジェクト

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

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

s = makePerson('Simon', 'Willison');
personFullName(s); // "Simon Willison"
personFullNameReversed(s); // "Willison, Simon"


これはこれでうまく行きますが、かなり見苦しいですね。グローバルな名前空間にいくつもの関数を作ることになってしまいます。本当にしたいことは関数をオブジェクトにくっつけることです。関数はオブジェクトなので、簡単にできます:
function makePerson(first, last) {
return {
first: first,
last: last,
fullName: function() {
return this.first + ' ' + this.last;
},
fullNameReversed: function() {
return this.last + ', ' + this.first;
}
};
}

s = makePerson('Simon', 'Willison');
s.fullName(); // "Simon Willison"
s.fullNameReversed(); // "Willison, Simon"


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

this は失敗の原因になることがよくありますので注意してください。例えば:
s = makePerson('Simon', 'Willison');
var fullName = s.fullName;
fullName(); // undefined undefined


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

この this キーワードを活用することで、makePerson 関数を改良することができます:
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
};
this.fullNameReversed = function() {
return this.last + ', ' + this.first;
};
}
var s = new Person('Simon', 'Willison');


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

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

person オブジェクトはだいぶ良くなりましたが、まだ改善の余地があります。person オブジェクトを作るたびに、その中に 2 つの新しい関数オブジェクトを作っています。関数のコードは共有されたほうがいいですよね?
function personFullName() {
return this.first + ' ' + this.last;
}
function personFullNameReversed() {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}


このほうが良いですね。メソッド関数を一度だけ作って、コンストラクターの中でそれへの参照を代入しています。もっとよくなりませんかね? 答えは yes です:
function Person(first, last) {
this.first = first;
this.last = last;
}
Person.prototype.fullName = function() {
return this.first + ' ' + this.last;
};
Person.prototype.fullNameReversed = function() {
return this.last + ', ' + this.first;
};


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

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

興味深いことに、JavaScript の組み込みオブジェクトのプロトタイプにも差し込むことができます。String オブジェクトに文字列を逆さにして返すメソッドを加えてみましょう:
var s = 'Simon';
s.reversed(); // TypeError on line 1: s.reversed is not a function

String.prototype.reversed = function reversed() {
var r = '';
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
};

s.reversed(); // nomiS


私たちの新しいメソッドは文字列リテラル上でさえも動きます!
'This can now be reversed'.reversed(); // desrever eb won nac sihT


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

Person.prototype.toString = function() {
return '';
}

s.toString(); // ""


avg.apply() の第 1 引数が null であったことを覚えていますか? もう一度見てみましょう。apply() の第 1 引数は 'this' として扱われるオブジェクトです。例えば、これは new のありふれた実装です:
function trivialNew(constructor, ...args) {
var o = {}; // オブジェクトを作成
constructor.apply(o, args);
return o;
}


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

以下の 2 つの呼び出しはほぼ等価です。
var bill = trivialNew(Person, 'William', 'Orange');
var bill = new Person('William', 'Orange');

apply() には姉妹関数 call があります。this を設定できる点は同じですが、引数に配列ではなく展開された値のリストをとります。
function lastNameCaps() {
return this.last.toUpperCase();
}
var s = new Person('Simon', 'Willison');
lastNameCaps.call(s);
// 上記の文は以下と等価:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps(); // WILLISON


内部関数

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

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

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

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

クロージャ

ここでは JavaScript が持つもっとも強力な、しかしもっともわかりにくいとも思われる概念のひとつを紹介します。これは何をしているのでしょうか?
function makeAdder(a) {
return function(b) {
return a + b;
};
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // ?
y(7); // ?


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

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


これは実際に起きていることです。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 オブジェクトを組み合わせたものです。クロージャは状態を保存します。従って、オブジェクトの代わりとしてよく使用されます。クロージャのすばらしい解説 を読むことができます。


https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions