JavaScript 1
作成日:2017年4月23日
オブジェクトと配列
JavaScriptには、単純な変数型として、数値、文字列、真偽値、null、undefined があり、これら以外の値はすべてオブジェクトです。 数値、文字列、真偽値はメソッドを呼び出すことができるという意味ではオブジェクトに似ていますが、これらの値は一度セットした値を変更することができません(イミュータブル)。それに対して、JavaScript のオブジェクトはキーによって整理された変更可能(ミュータブル)なデータの集合体です。 JavaScript では、配列も、関数も、正規表現もオブジェクトです。
オブジェクトは複数のプロパティを含むコンテナ(入れ物)で、プロパティは名前と値で構成されています。プロパティ名は任意の文字列が利用でき、空文字も利用可能。 プロパティの値には、undefinedを除くすべてのJavaScriptの値をセットできます。
オブジェクトの生成
オブジェクトの生成にはコンストラクタ(オブジェクトを生成する特別な関数)を用いるか、オブジェクトリテラルを用います。
オブジェクトリテラルによる生成
中括弧の中にプロパティの名前(キー)と値のペアをカンマで区切って記述します。オブジェクトリテラルは、新しいオブジェクトを生成し初期化する式で、この式を評価するたびにオブジェクトが生成されます。
プロパティの値を関数にすることもでき、その場合はオブジェクトのメソッドとなります。
var empty_object = {}; //空のオブジェクト var person = { //プロパティを設定したオブジェクト "first-name": "Johnny", "last-name": "Smith" };
プロパティ名の前後に付けるクオート("")は、文字列が予約語ではなく、JavaScriptの名前に則っていれば、省略することができます。 上記の例"first-name"の場合はクオートが必要ですが、first_nameの場合は省略可能です。 プロパティの区切りにはカンマを使用します。
識別子の先頭の文字は、英字、アンダースコア、ドル記号のいずれかでなければならず、それに続く文字は、英字、数字、アンダースコア、ドル記号のいずれかでなければなりません。
プロパティの値には任意の式を記述でき、他のオブジェクトリテラルを含むこと(ネスト)も可能です。
var flight = { airline: "American Airlines", number: 289, departure: { airport: "LGA", time: "2017-02-10 7:05AM", city: "New York" }, arrival: { airport: "ORD", time: "2017-02-10 8:50AM", city: "Chicago" } };
以下は、オブジェクト myObject を作成し、x と y というプロパティを設定する例です。 「myObject.x」でプロパティ x の値を参照できます。
var myObject = { x: 5, y: 20, func: function() { return this.x * this.y; } } console.log(myObject.x); // 5 と表示される。 console.log(myObject.func()); // 100 と表示される。
new 演算子(コンストラクタ)による生成
new 演算子の後にオブジェクトのプロパティを初期化するコンストラクタ関数を記述します。
var a = new Array(); //空の配列の生成 var d = new Date(); //現在の日時と時刻を表すオブジェクトの生成 var o = new Object(); //Object()コンストラクタを使った空のオブジェクトの生成 var o = {};と同じ
以下は、new 演算子を使ってオブジェクト myObject を作成し、x と y というプロパティを設定する例です。
var myObject = new Object(); myObject.x = 5; myObject.y = 20; myObject.func = function() {return this.x * this.y; }; console.log(myObject.y); // 20 と表示される。 console.log(myObject.func()); // 100 と表示される。
プロパティへのアクセス
オブジェクトのプロパティの値へのアクセスは通常、ドット演算子を使用します。ドットの右側の値は、プロパティの名前です(プロパティ名は識別子でなければなりません)。
変数と同じようにプロパティに値を書き込んだり、読み出すことができます。
var book = {}; //オブジェクトを生成し、その参照を変数に格納 book.title = "Programming"; //titleというプロパティを生成(値は"Programming") book.chapter1 = new Object(); //オブジェクトを生成しchapter1というプロパティに格納 book.chapter1.title = "JavaScript"; //book.chapter1のプロパティtitleを生成し値("JavaScript")を格納 console.log(book.title); //Programming console.log(book.chapter1.title); //JavaScript
値の取得
文字列式を [] で囲んで指定することで、オブジェクトから値を取り出すことができます。 もし文字列式が文字列リテラルで、予約語でない JavaScript の名前のルールに則っていれば、[] の変わりに . (ドット)を利用することもできます。
var book = {}; book.title = "Programming"; book.chapter1 = new Object(); book.chapter1.title = "JavaScript"; var a = book.title; var b = book["title"]; var c = book.chapter1["title"]; console.log(a + " " + b + " " + c); //出力 Programming Programming JavaScript
存在しないメンバを取得しようとした場合には undefined が返されます。
undefined に対して、さらにそのプロパティを取得しようとすると、TypeError例外が投げられます。 && 演算子を利用することでそうした事態を避けることができます。
console.log(book.test); //-> undefined console.log(book.test.test); //-> TypeError console.log(book.test && book.test.test); //-> undefined
|| 演算子を使ってデフォルト値を設定することができます。
var title1 = book.title1 || "unknown" ; console.log(title1); -> unknown
オブジェクトプロパティの調査
あるオブジェクトにどのようなプロパティがあるかを調べるには for/in ループで簡単に調べられます。以下はオブジェクトのプロパティを調べてコンソールに表示する関数の例です。
function ConsoleDisplayPropertyNames(obj){ var names = ""; for(var name in obj) names += name + "\n"; console.log(names); } ConsoleDisplayPropertyNames(book); //title //chapter1 ConsoleDisplayPropertyNames(book.chapter1); //title
for in 文を利用すると、オブジェクト内にあるすべてのプロパティの名前を順に取り出すことができますが、関数やプロトタイプチェーン上のプロパティなど、 場合によっては必要がないものが含まれるので、それら不要な値を取り除く必要があるかも知れません。そのための方法として一般的なのは、hasOwnProperty メソッドを使う方法と、typeof 演算子を使って関数を取り除く方法があります。
function ConsoleDisplayPropertyNames2(obj){ var names = ""; for(var name in obj){ if(typeof obj[name] !== 'function') { names += name + "\n"; } } console.log(names); } ConsoleDisplayPropertyNames2(window.location); //以下は出力 href origin protocol host hostname port pathname search hash toJSON
- typeof 演算子
- typeof 演算子は対象となる値のデータ型を表す文字列を返す演算子です。
値に応じて返される値は以下のようなものがあります。
- 数値: number
- 文字列: string
- 真偽値: boolean
- null : object
- NaN:number
- undefined:undefined
- オブジェクト:object
- 配列: object
- 関数: function
以下は、hasOwnProperty メソッドを使う方法です。
function ConsoleDisplayPropertyNames3(obj){ var names = ""; for(var name in obj){ if(obj.hasOwnProperty(name)) { names += name + "\n"; } } console.log(names); } ConsoleDisplayPropertyNames3(window.location); //以下は出力 (関数も出力されている) href origin protocol host hostname port pathname search hash assign replace reload toString valueOf toJSON
取得されるプロパティは順番が保証されていないので、どういう順番であっても正しく動作する処理にしておく必要があります。もし、プロパティを特定の順番で取り出したいのであれば、for in 文をあきらめて、代わりに、正しい順序にプロパティの名前を並べた配列を用意しておき、for 文を使うのが簡単な方法になります。
var person = {}; person.first_name = "John"; person.middle_name ="T"; person.last_name = "Smith"; person.profession = "Musician"; var i; var properties = [ 'first_name', 'middle_name', 'last_name', 'profession' ]; for(i=0; i < properties.length; i++){ console.log(properties[i] + ': ' + person[properties[i]]); } //以下は出力 first_name: John middle_name: T last_name: Smith profession: Musician
Object.keys プロパティの名前の配列
ES5 で導入された Object.keys() メソッドを使うと、オブジェクトが持つプロパティの名前の配列を通常のループで取得するのと同じ順序で返してくれます。
Object.keys() メソッドの戻り値は配列なので、配列のメソッド forEach() を使うことができます。
var person = { first_name: "John", middle_name: "T", last_name:"Smith", profession:"Musician" }; Object.keys(person).forEach(function (key) { console.log("プロパティ: " + key ); }); /* 出力 プロパティ: first_name プロパティ: middle_name プロパティ: last_name プロパティ: profession */
プロパティの名前(文字列)の配列から値を取得するには、プロパティの名前を [ ] で囲みます。この場合、 . (ドット)を使うと undefined になってしまいます。
var person = { first_name: "John", middle_name: "T", last_name:"Smith", profession:"Musician" }; Object.keys(person).forEach(function (key) { console.log("プロパティ: " + key + " 値: " + person[key]); //値は person.key では undefined になります }); /* 出力 プロパティ: first_name 値: John プロパティ: middle_name 値: T プロパティ: last_name 値: Smith プロパティ: profession 値: Musician */ //index も出力 Object.keys(person).forEach(function (key, index) { console.log(index + " プロパティ: " + key + " 値: " + person[key]); }); /* 出力 0 プロパティ: first_name 値: John 1 プロパティ: middle_name 値: T 2 プロパティ: last_name 値: Smith 3 プロパティ: profession 値: Musician */
オブジェクトプロパティの存在確認
in 演算子を使ってプロパティが存在するかを確認することができます。
if("x" in o) o.x = 1; //オブジェクト o が x というプロパティを持つ場合は、その値を設定する。
存在しないプロパティから値を読み出すと未定義値が返されるので、以下のコードで同じことが可能です。
if(o.x !== undefined) o.x = 1; //プロパティ x が存在し、未定義値でない場合はその値を設定する。
※ !== 演算子を使用していることに注意。 !== 演算子、=== 演算子では null と undefined を区別します。
オブジェクトプロパティの削除
delete 演算子を使ってプロパティを削除することができます。
delete.book.chapter1;
delete 演算子を使うと単にプロパティに未定義値が代入されるだけではなく、 for/in ループでも調べられなくなり、in 演算子でも存在しないと判定されます。
連想配列としてのオブジェクト
オブジェクトのプロパティにアクセスするにはドット演算子を使用するか、配列演算子([])を使うことができます。 以下はどちらも同じ値になります。
object.property object["property"]
識別子はデータ型ではないので、プログラムにそのまま記述しなければならず、プログラムで操作することはできませんが、[] 演算子を使ってプロパティを表現する場合、名前は文字列なので(文字列はデータ型)、プログラムで操作が可能です。
言い換えると、ドット演算子を使用する場合、プロパティ名(キー)に変数を使用することができません。そのためキーに変数を使う場合は、[] 演算子を使います。
var obj = {}; obj.name = "Object"; obj.color = "Green"; obj.shape = "Circle"; var user_input = "color"; console.log(obj[user_input]); //Green console.log(obj.user_input); //undefined
以下のコードを実行すると、user オブジェクトのプロパティ address1, address2, address3 が読み出され連結されます。
var addr = ""; for(i = 1; i < 4; i++){ addr += user["address" + i]+ "\n"; }
このように配列表記を利用したほうが柔軟性があります。識別子は静的なものなので、あらかじめプログラムに記述しておかなければなりませんが、 文字列は動的なもので、実行時に変えることができます。
オブジェクトのキーに変数を使う
前述の通り、ドット演算子を使用する場合、プロパティ名(キー)に変数を使用することができません。
以下のように、キーに変数名を入れると展開された変数ではなく、変数名がそのまま入ってしまいます。
var myValue = "値のサンプル"; var myKey = "variable_key"; var myObj = {myKey: myValue}; console.log(myObj); //Object { myKey: "値のサンプル" }
キーに変数を使う場合は、以下のように一度オブジェクトを定義して、[] 演算子を使ってキーに変数を指定します。
var myValue = "値のサンプル"; var myKey = "variable_key" var myObj = {}; myObj[myKey] = myValue; console.log(myObj); //Object { variable_key: "値のサンプル"
ES2015(ES6) 以降の場合、以下のように定義する際に [ ] を使ってキーの変数を指定すれば問題なく変数が展開されます。※但し、この方法は IE11 では使えません(エラーになります caniuse ES6)。
var myValue = "値のサンプル"; var myKey = "variable_key"; var myObj = {[myKey]: myValue}; console.log(myObj); //Object { variable_key: "値のサンプル" }
Object のプロパティとメソッド
JavaScript のオブジェクトは全て Object クラス(Object オブジェクト)を継承するので、どのようなクラスも Object で定義されたプロパティやメソッドを継承します。
constructor プロパティ
全てのオブジェクトは constructor プロパティを持ち、 construcor プロパティはオブジェクトの初期化で使用されてたコンストラクタ関数を参照します。constructor プロパティは戻り値としてインスタンスの生成に使用されたコンストラクタ(Function オブジェクト)を返し、オブジェクト名(文字列)を返すわけではありません。特定のオブジェクトの型を調べる手段として constructor プロパティが使えます。
var d = new Date(); console.log(d.constructor); //function Date() と表示される console.log(d); //表示例 Date 2017-02-19T17:19:15.739Z console.log(d.constructor == Date); //評価結果は true if((typeof d == "object")&&(d.constructor == Date)) { console.log("Date オブジェクト"); } //Date オブジェクト と表示される
toString() メソッド
JavaScript はオブジェクトを文字列に変換する必要がある場合、このメソッドを呼び出します。
toLocaleString() メソッド
オブジェクトを表すローカライズされた文字列を返す。Object クラスのデフォルトの toLocaleString() メソッドは toString() と同じ文字列を返します。
valueOf() メソッド
文字列以外の基本型(数値が多い)にオブジェクトを変換する必要があるときに自動的に呼び出されます。
hasOwnProperty() メソッド
呼び出しで使用したオブジェクトが、引数で指定した名前のプロパティ(但し継承したものではないプロパティ)を持つ場合に true を返す。
propertyIsEnumerable() メソッド
呼び出しで使用したオブジェクトが、引数で指定した名前の継承したものではないプロパティを持ち、かつ、そのプロパティが for/in ループで 調べられる場合に trueを返します。 ユーザーが定義したプロパティは全て for/in ループで調べられるので、propertyIsEnumerable() メソッドは hasOwnProperty() メソッドと 同じ値を返すことになります。
isPrototypeOf() メソッド
呼び出したオブジェクトが、引数で指定したオブジェクトのプロトタイプオブジェクトである場合に true を返します。
コンストラクタ
オブジェクト指向のプログラミング言語では、オブジェクトのクラスを定義して個々のオブジェクトをそのクラスのインスタンスとして生成します。JavaScript では Java や C++ のような通常のクラスをサポートしていませんが、コンストラクタ関数やプロトタイプオブジェクトを使ってクラスをシミュレートすることができます。
プロパティを持たないオブジェクトはオブジェクトリテラルで {} と記述するか、
new Object();
という式で生成できます。new 演算子の後に関数呼び出しを記述しています。これで、新たにプロパティを持たないオブジェクトが生成され、そのオブジェクトを this キーワードに代入して、指定した関数が呼び出されます。
このように new 演算子と一緒に使うための関数をコンストラクタ(関数)と呼びます。コンストラクタの役目は、新しく生成されたオブジェクトに対して、 プロパティに値を設定するなどの初期化を行い、オブジェクトを使えるようにすることです。
function Rectangle(w,h){ //コンストラクタ関数の定義 this.width = w; this.height = h; } //新しいオブジェクトが適切に初期化されるように、width と height の値を引数としてコンストラクタに渡します var rect = new Rectangle(3, 7); console.log("width: " + rect.width + " / height: " + rect.height); //width: 3 / height: 7 と表示される
コンストラクタはオブジェクトのクラス(をシミュレートしたもの)を定義するするものなので、生成するオブジェクトのクラス(をシミュレートしたもの)の名前をコンストラクタ関数名として使うのが一般的です。
オブジェクトのクラス(をシミュレートしたもの)を定義するには、コンストラクタを定義するだけでOKです。
プロトタイプと継承
オブジェクトのプロパティとして呼び出される関数をメソッドと呼びます。
function Rectangle(w, h) { //コンストラクタ関数の定義 this.width = w; this.height = h; } var r = new Rectangle(3, 7); //Rectangleオブジェクトの生成 r.area = function() { //Rectangleオブジェクトにメソッドを追加 return this.width * this.height; } var a = r.area(); //メソッドを呼び出して面積を計算 console.log("面積: " + a); //面積: 21 と表示される
この場合、追加されたメソッドはオブジェクト r(インスタンス)に追加されます。(クラスにではない)
コンストラクタの中で、面積を計算する関数を参照する(全てで共有される) area メソッドを追加する場合は以下のようになります。
function Rectangle(w, h) { //コンストラクタ関数の定義 this.width = w; this.height = h; this.area = function() { //メソッドの定義 return this.width * this.height; } } var r = new Rectangle(3, 7); //Rectangleオブジェクトの生成 var a = r.area(); //全てで共有されるメソッドを呼び出して面積を計算 console.log("面積: " + a); //面積: 21 と表示される
上記の場合、生成された全てのオブジェクトが3つのプロパティを持つちます。width と height は Rectangle オブジェクトごとに値が異なるのでそれぞれのプロパティとして持っているのは問題ありませんが、 全てで共有されるメソッドのために、全てのオブジェクトに対してプロパティを追加するのは非効率(メモリの無駄使い)になります。
全ての JavaScript オブジェクトには、プロトタイプオブジェクトというオブジェクトへの参照が含まれています。JavaScript のオブジェクトはプロトタイプからプロパティを継承するようになっています。
new 演算子を使うと、空のオブジェクトを生成した後、new 演算子はオブジェクトに対してプロトタイプを設定します。コンストラクタ関数の prototype プロパティの値がオブジェクトのプロトタイプになります。
全てのコンストラクタは定義時に自動的に prototype プロパティが生成され、値が設定されます(prototype プロパティの初期値は constructor プロパティしかプロパティがないオブジェクト)。このプロトタイプオブジェクトにプロパティを追加すると、コンストラクタで初期化された全てのオブジェクトで そのプロパティを使えるようになります。
//コンストラクタ関数は、インスタンスごとに異なるプロパティを初期化 function Rectangle(w, h) { this.width = w; this.height = h; } //全てで共有するプロパティについては、プロトタイプオブジェクトに追加 Rectangle.prototype.area = function(){ return this.width * this.height; } //継承されたプロパティは、通常のプロパティと同じように使用できる。 //プロパティが継承されたものかどうかを区別するには、Object.hasOwnProperty()メソッドを使う。 var r = new Rectangle(3, 7); console.log(r.hasOwnProperty("width")); //true width は r 直属のプロパティ console.log(r.hasOwnProperty("area")); //false area は r の継承されたプロパティ console.log("area" in r); //true area は r のプロパティ console.log("面積: " + r.area()); //面積: 21 と表示される
継承プロパティへのアクセス
それぞれのクラスごとにプロトタイプオブジェクトが1つずつあり、一連のプロパティが設定されます。オブジェクト「o」のプロパティ「p」を読み出す時に、JavaScript は最初に、オブジェクト「o」にプロパティ「p」があるかをチェックし、 ない場合は次に、オブジェクト「o」のプロトタイプオブジェクトにプロパティ「p」があるかをチェックします。
プロトタイプからプロパティ「p」を継承するオブジェクト「o」にプロパティ「p」を設定すると新しいプロパティ「p」が生成され、オブジェクト「o」に直接格納されます。その場合、オブジェクト「o」に専用のプロパティ「p」ができたので、以降はプロトタイプからプロパティ「p」の値を継承しません。オブジェクト「o」のプロパティ「p」がプロトタイプオブジェクトのプロパティ「p」を隠し(隠蔽)ます。
プロトタイププロパティは、あるクラスの全てのオブジェクトで共有されるので、そのクラスすべてのオブジェクトに対して 同一のプロパティを定義する場合に限って使用するのが一般的です。
以下は、文字列から前後の空白文字を削除する trim メソッドを String オブジェクトに追加する例です。すでに trim メソッドが存在する場合は何もしません。
if(!String.prototype.trim) { String.prototype.trim = function() { return String(this).replace(/^\s+|\s+$/g, ""); } } var str = " string.. "; console.log(str + "end"); // string.. end console.log(str.trim() + "end"); //string..end
JavaScript のクラス
JavaScript にはクラスという正式な概念はありません。
静的プロパティ(クラスプロパティ)/静的メソッド(クラスメソッド)
静的プロパティ/静的メソッドとは、インスタンスを生成しなくてもオブジェクトから直接呼び出せるプロパティ/メソッドのことです。(インスタンス経由で呼び出すプロパティ/メソッドはインスタンスプロパティ/インスタンスメソッドと呼ばれます)
静的プロパティ/静的メソッドを定義するには、以下のようにコンストラクタ(オブジェクト)に直接追加します。
オブジェクト名 . プロパティ名 = 値 オブジェクト名 . メソッド名 = function () { /* メソッドの定義 */ }
- JavaScript で静的プロパティ(クラスプロパティ)をシミュレートするには、コンストラクタ関数自身のプロパティを定義します。
- JavaScript で静的メソッド(クラスメソッド)を定義するには、関数をコンストラクタのプロパティとして設定します。
//コンストラクタ function Circle(radius){ this.r = radius; } //Circle.PIはクラスプロパティ Circle.PI = 3.14159; //円の面積を求めるインスタンスメソッド Circle.prototype.area = function() { return Circle.PI * this.r * this.r; } //2つのCircleオブジェクトを引数に取り半径の大きい方を返すクラスメソッド Circle.max = function(a,b){ if(a.r > b.r)return a; else return b; } var c1 = new Circle(1.0); //Circleクラスのインスタンスを生成 var c2 = new Circle(3.0); //もう1つのインスタンスを生成 c1.r = 5; //r インスタンスプロパティを設定(上記で初期化されているので実際は上書き) var a = c1.area(); //area()インスタンスメソッドの呼び出し console.log(a); //78.53975 var bigger = Circle.max(c1,c2); //max()クラスメソッドを使用 console.log(bigger); //Object { r: 5 }
オブジェクト共通メソッド
新しいクラスを定義する場合は、定義しておくべき以下のような関数があります。
toString() メソッド
オブジェクトを文字列に変換する適切な toString() メソッドを prototype に定義します。
Circle.prototype.toString = function() { return "[Circle of radius " + this.r + "]"; } console.log(c1.toString()); //[Circle of radius 5] //toString() メソッド未定義の場合は[object Object]
valueOf() メソッド
valueOf() メソッドは toString() メソッドと同じように使われます。
オブジェクトを文字列以外の基本型(数値が多い)に変換する必要があるときに valueOf() メソッドが自動的に呼び出されます。valueOf() メソッドは、this キーワードで参照するオブジェクトの「値」を表す基本型の値を返します。
オブジェクトはもともと基本型ではないので、通常対応する基本型はなく、 デフォルトの valueOf() メソッドは変換を行わず、呼び出しに使われたオブジェクトを返すだけです。クラスが適当な基本型を持つようにするには、そのクラス専用の valueOf() メソッドを定義するようにします。
Circle.prototype.valueOf = function() { return this.r; //半径rを返すように定義 } console.log(c1.valueOf()); //5
比較用のメソッド
JavaScript の等値演算子は、オブジェクトに対して「値による」比較ではなく「参照による」比較を行います(2つのオブジェクトが同じプロパティ名や値を持つかを比較するのではなく、2つのオブジェクト参照が同じオブジェクトを参照しているかどうかを比較にします)。値として2つのオブジェクトが等しいかどうかや、2つのオブジェクトの大小を比較するには、比較演算を行うメソッドをクラスの定義時に追加します。
Java では、オブジェクトを比較する場合にメソッドを使いますが、この方法を真似て以下のようなメソッドを必要に応じて定義します。
独自に定義したクラスで、何を持って「等しい」と見なすかはプログラマが決めることになります。
同じ値を持つかどうかをチェックするメソッド equals()
このメソッドは引数を1つ受け取り、メソッド呼び出しに使用されたオブジェクトが引数と等しい場合には true を返すようにします。一般的には、2つのオブジェクトの全てのインスタンスプロパティが同じ値を持てば、2つのオブジェクトは同じ値を持つと見なします。
Circle.prototype.equals = function(that) { return this.r == that.r; //半径が同じならtrueを返す } console.log(c1.equals(c2)); //false
2つのオブジェクトの大小を比較する compareTo()
「<」や「<=」のような JavaScript の比較演算子をオブジェクトに対して使うと、オブジェクトの valueOf() メソッドがまず呼出されて、 基本型の値が返ってくれば、その値で比較が行われます。
オブジェクトの大小を比較する場合は、compareTo() と云う名前のメソッドを定義します。 compareTo() メソッドは引数を1つ取り、メソッドが呼出されたオブジェクトと引数を比較し、 オブジェクトが引数よりも小さい場合は、負数を返し、引数よりも大きい場合は、 正数を返し、等しい場合は0を返します。
Circle.prototype.compareTo = function(that) { return this.r - that.r; //半径を比較 } console.log(c1.compareTo(c2)); //2
スーパークラスとサブクラス
JavaScript の場合 Object クラスが最も汎用的なもので、ほかのクラスは Object クラスのサブクラスと見なすことができます。Object クラスはすべての組み込みクラスのスーパークラスであり、そのほかのクラスは Object クラスから基本的なメソッドを継承します。
また、プロトタイプオブジェクトは Object.prototype からプロパティを継承します。例えば、Circle オブジェクトは Circle.prototype と Object.prototype の両方のオブジェクトからプロパティを継承します。
Circle オブジェクトのプロパティを探す場合、最初にこのオブジェクト(自身)を探し、見つからない場合は Circle.prototype オブジェクトを探し、 それでも見つからなければ Object.prototype オブジェクトを探します。
配列
JavaScript には本物の配列は用意されていませんが、配列に似た特性を持ったオブジェクトが用意されています。
JavaScript は型のない言語なので、配列の各要素にはどの型の値でも格納できます。 そのため、同じ配列でもそれぞれの要素ごとに型が違うことがあります。
配列の生成には以下の方法があります。
- Array()コンストラクタでの生成
- 配列リテラルでの生成
Array()コンストラクタでの生成
引数なしの呼び出し
var a = new Array(); //要素のない空の配列が生成される // var a = []; と同じ
JavaScript の標準ビルトインオブジェクトの一部は、new 演算子の省略が認められているので、上記は以下でも同じです。(ただし、new演算子を省略した場合に挙動が変わるものがあるので注意が必要です。)
var a = Array(); //要素のない空の配列が生成される
配列の要素を明示的に指定して呼び出す方法
var a = new Array(5,4,3,2,1,"test");
各要素が0からインデックスが付けられ、引数の個数が length プロパティに設定されます。 この場合、配列リテラルを使う方が簡単です。
1個の数値を引数として指定して呼び出す方法
var a = new Array(10); //配列では new は省略できるので以下でも同じです。 var a = Array(10);
指定された個数の空の要素の配列(空のスロットがある配列)が生成されます。各要素にアクセスすると値は未定義値になりますが、スロットに実際の undefined 値があるわけではありません。
var a = Array(10); console.log(a.length); //10 console.log(a); // (10) [empty × 10] console.log(a[0]); //undefined
配列リテラルでの生成
角括弧([])の中に配列の要素をカンマで区切って記述します。
配列リテラルは、一対の角括弧([])に囲まれ、コンマで区切られた一連のデータとして表現され、式のどこにでも記述することができます。そして先頭の値は「0」というプロパティ名を持ち、2つ目の値は「1」、というように連続した数値のプロパティ名が付けられます。
var empty = []; var numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine' ]; console.log(empty[1]); //undefined console.log(numbers[1]); //one console.log(empty.length); //0 console.log(numbers.length); //10
オブジェクトリテラルを使って記述した場合は、以下のようになります。
var numbers_object = { '0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine' };
配列リテラルとオブジェクトリテラルのどちらを使っても、よく似たオブジェクトを生成することができますが、両者には大きな違いがあります。 numbers は Array.prototype を継承していますが、numbers_object は Object.prototype を継承しています。そのため、numbers は配列を扱うのに便利なメソッドを継承しています。また、numbers には length というプロパティがありますが、numbers_object には存在しません。
配列の長さ
length プロパティに配列の要素の個数が格納されます。
※ length プロパティは常に配列の一番大きな添字より 1 大きい値になります。以下のように length は必ずしも配列中の要素の数ではないので注意が必要です。
var a = ['apple', 'banana', 'orange']; console.log(a.length); // 3 a[10] = 'melon'; console.log(a.length); // 11(配列の一番大きな添字より 1 大きい値)
他の言語とは異なり、JavaScript における配列ではサイズの上限はありません。 現在の length 値と同じか、それよりも大きい値を添え字とした要素にデータを格納すると、length プロパティは新しい要素を含むサイズにまで自動的に大きくなります。
var myArray = []; console.log(myArray.length); //0 myArray[100] = true; console.log(myArray.length); //101 //但し、myArrayにプロパティは1つしかない。他の要素の値は undefined var i; for(i = 0; i < myArray.length; i += 1){ console.log(myArray[i]); } //undefined が100回表示されr、101番目の値が true
配列の要素の追加
JavaScriptの場合、要素の個数に制限はなく、個数を自由に変更することができます。また、配列に値を代入するだけで、配列に新しい要素を追加することもできます。
length プロパティは、配列の最も大きな整数値を持つプロパティのプロパティ名に1を加えた値になりますが、それは必ずしも配列に存在する プロパティの数と一致するとは限りません。
var a = [1,2,3]; console.log(a.length); //3 a[10] = 10; console.log(a.length); //11
配列の長さの変更
length プロパティは、読み書き可能な値です。現在より小さな値を length プロパティに設定すると、配列が切り詰められ、この長さに収まらない要素は廃棄され、格納されていた値も失われます。現在より大きな値を設定すると、指定された長さになるまで、配列の最後に未定義値が追加されます。
var numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six']; numbers.length = 3; //numbersは['zero', 'one', 'two'] var i; for(i = 0; i < numbers.length; i += 1){ console.log(numbers[i]); //zero one two } //for in 文を使った場合 for (var i in numbers) { console.log(numbers[i]); //zero one two }
新しい要素を配列の最後に追加するには、配列の現在の length 値を使うことができます。
var numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six']; numbers.length = 3; //numbersは['zero', 'one', 'two'] numbers[numbers.length] = 'four'; //numbersは['zero', 'one', 'two', 'four']
上記と同じことをするのには、pushメソッドを利用した方が便利です。
numbers.push('five'); //numbersは['zero', 'one', 'two', 'four', 'five']
配列の要素の削除
JavaScript の配列は実際にはオブジェクトなので、配列から要素を削除するには、delete 演算子を使うことができます。
但し、この方法を使った場合、配列に「穴」が残ってしまいます(削除された要素は undefined になります)。これは削除された要素よりも右側にある(大きい) 要素のプロパティ名が変化しないためです。
delete 演算子を使った場合、配列の要素に未定義値を設定できますが、要素自身は削除されません。実際に配列の要素を削除するには配列のメソッド (Array.shift(), Array.pop(), Array.splice())を使います。
delete numbers[2]; //numbersは['zero', 'one', undefined, 'four', 'five']
配列を空にする(length に 0 を設定)
length に 0 を設定すると、配列のインスタンスを保ったまま中身を全て削除することができます。
var numbers = [1,2,3]; console.log(numbers); //[1, 2, 3] //length に 0 を設定して中身を全て削除 numbers.length = 0; console.log(numbers); //[]
要素の列挙
JavaScript の配列は実際にはオブジェクトなので for in 文を利用してプロパティを列挙することができます。但し、 for in 文はプロパティの順序 が保証されないという問題があり、しかも for in 文を使った場合、プロトタイプチェーン上に存在する不要なプロパティが取り出されてしまう 問題もあります。本来の for 文を使えば、これらの問題は避けることができます。
var i; for(i = 0; i < numbers.length; i += 1){ console.log(numbers[i]); }
配列かどうかの判定
typeof 演算子を使って配列の型を調べると「object」が返ってきてしまうので、役に立たちません。 JavaScriptには、オブジェクトと配列を見分けるための適切なメカニズムが存在しないので次のような関数を定義します。
var numbers = ['zero', 'one', 'two', 'three' ]; var numbers_object = {'0': 'zero', '1': 'one', '2': 'two', '3': 'three' }; var is_array = function(value){ return value && //渡された値が真かどうかを確認(nullや他のfalseな値を除外するため) typeof value === 'object' && //型が「object」であることを確認 //(これがtrueになるのはオブジェクト、配列、それにnull) value.constructor === Array; //constructorが「Array」であることを確認 } console.log(is_array(numbers)); //true console.log(is_array(numbers_object)); //false
この関数は異なるグローバルオブジェクト(ウィンドウやフレームなど)で作成された配列を識別することができないため、そうした外部からもたらされた配列も正確に判断するには以下のようにします。
var is_array = function(value){ return value && //渡された値が真かどうかを確認 typeof value === 'object' && //型が「object」であることを確認 typeof value.length === 'number' && //lengthプロパティを持っていて、それが数値であるかどうか typeof value.splice === 'function' && //spliceメソッドを持っているかどうか !(value.propertyIsEnumerable('length')); //lengthプロパティが列挙可能かどうか(for inループで取り出せるか) }
配列のメソッド
- join(): 配列の全ての要素を文字列に変換し連結
- reverse(): 配列の要素の順番を逆にする
- sort(): 配列の要素をソートする
- concat(): 配列に要素を追加して、新たな配列を生成
- slice() : 配列の一部を切り出した新しい配列(スライス)を返す
- splice() : 配列から要素を削除したり、配列に要素を挿入したり置換する
- push() : 配列の最後に1個または複数の要素を付加
- pop(): 配列の最後の要素を取り除く
- shift() : 先頭にある要素を取り除き、その取り除いた要素を返す
- unshift() : 配列の先頭に1個または複数の要素を付加し、元の要素を後ろにずらす
- toString(): 個々の要素を文字列に変換し、カンマで区切ったリストを出力
- indexOf(): ある値が配列内で最初に見つかった要素のインデックスを返す
- includes(): 特定の要素が配列に含まれているかどうかを真偽値で返す
- filter():配列の内容を特定の条件で絞り込み、条件に合致する要素から成る新たな配列を返す
- find():配列の内容を特定の条件で絞り込み、条件に合致する最初の要素を返す(ES6)
- fill():配列中の開始位置から終了位置までの要素を固定値で設定
- map(): 配列の各要素に対してコールバック関数を呼び出し、結果を含む配列を返す
- forEach(): 配列の各要素に対してコールバック関数を呼び出し実行する
- reduce(): 配列の各要素に対してコールバック関数を実行して単一の結果(累積値)を返す
- for of 文: メソッドではなく制御命令。配列などの iterable オブジェクトに対して反復処理を実行
Array.join()メソッド
配列の全ての要素を文字列に変換し連結するときに使用します。区切り文字を引数に指定することも可能で、指定しない場合はカンマが区切り文字として使われます。
array.join() array.join(separator)
join() メソッドは、array の各要素を文字列に変換し、 separator で指定された区切り文字を各要素間に挿入しながらそれらの文字列を連結し、その結果の文字列を返します。join() メソッドの逆方向の処理(文字列を配列要素に分割する場合)は、String オブジェクトの split() メソッドを使用します。もし、多数のばらばらなデータから文字列を生成する場合は、すべてのデータを + を使って連結するよりも、それらのデータを 配列に格納してから join() メソッドを利用したほうが、通常高速になります。
- 引数 separator
- 配列の要素を区切るために使用する文字あるいは文字列を指定します。 この引数を指定しなかった場合は、カンマが区切り文字として使用されます。 もし、セパレータを挟みたくなければ、引数に空文字を渡します。
- 戻り値
- array の各要素を文字列に変換し、separator で指定された区切り文字を各要素間に挿入して、 それらの文字列を連結したもの。
var a = [1,2,3]; var s1 = a.join(); var s2 = a.join(""); console.log(s1); //1,2,3 console.log(s2); //123
Array.reverse()メソッド
配列の要素の順番を逆にするときに使用します。要素を並べ替えて新しい配列を生成するのではなく、既存の配列の中で並べ替えます。
array.reverse()
reverse() メソッドは、配列の要素を逆順に並べ替えるメソッドです。 新たに配列を生成せずに、指定されたarrayの要素を逆順に並べ替えます。 array への参照が複数ある場合、配列要素の新しい順序はすべての参照に反映されます。
- 戻り値
- その配列自身を返す
var a = ['a', 'b', 'c']; var b = a.reverse(); //aとbはどちらも ['c','b','a']
Array.sort()メソッド
配列の要素をソートするときに使用します。reverse() メソッド同様新しい配列を生成するのではなく、既存の配列の中でソートするだけです。
array.sort() array.sort(orderfunc)
- 引数 orderfunc
- ソート順を指示する関数(比較関数)を指定
引数を指定せずに sort() メソッドを呼び出すと、アルファベット順にソートされます(厳密には文字エンコーディングにより順序が決まります)。 各要素は、必要に応じて文字列に変換されて、大小が比較されます。 アルファベット順以外のソート方法を使用したい場合は、比較関数を指定して、2つの値を比較し、両者の大小関係を示す値を返すようにする必要があります。 この比較関数は2つの引数を取り、これらの引数を a と b とすると、以下のようにします。
- 「a が b より小さい」と判定される場合は、ゼロより小さな値を返す。
その結果、b の前に a が来るように整列する。 - 「a と b が等しい」と判定される場合は、ゼロを返す。
- 「a が b がより大きい」と判定される場合は、ゼロより大きな値を返す。
- 戻り値
- その配列自身を返す
var n = [4, 8, 15, 16, 23, 42]; n.sort(); console.log(n); //n は [15, 16, 23, 4, 42, 8] //数値を正しくソートできないので比較関数を指定する。 n.sort(function(a,b){ if(a < b)return -1; //先頭の引数を2番目の引数より前にする場合,返す値をゼロより小さな値にする if(a > b)return 1; //先頭の引数を2番目の引数より後ろにする場合,返す値をゼロより大さな値にする return 0; //2つの値が同じ場合,返す値をゼロする }); console.log(n); //[ 4, 8, 15, 16, 23, 42 ] //上記は以下のように書き換えられる。 n.sort(function(a,b){ return a < b ? -1 : 1; }) //または、下記のように簡単に書き換えられる。 n.sort(function(a,b){ return a-b; //a-bの値によりソートされる }); //a-bが正の値の場合:a(先頭の引数)はb(2番目の引数)の後ろへ //a-bが負の値の場合:a(先頭の引数)はb(2番目の引数)の前へ
上記の関数は数値をソートしますが、今度は文字列をソートできません。単純な値を持つ配列をソートできる関数を作るには以下のようにします。
var m = ['aa', 'bb', 'a', 4, 8, 15, 16, 23, 42]; m.sort(function(a,b){ if(a === b){ return 0; } if(typeof a === typeof b){ return a < b ? -1 : 1; } return typeof a < typeof b ? -1 : 1; }); console.log(m); //m は[4, 8, 15, 16, 23, 42, 'a', 'aa', 'bb']
もし大文字小文字の区別をつけたくない場合は、比較関数において実際の比較の前にオペランドを小文字に変換しておきます。
Array.concat()メソッド
配列に要素を追加して、新たな配列を生成します。
array.concat(value,...)
concat() メソッドを使うと、引数の各値を array に連結した新しい配列を返してくれます。 配列は新たに作成されるので、元の配列は影響を受けません。
- 引数 value,...
- array に連結する値(複数可)
- 戻り値
- 引数の各値を array の後ろに連結した新しい配列を返します
var a = [1,2,3]; var b = a.concat(4,5); //bは[1,2,3,4,5] var c = a.concat([4,5]); //cは[1,2,3,4,5] var d = a.concat([4,5],[6,7]); //dは[1,2,3,4,5,6,7] var e = a.concat(4,[5,[6,7]]); //eは[1,2,3,4,5,[6,7]] var f = a.concat("four", true, false); //fは[1,2,3,"four",true,false]
Array.slice() メソッド
配列の一部を切り出した新しい配列(スライス)を返します。
array.slice(start,end)
slice() メソッドは、array の一部を切り出した新しい配列を返します。 新しい配列には元の配列の start 番めから end-1 番めの要素が含まれます。 endが指定されない場合は、返される配列には start から配列の最後までの要素がすべて含まれます。 このメソッドは元の配列には影響を与えません。 もし、start の値が array.length と等しいか、より大きかった場合、空の配列が返されます。 配列から切り出した要素を元の配列から取り除きたい場合は、Array.splice() メソッドを使います。
- 引数 start
- 切り出す部分の最初のインデックス(最初は0)。 この値が負の場合は、配列の最後から数えたインデックスを表すことになります。 つまり、-1は最後の要素のインデックスを表し、 -2は最後から2番目の要素のインデックスを表します。省略した場合は 0(インデックス 0 から開始)。
- 引数 end
- 切り出す部分の最後のインデックス(この要素は含まれない)。 指定されない場合は、start から配列の最後まですべての要素が含まれます。 負の場合は、配列の最後から数えたインデックスを表すことになります。省略した場合は最後 (array.length) までを取り出します。
- 戻り値
- array の start から end までの要素を含んだ新しい配列を返します。
var a = ["A", "B", "C", "D", "E"]; var a1 = a.slice(0,3); //[ "A", "B", "C" ] var a2 = a.slice(3); //[ "D", "E" ] var a3 = a.slice(1, -1); //[ "B", "C", "D" ] var a4 = a.slice(-3, -2); //[ "C" ]
call() を使って slice メソッドを引数なしで呼び出すことで、配列のようなオブジェクトを新しい配列に変換することができます。
以下は配列のようなオブジェクト arguments から新しい配列を生成する関数の例です。
function list_args() { return Array.prototype.slice.call(arguments); } console.log(list_args('a','b', 3)); // ["a", "b", 3]
document.querySelectorAll() はセレクタにマッチする全ての要素の NodeList(配列のようなオブジェクト)を返しますが、以下のようにすれば配列に変換することができます。マッチする要素がない場合は空の配列を返します。
Array.prototype.slice.call(document.querySelectorAll(セレクタ) || []);
以下はクラス foo の要素全てを取得した NodeList を配列に変換して変数 foos に格納して、配列のメソッド forEach を使って要素のテキストを出力する例です。
let foos = Array.prototype.slice.call(document.querySelectorAll('.foo') || []); foos.forEach(function(elem) { console.log(elem.textContent); });
以下のように変数に入れずにつなげて記述することもできます。
Array.prototype.slice.call(document.querySelectorAll('.foo') || []).forEach(function(elem) { console.log(elem.textContent); }) ;
Array.splice() メソッド
配列から要素を削除したり、配列に要素を挿入したりする汎用的メソッドです(削除と挿入を同時に実行も可能)。slice() や concat() と異なり、新たな配列を生成しません。
array.splice(start, deleteCount, value, ...)
splice() メソッドは start から0個以上の要素を取り除き、引数で指定された0個以上の値で置き換えます(start も含まれます)。 挿入、削除が行われたインデックスよりも後ろにある配列の要素は、要素が連続して並ぶように適宜移動されます。
- 引数 start
- 挿入または削除を開始する位置。
- 引数 deleteCount
- start も含めた削除する要素の数。 省略した場合、splice() は start から配列の最後までのすべての要素を削除します。
- 引数 value, ...
- array の start から始まる場所に挿入したい0個以上の値(要素)。
- 戻り値
- arrayから削除された要素を含んだ配列。
var a = [1,2,3,4,5,6,7,8]; var a1 = a.splice(4); console.log(a); //[1,2,3,4] console.log(a1); //[5,6,7,8] var a2 = a.splice(1,2); console.log(a); //[1,4] console.log(a2); //[2,3]
最初の2つの引数は削除の指定をするものです。その後に引数が指定された場合は、1番目の引数で指定された位置に挿入する要素になります。
var a = ['a', 'b', 'c']; var r = a.splice(1, 1, 'apple', 'orange'); //a は["a""apple","orange","c"] //r は ["b"]
要素を削除しない(挿入のみの)場合は2番目の引数(削除する要素の個数)を0にします。
var a = [1,2,3,4,5]; a.splice(2,0,'a','b'); //[]が返され、aは[1,2,'a','b',3,4,5] a.splice(2,2,[1,2],3); //['a','b']が返され、aは[1,2,[1,2],3,3,4,5] //splice()メソッドでは concat() と異なり、 //配列要素を展開しないでそのまま(配列そのものを)挿入します。
Array.push() メソッド
配列の最後に1個または複数の要素を付加し、付加した後の配列の長さを返します。
array.push(value, ...)
push() メソッドは引数を順番に array の最後に付け加えます。このメソッドは、新しい配列を作らずに、直接 array を書き換えます。 また、配列は展開されず、そのまま追加されます。 push() メソッドと、対になっている pop() メソッドを使うと、先入れあと出し(FILO)というスタックの機能を提供します。
- 引数 value,...
- arrayの最後に付け加える値(複数可)
- 戻り値
- 配列の新しいサイズ
var a = ['a', 'b', 'c']; var b = ['x', 'y', 'z']; var c = a.push(b, true); //a は['a', 'b', 'c',['x', 'y', 'z'], true] //c は5
Array.pop() メソッド
配列の最後の要素を取り除き、その要素を返します。
array.pop()
pop() メソッドは array の最後の要素を取り除き、配列の大きさを1つ小さくし、取り除いた要素を返します。pop と push の2つのメソッドを使うと配列をスタックのように利用できます。
- 戻り値
- array の最後の要素。配列が空だった場合は undefined を返す。
var stack = []; stack.push(1,2); //stack:[1,2] になり、2(サイズ)が返される stack.pop(); //stack:[1] になり、2(最後の要素)が返される stack.push([4,5]); //stack:[1,[4,5]] になり、2(サイズ)が返される stack.pop(); //stack:[1] になり、[4,5](最後の要素)が返される
Array.shift() メソッド
配列の先頭の要素を削除し、削除した要素の値を返し、削除されて空になった場所へ後続の要素をずらします。
array.shift()
- 戻り値
- 取り除いた要素(このメソッドを実行する前の先頭の要素)
shift() メソッドは array の先頭にある要素を取り除き、その取り除いた要素を返します。 その際、残った要素すべてを前にずらし、空いた空間を埋めます。 shift() メソッドは新しい配列を生成せずに、直接 array を書き換えます。 shift() メソッドは Array.pop() メソッドに似ていますが、最後の要素ではなく、最初の要素を操作するという点が異なり、 一般的に pop メソッドよりも遅い。
var a = ['a', 'b', 'c']; var c = a.shift(); console.log(a); //['b', 'c'] console.log(c); // a
Array.unshift() メソッド
配列の先頭に1個または複数の要素を付加し、既存の配列要素をより大きいインデックス番号のほうへシフトさせて、 付加した後の配列の長さを返します。
array.unshift(value, ...)
unshift() メソッドは元の要素を後ろにずらして array の先頭に引数を挿入します。unshift() メソッドの最初の引数は、 array のインデックス0の要素になります。そして、2番目の引数はインデックス1の要素になり、3番目以降も同様になります。 unshift() メソッドは新しい配列を生成せずに、直接 array を書き換えます。
- 引数 value, ...
- array の先頭に挿入したい1個以上の値
- 戻り値
- array の新しい大きさ
var a = ['a', 'b', 'c']; var r = a.unshift('#', '@'); console.log(a); //['#', '@', 'a', 'b', 'c'] console.log(r); // 5(サイズ)
toString()メソッド
配列を指定して toString() メソッドを実行すると、必要に応じて各要素の toString() メソッドを呼び出し、 個々の要素を文字列に変換し、カンマで区切ったリストを出力します。(出力されたリストには角括弧([])などの配列の区切り記号は含まれません。)引数を指定しないで join() メソッドを呼び出した場合も、同じ文字列が返されます。
[1,3,5].toString(); //'1,3,5'が出力される ["a", "b", "c"].toString(); //'a,b,c'が出力される [1,[2,'c']].toString() //'1,2,c'が出力される
indexOf() メソッド
指定された値を配列で検索します。メソッドは、最初に見つかった要素のインデックスを返します。指定された値が見つからない場合は -1 を返します。
var index = array.indexOf(searchElement[, fromIndex]);
- searchElement
- array 内で検索される値(配列要素)。(必須)
- fromIndex
- 検索の開始位置を示す配列インデックス。 fromIndex を省略すると、検索はインデックス 0 から開始されます(配列全体が検索されます)。
- 戻り値
- searchElement が配列内で最初に見つかった要素のインデックス。searchElement が見つからなかった場合は -1。
引数に与えられた配列要素は === 演算子のような厳密等価で searchElement と比較されます。
var array = [2, 5, 9]; var index = array.indexOf(2); console.log(index); // index は 0 index = array.indexOf(7); console.log(index); // index は -1
Array.includes() メソッド
includes() メソッドは引数に指定された特定の要素が配列に含まれているかどうかを真偽値(true または false)で返します。
String(文字列)にも includes() メソッドがあり、文字列に指定した値が含まれているかどうかを真偽値で返し、使い方はほぼ同じです。
文字列や文字を比較する場合、大文字と小文字を区別します。また、false は 0 と同じとはみなされません。
arr.includes(valueToFind[, fromIndex])
- valueToFind
- array 内で検索する値。(必須)
- fromIndex
- 検索の開始位置を示す配列インデックス。 デフォルトは 0(配列全体が検索されます)。
- 戻り値
- 指定した値(valueToFind)が配列に含まれていれば true を、含まれていなければ false を返します。
var array = [1, 2, 3, 'apple', 'banana']; console.log(array.includes(2)); //true console.log(array.includes(2,2)); // false console.log(array.includes('app')); // false console.log(array.includes('apple')); //true //文字列 var string = 'JavaScript'; console.log(string.includes('av')); //true console.log(string.includes('av', 4)); //false console.log(string.includes('j')); //false
Array.filter() メソッド
filter() メソッドは配列内の全ての要素をコールバック関数で評価し、指定した条件に合致する要素から成る新たな配列を返します。
var newArray = array.filter(callback [, thisArg])
- 引数 callback
- 配列の各要素に対して実行するコールバック関数。この関数が true を返した要素が新しい配列として生成され、false を返した要素は除外されます。この関数には以下の三つの引数が与えられます。
- element: 配列内の現在の要素
- index:配列内の現在の要素のインデックス
- array:filter() メソッドを実行している配列
- 引数 thisArg
- callback を実行するときに this として使用するオブジェクト(オプション)
- 戻り値
- コールバック関数が true を返した要素からなる新しい配列。一つの要素も条件に一致しなかった場合は、空の配列が返されます。
以下は 100 未満の値を持つ要素を取り除いた配列を生成する例です。
var array = [120, 78, 9, 273, 33]; var filtered = array.filter(function(value) { return value > 100; }); console.log(filtered); //Array [ 120, 273 ]
以下は、コールバック関数を別途定義して、偶数の要素のみを抽出する例です。
var array = [0, 3, 6, 9, 11, 24]; function isEven(value) { return value % 2 === 0; } var filtered = array.filter(isEven); console.log(filtered); //Array(3) [ 0, 6, 24 ]
以下は、検索条件で配列の絞り込みをしています。関数 filterNames() には、検索する文字(query)と対象の配列(array)を引数として渡し、検索文字列が含まれている要素の配列を返します。
var fruits = ['apple', 'pineapple', 'orange', 'strawberry', 'blueberry']; var fish = ['Tuna', 'Yellow Tail', 'Red Snapper', 'Squid'] ; function filterNames(query, array) { return array.filter(function(el) { return el.toLowerCase().indexOf(query.toLowerCase()) > -1; }) } console.log(filterNames('apple', fruits)); //Array [ "apple", "pineapple" ] console.log(filterNames('na', fish)); // Array [ "Tuna", "Red Snapper" ]
以下は、第2引数(thisArg)のオブジェクトを指定する例です。
配列の値とオブジェクトのプロパティ名を比較して、同じ値があれば抽出してその配列を返す例です。
var fruitsArray = ['apple', 'pineapple', 'orange', 'strawberry', 'blueberry']; //配列 var fruitsObject = {melon:'$10.00', apple:'$3.00', blueberry:'$5.00'}; //オブジェクト var commonFruits = fruitsArray.filter(function(value){ //for in 文 オブジェクトに含まれるプロパティを順に取り出す for( var prop in this ) { //オブジェクトのプロパティ名と配列の値を比較 if( prop === value ) return value; } }, fruitsObject) // 第2引数に this として使用するオブジェクトを指定 console.log(commonFruits); //Array [ "apple", "blueberry" ]
条件に合致する最初の要素を取得するには ES6 で導入された find() が使えます。
Array.fill() メソッド
fill() メソッドは、配列中の開始位置から終了位置までの要素を固定値で設定します(同じ値で埋めます)。
array.fill(value[, start[, end]])
- 引数 value
- 配列に設定する値
- 引数 start(オプション)
- 開始する位置。既定値は 0
- 引数 end(オプション)
- 終了する位置。既定値は this.length。length は配列の一番大きな添字より1大きい値です。
- 戻り値
- 変更された配列(配列自身を変更します)。
var array1 = [1,2,3]; array1.fill(4); console.log(array1); // [4, 4, 4] var array2 = ['a', 'b', 'c']; array2.fill('x', 1); console.log(array2); // ["a", "x", "x"] var array3 = [0,1,2,3,4,5]; array3.fill(7, 2, 4); //終了位置は含まれない console.log(array3); // [0, 1, 7, 7, 4, 5] //0 で埋めた5つの要素の配列を生成 var array4 = Array(5).fill(0); console.log(array4); // [0, 0, 0, 0, 0]
以下は要素を3つ持つ配列を生成して全てに undefined を設定する例です。
単純に Array(3) としても同じような気がしますが、Array(3) は空のスロットが3つあり、undefinedで 埋められているわけではありません。
var a = Array(3).fill() console.log(a.length); //3 console.log(a); // [undefined, undefined, undefined] console.log(a[0]); //undefined var b = Array(3); console.log(b.length); //3 console.log(b); // [empty × 3] 空のスロットが3つ console.log(b[0]); //undefined アクセスすると undefined が返る
Array.map() メソッド
map() メソッドは与えられたコールバック関数を配列の順番通りに、各要素に対して呼び出し、その結果から新しい配列を生成します。
var newArray = array.map(callback [, thisArg])
- 引数 callback
- 配列の各要素に対して実行するコールバック関数。各要素に対してこの関数を実行して、その結果を新しい配列として返します。この関数には以下の三つの引数が与えられます。
- value:現在処理中の要素の値
- index:現在処理中の要素の配列内におけるインデックス
- array:map() メソッドを実行している配列
- 引数 thisArg
- callback を実行するときに this として使用するオブジェクト(オプション)
- 戻り値
- 与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列。
以下は、文字列(単語)からなる配列を、コールバック関数でその文字数による新しい配列を作成する例です。
var fruits = ["pineapple","grape","melon", "strawberry"]; var lengths = fruits.map(function(fruit) { return fruit.length; }); console.log(lengths); //Array(4) [ 9, 5, 5, 10 ]
以下は、配列の要素の各文字列を大文字に変換した新しい配列を作成する例です。
var fruits = ['apple', 'grape', 'melon'] var upperCaseArray = fruits.map(function(value) { return value.toUpperCase(); }); console.log(upperCaseArray) // Array(3) [ "APPLE", "GRAPE", "MELON" ]
以下は、数値からなる配列をコールバック関数で、その値の2乗の要素の新しい配列を作成する例です。
var numbers = [1,2,3]; var squares = numbers.map(function( value ) { //配列の各要素の二乗の計算 return value * value; }); console.log(squares); //Array(3) [ 1, 4, 9 ]
上記を配列の for 文を使って書くと以下のようになり、同じ結果が得られます。
但し、map() を使ったほうが簡単で記述も簡潔になります。また、for 文は結果を返してくれませんが、map() は結果の配列を返してくれます。
var numbers = [1,2,3]; var squares = []; var value; for (var i = 0; i < numbers.length; i++){ value = numbers[i]; squares.push(value * value); } console.log(squares); //Array(3) [ 1, 4, 9 ]
以下は、前述の例をアロー関数(=>)を使って記述した例です。かなり簡潔になります。
var numbers = [1,2,3]; var squares = numbers.map(value => value * value); console.log(squares); //Array(3) [ 1, 4, 9 ]
アロー関数とは => (アロー/矢印)を使って関数リテラルを記述します。アロー関数の基本的な書式は以下のようになっています。
(引数,...) => {...関数の本体(処理の記述)...}
以下は、偶数版目の要素(index が奇数の要素)の値を2倍にした配列を作成する例です。index は 0 から始まります。
var result = [1,2,3,4,5].map( function( value, index, array ) { //index が奇数の時だけ2倍に(index は0から始まる) if( index % 2 === 1 ) { return value * 2; }else{ return value; } }); console.log(result); //Array(5) [ 1, 4, 3, 8, 5 ]
以下は、2 つの配列からオブジェクトの配列を作成する例です。
ES2015 (ES6) 以降(※)では、オブジェクトのキーに変数を入れて展開できるようになっています。その際に、キーは [ ] で囲む必要があります。
var fruits = ['Apple', 'Banana', 'Orange']; var prices = ['$2.00', '$0.50', '$1.25']; var fruitPrices = fruits.map(function(value, i){ //オブジェクトのキーに変数を使う場合は、[ ] で囲む return {[value]: prices[i]}; }); console.log(fruitPrices); //Array(3) [ {…}, {…}, {…} ] //Object { Apple: "$2.00" } //Object { Banana: "$0.50" } //Object { Orange: "$1.25" }
※ IE11 は ES2015 (ES6) に対応していません。(caniuse ES6)
そのため、IE11 で上記を実行すると「SCRIPT1028: Expected identifier, string or number」というエラーになります。
IE11 に対応するには、以下のようにします。(参考:オブジェクトのキーに変数を使う)
var fruits = ['Apple', 'Banana', 'Orange']; var prices = ['$2.00', '$0.50', '$1.25']; var fruitPrices = fruits.map(function(value, i){ var obj = {}; //オブジェクトを定義 obj[value] = prices[i]; //[] 演算子を使ってキーに変数を指定 return obj; }); console.log(fruitPrices); //Array(3) [ {…}, {…}, {…} ] //Object { Apple: "$2.00" } //Object { Banana: "$0.50" } //Object { Orange: "$1.25" }
Array.forEach() メソッド
forEach() メソッドは、与えられたコールバック関数(callback)を、配列の各要素に対して一度ずつ実行します。
array.forEach(callback[, thisArg])
- 引数 callback
- 配列の各要素に対して昇順で呼び出して実行するコールバック関数。すでに削除されたインデックスや、まだ値が代入されていないインデックスに対しては呼び出されません。この関数には以下の三つの引数が与えられます。
- value:現在処理中の要素の値
- index:現在処理中の要素の配列内におけるインデックス
- array:forEach() メソッドを実行している配列
- 引数 thisArg
- callback を実行するときに this として使用するオブジェクト(オプション)。
- 戻り値
- undefined
forEach() は呼び出された配列を変化させません。(コールバック関数の第3引数を使えば変更することもできます)
for 文に似ていますが、処理を中断する break や continue は使えません(エラーになります)。
以下は、コールバック関数の第1引数を使って、配列の要素の値を出力する例です。
var fruits = ["apple", "melon", "orange", "grape"]; fruits.forEach(function(val) { console.log(val); }); /* 以下が出力されます。*/ //apple //melon //orange //grape
以下は、前述の例とほぼ同じですが、配列の要素がオブジェクトの場合の例です。値がオブジェクトなので、そのキーを指定して値を取得しています。
var fruits = [ {name: "apple", price: "$1.00"}, {name: "melon", price: "$5.00"}, {name: "orange", price: "$2.00"}, {name: "grape", price: "$3.00"}, ]; fruits.forEach(function(val) { console.log(val.name + " : " + val.price); }); /* 以下が出力されます。*/ //apple : $1.00 //melon : $5.00 //orange : $2.00 //grape : $3.00
以下は、配列 items の要素をコピーして、コピーした配列 copy の要素を出力する例です。
var items = ['item1', 'item2', 'item3']; var copy = []; items.forEach(function(item){ copy.push(item) }); copy.forEach(function(val){ console.log(val); }); /* 以下が出力されます。*/ //item1 //item2 //item3
以下は、偶数のインデックスの場合(奇数の要素の場合)、値を2倍にする例です。この例の場合、コールバックの第3引数(array)を使って、その配列自体の要素を変更しています。(インデックスは0から始まります)
var numbers = [1,2,3,4,5]; numbers.forEach(function(number, index, array) { if(index % 2 === 0) { number = number * 2; } array[index] = number; }); numbers.forEach(function(number) { console.log(number); //元の配列の内容を確認 }); /* 以下が出力されます。*/ //2 //2 //6 //4 //10
Array.reduce() メソッド
reduce() は配列の各要素に対してコールバック関数を実行して単一の結果(累積値)を返します。
array.reduce(callback( accumulator, currentValue, index, array ), initialValue)
- 引数 callback
- 配列の各要素に対して実行される関数。この関数には以下の4つの引数が与えられます。
- accumulator:コールバック関数の演算結果を累積する変数のようなもの
- currentValue:現在処理されている配列の要素(の値)
- index:(オプション)現在処理中の要素のインデックス。initialValue が指定された場合は 0 から、そうでない場合は 1 から開始
- array:(オプション)reduce() が呼び出された配列
- 引数 initialValue
- (オプション)accumulator の初期値(コールバック関数の最初の呼び出しで使用する値)。initialValue を省略すると、配列の最初の要素が accumulator の初期値として使用されます。また、空の配列に対して reduce() を呼び出した際に initialValue が指定されていないと TypeError が発生します。
- 戻り値
- accumulator と配列の各要素を順番にコールバック関数へ渡して処理した結果(累積値)。配列やオブジェクトなども可。
以下は reduce() を使って配列の全ての要素を加算した値を返す例です。
var result = [1, 2, 3, 4, 5].reduce(function(accumulator, currentValue, index) { return accumulator + currentValue }) console.log(result); //15
第2引数の initialValue が省略されているので、コールバック関数の最初の呼び出しでは、accumulator の値は配列の最初の要素 1 が、currentValue は配列の2番めの要素 2 になります。
2回めの呼び出しでは、accumulator には前回の演算 accumulator + currentValue の結果の 3 が、currentValue は配列の3番めの要素 3 になります。
呼び出し | accumulator | currentValue | 返り値 | index |
---|---|---|---|---|
初回 | 1 | 2 | 3 | 1 |
2回目 | 3 | 3 | 6 | 2 |
3回目 | 6 | 4 | 10 | 3 |
4回目 | 10 | 5 | 15 | 4 |
以下は initialValue を指定して、配列の全ての要素を加算した値を返す例です。
var result2 = [1, 2, 3, 4, 5].reduce(function(accumulator, currentValue, index) { return accumulator + currentValue }, 10) console.log(result2); //25
呼び出し | accumulator | currentValue | 返り値 | index |
---|---|---|---|---|
初回 | 10 | 1 | 11 | 0 |
2回目 | 11 | 2 | 13 | 1 |
3回目 | 13 | 3 | 16 | 2 |
4回目 | 16 | 4 | 20 | 3 |
5回目 | 20 | 5 | 25 | 4 |
以下は reduce() を使って渡された配列内の最大値を返す例です。
var max = [7, 6, 23, 5, 1].reduce(function(x, y) { return x > y ? x : y; }); console.log(max); //23 //関数にした場合の例 function my_max(array) { return array.reduce(function(x, y) { return x > y ? x : y; }); } console.log(my_max([7, 6, 23, 5, 1])); //23
以下はオブジェクトの配列に含まれた値を加算する例です。各要素の値は arry[n].val で取得できます。
この場合、initialValue を指定しないと最初の要素(オブジェクト)が accumulator の初期値として使用され、数値として正しく処理できないため、第2引数の initialValue(初期値)を指定する必要があります(以下では変数に入れていますが、直接 0 を指定しても同じです)。
var arry = [{val:1}, {val:2}, {val:3}]; //オブジェクトの配列 var initialValue = 0; //初期値を指定 var result = arry.reduce(function(accumulator, currentValue) { return accumulator + currentValue.val //オブジェクトのプロパティ .val を加算 }, initialValue); console.log(result); // 6
以下はオブジェクトの配列 memebers からメールアドレス(email)だけの配列を作成する例です。
initialValue(初期値)に空の配列 [ ] を指定しているので1回目の呼び出し(ループ)での accumulator の値は [ ] になります。
コールバック関数では concat() を使って配列に要素を追加しています。
var memebers = [ { name: 'Foo', email: 'foo@example.com'}, { name: 'Bar', email: 'bar@example.com'}, { name: 'Buz', email: 'buz@eexample.com'} ] var emails = memebers.reduce((accumulator, currentValue) => { //concat() で配列に要素を追加 return accumulator.concat(currentValue.email); },[]) //initialValue に空の配列を指定 console.log(emails); // ["foo@example.com", "bar@example.com", "buz@eexample.com"]
for of 文
for of 文は ES6 (ES2015) で導入された制御命令で、配列などの iterable オブジェクトに対して反復処理を実行することができます。
参考:MDN for...of / イテレーターとジェネレーター
以下が構文です。
for (variable of iterable) { statement }
iterable に指定した配列などの要素を1つずつ variable に取り出して処理を繰り返し実行します。
- variable
- 任意の変数(名)。反復処理の際に、iterable の要素の値がこの変数に割り当てられます。variable は const, let, var で宣言することができます。
- iterable
- 反復処理を行う対象の配列などの反復可能なプロパティを持つオブジェクト
以下は配列 array1 の値をコンソールに出力する例です。
var array1 = ['apple', 'banana', 'orange']; //配列の各要素を反復処理 for (var value of array1) { console.log(value); } //出力結果 apple banana orange
文字列も iterable オブジェクトなので、for of 文を使うことができます。
const bar = 'bar'; //文字列の各要素を反復処理 for (let value of bar) { console.log(value); } //出力結果 b a r
以下は querySelectorAll() で取得した .bar の NodeList の各要素を出力する例です。querySelectorAll() で取得した NodeList も iterable オブジェクトです。
<div class="bar">Div</div> <h3 class="bar">Heading</h3> <p class="bar">Paragraph</p> <script> //クラス bar の要素(NodeList)を取得 var nodeList =document.querySelectorAll('.bar'); for (var elem of nodeList) { console.log(elem); } </script> <!-- 以下が出力結果 <div class="bar">Div</div> <h3 class="bar">Heading</h3> <p class="bar">Paragraph</p> -->
for of 文と for in 文
for of 文は反復処理を行う対象のオブジェクト(iterable オブジェクト)が定義している順序で値を反復処理しますが、for in 文はそのオブジェクトの列挙可能なプロパティのみを順序不定で出力します。
以下は前述の NodeList を for in 文を使って同様の処理を実行した場合です。
<div class="bar">Div</div> <h3 class="bar">Heading</h3> <p class="bar">Paragraph</p> <script> //クラス bar の要素(NodeList)を取得 var nodeList =document.querySelectorAll('.bar'); //for in 文 for (var elem in nodeList) { console.log(elem); } </script> <!-- 出力結果(列挙可能なプロパティが出力される) 0 //値は出力されずインデックスのみ 1 2 length //NodeList のプロパティ item //NodeList のメソッド(以下同じ) entries forEach keys values -->
組み込みオブジェクト
JavaScript のデータ型には以下があります。
- 基本型(プリミティブ)
-
- 数値型(Number):整数や小数
- 文字列型(String):(') または (")で囲まれた0個以上の文字の集まり
- 真偽型(Boolean):true と false
- 特殊型:null と undefined
- 参照型
-
- 配列(Array)
- オブジェクト(Object)
- 関数(Function)
数値、文字列、論理(真偽)値、null、undefined がプリミティブ値、それ以外がオブジェクト(参照型)というように2つに分類でき、プリミティブ値はプロパティを持ちません。
但し、数値、文字列、論理値の3つのプリミティブ値は、コンテキストによってプロパティを使う(参照する)ことができます。
文字列はプリミティブ値ですが、以下のようにコンテキストによっては、プロパティにアクセスすることができます。
この時に使い捨てで呼び出される String オブジェクトをラッパーオブジェクトと呼びます。
var name = "John"; console.log(name.length); //4 console.log(name.slice(0,2)); //Jo
感覚的には以下のように、new 演算子を使ってオブジェクトを生成するような感じですが、通常は基本データ型を new 演算子を使ってインスタンス化する必要はなく、返って有害のことが多いので避けるようにします。
(new String("John")).length;
- ラッパーオブジェクト
- 数値、文字列、論理値を扱うための(使い捨てで呼び出される)オブジェクトのことをラッパーオブジェクトと呼びます。ラッパーオブジェクトは基本型のデータ(値)をラップしてオブジェクトとしての機能を追加するためのオブジェクトで、JavaScript では基本データ型とラッパーオブジェクトを自動的に相互変換するので、通常これを意識する必要はありません。
プリミティブ値(基本型)とオブジェクト(参照型)
基本型と参照型の違いは、基本型の変数には、値そのものが格納されますが、参照型の変数には、その参照値(値を実際に格納しているメモリ上のアドレス)を格納している点です。
言い換えると、代入や関数の引数にした場合に値渡しになるか、参照渡しになるかの違いがあります。
プリミティブ値は、値渡しになります。
var ap = 'a,b,c'; var bp = ap; bp = '1,2,3'; console.log(ap); // a,b,c console.log(bp); //1,2,3
オブジェクトは、参照渡しになります。参照渡しなので、値(この例ではArray)を変更すると、元となった値も変更されます。
var ao = ["a","b","c"]; var bo = ao; bo[0] = 1; console.log(ao); // [1,"b","c"] console.log(bo); // [1,"b","c"]
組み込みオブジェクト
JavaScript には以下のような複数の組み込みのオブジェクト(Built-in Object/ビルトインオブジェクト)があります。
- Global オブジェクト(通称)
- Object オブジェクト
- Array オブジェクト
- String オブジェクト
- Boolean オブジェクト
- Number オブジェクト
- Function オブジェクト
- Math オブジェクト
- Date オブジェクト
- RegExp オブジェクト
- Error オブジェクト
組み込みオブジェクトは、JavaScritp に予め組み込まれたオブジェクトで、ブラウザオブジェクトがブラウザ上でしか動作しないのに対して、組み込みオブジェクトは JavaScript が動作する全ての環境で利用できます。
組み込みオブジェクトは、特別な宣言や定義を行うことなく利用できます。自分で定義するオブジェクトの場合、インスタンス化を行う必要がありますが、組み込みオブジェクトの場合インスタンス化を意識する必要はありません。
基本データ型でも、以下のように new 演算子を使ってオブジェクトを生成することはできますが、ほとんどの場合、これは冗長であるだけで、返って有害なことが多いので避けるようにします。
var str = new String("Hello"); //基本データ型を new 演算子でインスタンス化するのは避ける
グローバルオブジェクト
Global オブジェクトは直接使用されることがなく、new 演算子では作成できませんし、配下のメソッドを呼び出すこともできません。グローバルオブジェクトは、グローバル変数やグローバル関数を管理するためのオブジェクトと言うことができます。
JavaScript インタプリタが起動すると、JavaScript コードを実行する前にグローバルオブジェクトが生成されます。グローバルオブジェクトのプロパティは、グローバル変数になります。言い換えると、グローバル変数を宣言することは、グローバルオブジェクトのプロパティを定義することになります。
グローバル変数・グローバル関数とは、トップレベルの(関数の中ではない)変数・関数のことです。
JavaScript インタプリタはグローバルオブジェクトを初期化し、グローバルオブジェクトのプロパティや関数を参照するようにします。
これらのグローバル変数・グローバル関数は Global.xxx ではなく、単に変数名、関数名を記述して参照することができます。
変数名 関数名(引数) console.log(isFinite(Infinity)); //false console.log(parseInt("123xyg")); //123
グローバル変数・グローバル関数は、自分で定義することができますが、以下のようなデフォルトのグローバル変数・グローバル関数を提供しています。
プロパティ名 | 概要 |
---|---|
Infinity | 無限大 |
NaN | 数値ではない値 (Not a number) |
undefined | 未定義値 |
eval() | 引数の文字列を JavaScript コードとして実行(評価) |
isFinite() | 有限値かどうか(NaN, 正負の無限大以外は真を返す) |
isNaN() | 数値でない(NaN)かどうか |
parseFloat() | 文字列を浮動小数点数に変換 |
parseInt() | 文字列を整数値に変換 |
decodeURI() | 文字列を URI デコード |
decodeURIComponent() | 文字列を URI デコード |
encodeURI() | 文字列を URI エンコード |
encodeURIComponent() | 文字列を URI エンコード |
escape() | 文字列をエスケープ処理 |
unescape() | エスケープ文字を元に戻す |
トップレベルコード(関数の一部ではないコード)では、JavaScript の this キーワードを使ってグローバルオブジェクトを参照できます。
クライアントサイド JavaScript では、グローバルオブジェクトを参照する window という変数が存在します。言い換えると、ブラウザ上では window オブジェクトがグローバルオブジェクトになります。
クライアントサイド JavaScript では、既に window が存在するので必要はありませんが、トップレベルコードの this キーワードがグローバルオブジェクトを参照するので、次のようにすればどんな環境でも変数 global で、グローバルオブジェクトを参照できます。
var global = this; console.log(global); //Window
Object オブジェクト
Object オブジェクト(Object クラス)は、JavaScript の全てのオブジェクトの基本オブジェクトで、それ自身をインスタンス化できるだけでなく、他のオブジェクトに対してオブジェクトの共通な性質・機能を提供する役割を担っています。(Object クラスは JavaScript の全てのクラスの基底クラスと言えます)
組み込みオブジェクトも、ユーザー定義オブジェクトも、オブジェクトと名前の付くものは全て Object オブジェクトで定義されたプロパティやメソッドを利用することができます。
個々のプロパティやメソッドについては「Object のプロパティとメソッド」を参照ください。
名称と役割は Java の Object クラスに似ていますが、継承の仕組みは Java とは異なり、プロトタイプ継承です。
以下のように Object.prototype オブジェクトに追加したプロパティは、任意のオブジェクトから読み取りアクセス可能になります。但し、これは説明のための例で、実際には影響が広範囲になるためこのようなことはしません。他のプラグインなどと名前の衝突が起きる可能性があります。
Object.prototype.foo = "foo"; var date = new Date(); console.log(date.foo); //foo console.log("str".foo); //foo console.log((55).foo); //foo
匿名オブジェクト
Object オブジェクトを直接インスタンス化することで、ユーザーが独自のオブジェクトを定義することができます。
var obj = new Object(); //または var obj = {}; //オブジェクトリテラル
このようなオブジェクトのことを、独自のオブジェクト名を持たないことから、匿名オブジェクト(無名オブジェクト)と呼ぶことがあります。
オブジェクトの生成やプロパティへのアクセスについては、「オブジェクトの生成」「プロパティへのアクセス」を参照ください。
String オブジェクト
String オブジェクトは、文字列型の値を扱うためのラッパーオブジェクトで、文字列の抽出や検索、加工等を行うための機能を提供します。
以下のように new 演算子で明示的にオブジェクトを生成することができますが、
var str = new String("Hello");
通常はリテラル表現を使って以下のように記述します。(基本データ型を new 演算子でインスタンス化するのは避ける)
var str = "Hello"; //または var str = 'Hello';
String オブジェクトのメソッド
String オブジェクトのメソッドには以下のようなものがあります。
タイプ | メソッド | 概要 |
---|---|---|
検索 | indexOf() | 文字列の先頭から終わりまでのサブストリングを検索 |
lastIndexOf() | 文字列の終端から先頭までのサブストリングを検索 | |
部分文字列 | charAt() | 文字列の n 番めの文字を返す |
slice() | 文字列のスライスまたはサブストリングを返す | |
substring() | 文字列中の指定されたサブストリングを返します | |
substr() | 文字列のサブストリングを抽出して返す | |
split() | 指定された文字列を分割し、サブストリングの配列を返す | |
正規表現 | match() | 正規表現のマッチングを検索 |
replace() | 文字列に対して検索と置換を行う | |
search() | マッチするサブストリングを検索 | |
大文字 小文字 |
toLowerCase() | すべての大文字を小文字に変換 |
toUpperCase() | すべての小文字を大文字に変換 | |
コード変換 | charCodeAt() | 特定の場所の文字エンコーディングを返す |
fromCharCode() | 個別のUnicodeエンコーディングの数値を指定して新しい文字列を生成 | |
文字修飾 | anchor() | 文字列をアンカーに変換 |
link() | 文字列をリンクに変換 | |
sub() | 下付き文字に変換 | |
sup() | 上付き文字に変換 | |
その他 | concat() | 2つの文字列を連結し、新しい文字列を返す |
length | 文字列の長さを示す整数(プロパティ) | |
trim() | 文字列の両端の空白を削除 |
String.indexOf() メソッド
文字列を検索し、その最初の文字の場所(位置)を返します。
string.indexOf(substring) string.indexOf(substring, start)
- 引数 substring
- string 内で検索するサブストリング。
- 引数 start
- string 内で検索を開始する位置を示す省略可能な整数引数。 有効な値は0(文字列内の先頭の文字)から string.length-1(文字列内の末尾の文字)までの値。 この引数を省略した場合は、文字列内の先頭の文字から検索。
- 戻り値
- string で指定された start 位置から最初に見つけた substring の位置。見つからなかった場合は、-1が返される。
String.indexOf() は、文字列の先頭から終わりまでのサブストリングを検索します。 文字列内の start の位置が指定されない場合は文字列の先頭から開始。 サブストリングが見つかったら、String.indexOf() は文字列で最初に見つかったサブストリングの最初の文字の位置を返します。 文字列内の文字の位置は0から始まる数値。文字列にサブストリングが見つからなければ、String.indexOf() は -1 を返します。
var str = 'JavaScript'; var pos = str.indexOf('a'); console.log(pos); // 1 pos = str.indexOf('a', 3); console.log(pos); //3 pos = str.indexOf('s'); console.log(pos); // -1(大文字・小文字は区別される) pos = str.indexOf('S'); console.log(pos); // 4
String.lastIndexOf() メソッド
文字列を後方から検索しその最初の文字の場所(位置)を返します。
string.lastIndexOf(substring) string.lastIndexOf(substring, start)
- 引数 substring
- string内で検索するサブストリング
- 引数 start
- string 内で検索を開始する位置を示す省略可能な整数引数。 有効な値は0(文字列内の先頭の文字)から string.length-1(文字列内の末尾の文字)までの値。 この引数を省略した場合は、文字列内の末尾の文字から検索。
- 戻り値
- string 内で指定された start 位置から最後に見つけた substring の位置。何も見つからなかった場合は-1が返されます。
var str = 'JavaScript'; var pos = str.lastIndexOf('a'); console.log(pos); // 3 pos = str.lastIndexOf('a', 1); console.log(pos); //1 pos = str.lastIndexOf('i'); console.log(pos); // 7 pos = str.lastIndexOf('S'); console.log(pos); // 4
String.charAt() メソッド
文字列から n 番めの文字を取得します。
string.charAt(pos)
- 引数 pos
- string から獲得する文字のインデックス
- 戻り値
- string の pos 番めの文字
String.charAt() メソッドは文字列 string の pos 番めの文字を返します。 文字列の先頭文字のインデックスは0です。指定された値が 0 から string.length-1 の範囲にない場合は、空の文字列が返されます。 JavaScript は文字列型以外の文字データ型を持たないので(char 型はないので)、長さ1の文字列が返されます。
var name = 'Thomas'; var initial = name.charAt(0); console.log(initial); // 'T'
String.slice() メソッド
文字列の一部を抜き出し新しい文字列を生成します。
string.slice(start, end)
- 引数 start
- スライスが開始する文字列のインデックス。 マイナス値の場合は、この引数は文字列の終端からの位置を表す。 つまり、-1は最後の文字を表し、-2は最後から2番目の文字、といった具合。
- 引数 end
- スライスの終端の直後の文字インデックス。 これが指定されないと、スライスには文字列の先頭から終端までのすべての文字が含まれる。 この引数がマイナス値の場合は、文字列の終端からの位置を表す。
- 戻り値
- 先頭からのすべての文字を含む新しい文字列(ただし終端の文字は含まない)
slice() メソッドは、文字列のスライスまたはサブストリングを返します。このメソッドは、文字列を変更しません。 文字列メソッドである slice()、substring() そして substr() は、すべて文字列の指定された部分を返します。 slice() メソッドはマイナス値の引数を受け付けるので、substring() メソッドよりも柔軟です。
var text = 'This is a "sample" text.'; var a = text.slice(10); console.log(a); //"sample" text. var b = text.slice(0,3); console.log(b); //Thi var c = text.slice(-5); console.log(c); //text. var d = text.slice(17, 24); console.log(d); // " text.
String.substring() メソッド
String.substring() メソッドは string 中の指定されたサブストリングを返します。substring() メソッドは、sliceと 同じですが、パラメータに負の値を指定することができません。substring() を使わなければならない理由はないので、 代わりに slice() を使えば良いでしょう。
substring(from, to)
- 引数 from
- string 内で指定されたサブストリングの先頭文字の位置を示す整数。from の値は 0 から string.length-1 までの値でなければなりません。
- 引数 to
- string 内で指定されたサブストリングの最終文字の位置より1だけ大きい整数(省略可)。to の値は 1 から string.length までの値でなければなりません。
- 戻り値
- 長さが to-from の新文字列。この新文字列は、string の from から to-1 までの文字で構成されます。
var text = 'This is a "sample" text.'; var a = text.substring(10); console.log(a); //"sample" text. var b = text.substring(0,3); console.log(b); //Thi var c = text.substring(-5); //負の値は指定不可 console.log(c); //This is a "sample" text. var d = text.substring(17, 24); console.log(d); // " text.
String.substr() メソッド
指定の位置から指定の文字数分だけ抜き出します。
string.substr(start, length)
- 引数 start
- サブストリングの開始位置。この引数がマイナス値の場合、文字列の終端からの位置を示します。つまり、-1は最後の文字を示し、-2は終端から2番目の文字を示します。
- 引数 length
- サブストリングに含まれる文字の数。この引数が省略されると、返されるサブストリングには文字列の先頭から終端までのすべての文字が含まれます。
- 戻り値
- (start が指定する文字を含む)文字列の先頭部分から length 文字までのコピー。length が指定されていない場合は、文字列の終端までのコピー。
substr() メソッドは、文字列のサブストリングを抽出して返します。このメソッドは string を変更しません。 substr() メソッドは、必要なサブストリングを文字位置と length で表します。このメソッドは、substring() や2つの文字位置でサブストリングを指定する slice() の代替として利用できます。
var text = 'This is a "sample" text.'; var a = text.substr(10); console.log(a); //"sample" text. var b = text.substr(0,3); console.log(b); //Thi var c = text.substr(-5); // console.log(c); //text. var d = text.substr(17, 4); console.log(d); // " te
String.split() メソッド
指定された separator で文字列を分割し、サブストリングの配列を返します。
string.split(separator, limit);
- 引数 separator
- string を分割する文字、文字列または正規表現。区切り文字を指定しなかった場合は、要素が1つだけで、 その要素が元の文字列のままという配列が返されます。また、 separator に空文字("")を指定した場合、生成される配列は 文字列を1文字ずつ分解したものなります。
- 引数 limit
- 最大いくつの部分文字列に分割するかを指定。省略可能。
- 戻り値
- string をs eparator で分割したサブストリングの配列
split() メソッドは指定された文字列を分割し、サブストリングの配列を返します。 指定された separator を検出するたびに、そこで文字列を分割しサブストリングを作成します。区切り文字(文字列)はサブストリングには含まれません。 g フラグは無視されます。 String.split( )メソッドの逆の操作をするのは、Array.join() メソッドです。
var digits = '0123456789'; var a = digits.split('', 5); console.log(a); //['0', '1', '2', '3', '4'] var ip = '192.168.3.1'; var b = ip.split('.'); console.log(b); // ['192', '168', '3', '1'] var c = '|a|b|c|'.split('|'); console.log(c); //['', 'a', 'b', 'c', ''] var text = 'Java, PHP ,C++'; var d = text.split(/\s*,\s*/); console.log(d); //['Java', 'PHP', 'C++']; //但し、キャプチャグループ由来の文字列は生成される配列に含まれるので注意。 var text = 'Java, PHP ,C++'; var e = text.split(/\s*(,)\s*/); console.log(e); //['Java', ',', 'PHP', ',', 'C++'];
String.toLowerCase() メソッド
文字列を小文字に変換して、その結果を新しい文字(コピー)として返します。
string.toLowerCase()
- 戻り値
- すべての大文字を小文字に変換したstringのコピー
var string = "JavaScript and PHP"; console.log(string.toLowerCase()); //javascript and php
String.toUpperCase() メソッド
文字列を大文字に変換して、その結果を新しい文字(コピー)として返します。
string.toUpperCase()
- 戻り値
- すべての小文字を大文字に変換したstringのコピー
var string = "JavaScript and PHP"; console.log(string.toUpperCase()); //JAVASCRIPT AND PHP
String.charCodeAt() メソッド
指定した位置の文字コードを取得します。
string.charCodeAt(pos)
- 引数 pos
- エンコーディングが返される文字のインデックス。
- 戻り値
- string 内の pos 番目の文字の Unicode エンコーディング。このリターン値は 0 から 65535 までの16ビットの整数。
charCodeAt() メソッドは、文字そのものを返す charAt() と違って、特定の場所の文字エンコーディング(エンコードの値)を整数で返します。 Unicode エンコーディングから文字列を作成する方法については、String.fromCharCode() を参照。
var name = 'Thomas'; var initial = name.charCodeAt(0); console.log(initial); //84 console.log("日本語".charCodeAt(2)); //35486 var str = "日本語"; var cc = ""; for(var i = 0; i < str.length; i ++) { if(i == str.length -1) { cc += str.charCodeAt(i); }else{ cc += str.charCodeAt(i) + ","; } } console.log(cc); //26085,26412,35486
String.fromCharCode()メソッド
文字コードの並びから文字列を作成する静的なメソッド
String.fromCharCode(c1, c2, ...)
- 引数 c1, c2, ...
- 生成する文字列の文字の Unicode エンコーディングを表す0以上の整数。
- 戻り値
- 指定の文字を含む新しい文字列。
この静的なメソッドによって、文字個別の Unicode エンコーディングの数値を指定して新しい文字列を生成することがでます。 静的なメソッドである fromCharCode() は、String() コンストラクタのプロパティであり、文字列や String オブジェクトのメソッドではありません。 逆に String.charCodeAt() は、文字列の各文字のエンコーディングの取得を可能にするインスタンスメソッドです。
var s = String.fromCharCode(104, 101, 108, 108, 111); console.log(s); //hello console.log(String.fromCharCode(26085,26412,35486)); //日本語
String.anchor()メソッド
HTMLアンカーを文字列に追加します。
string.anchor(name)
- 引数 name
- HTML<a>タグの name 属性の値。生成するアンカーの名前になります。
- 戻り値
- HTMLの<a name="name">タグと</a>タグで囲まれた文字列のコピー。
var str = "some strings"; console.log(str.anchor("test")); //<a name="test">some strings</a>
String.link() メソッド
文字列へハイパーテキストリンクを追加します。
string.link(href)
- 引数 href
- 文字列に追加するハイパーテキストのリンク先を示すURL。この文字列引数は、HTMLの<a>タグの href 属性になります。
- 戻り値
- <a href="href">と</a>で囲まれた string のコピー。
var str = "Google"; console.log(str.link("http://google.com")); //<a href="http://google.com">Google</a>
String.sub()メソッド
<sub>で表された添え字にします。
string.sub()
- 戻り値
- <sub>と</sub>で囲まれた string のコピー。
var str = "Google"; console.log(str.sub()); //<sub>Google</sub>
String.sup()メソッド
文字列を<sup>を使ってスーパースクリプトにします。
string.sup()
- 戻り値
- <sup>と</sup>タグで囲まれた string のコピー。
var str = "Google"; console.log(str.sup()); //<sup>Google</sup>
String.concat() メソッド
複数の文字列を連結し、新しい文字列を返します。
string.concat(value, ...)
- 引数 value, ...
- 文字列に連結する1つまたは複数の値。
- 戻り値
- 引数を連結した新しい文字列。
文字列の連結は + 演算子を使ったほうが便利なので、このメソッドはほとんど使われません。 concat()メソッドは、必要に応じて引数をそれぞれ文字列に変換して、順番に文字列の終端に追加し、その結果の連結した文字を返します。 文字列そのものは変更されません。
var str = 'J'.concat('a', 'va'); console.log(str); //Java
String.length プロパティ
文字列の長さ
string.length
String.length プロパティは、指定された string の文字数を示す整数です。文字列 string の最後の文字のインデックスは、string.length-1 になります。
console.log("JavaScript".length); //10
String.trim() メソッド
trim() メソッドは、文字列の両端の空白を削除します。この空白には、空白文字(スペースやタブ、ノーブレークスペースなど)とすべての改行文字(LF や CR など)を含みます。(Javascript1.8.1より実装)
string.trim()
- 戻り値
- 呼び出し元の文字列の両端から空白を取り除いた新しい文字列。
trim() メソッドは両端の空白を取り除いた文字列を返します。trim() はその文字列自身の値には影響を与えません。
var str = ' foo '; console.log(str.trim()); // 'foo'
ブラウザがこのメソッドを実装していない場合は、正規表現を使って以下のようにすることもできます。(行頭の一回以上連続する空白文字、または行末の一回以上連続する空白文字を空文字に置き換えます)
string.replace(/^\s+|\s+$/g,'');
jQuery では $.trim() メソッドが用意されています。
Number オブジェクト
Number オブジェクトは、数値型(number)の値を扱うためのラッパーオブジェクトです。数値の整形の機能や最大値・最小値など特別な値を表す読み取り専用のプロパティ(定数)があります。
Number オブジェクトは、以下のように new 演算子で明示的に生成することもできますが、
var num = new Number(1234);
通常はリテラル表現を使って以下のように記述します。(基本データ型を new 演算子でインスタンス化するのは避ける)
var num = 1234;
以下は、Number オブジェクトで利用可能なメンバ(プロパティ・メソッド)です。
タイプ | メンバ | 概要 |
---|---|---|
プロパティ | MAX_VALUE | 最大値 |
MIN_VALUE | 最小値 | |
NaN | 数値以外の値(Not-a-Numberの略) | |
NEGATIVE_INFINITY | 負の無限大 | |
POSITIVE_INFINITY | 正の無限大 | |
メソッド | toExponential() | 指数形式に変換 |
toFixed() | 四捨五入 | |
toPrecision() | 指定桁数に変換 | |
toString() | n 進数の値に変換 |
Number.toExponential() メソッド
数値を指数表現で文字列に変換します。
number.toExponential(fractionDigits)
- 引数 fractionDigits
- 小数点以下の桁数を指定するパラメータで、0から 20の数値で指定でき、省略可能。
- 戻り値
- 指数表現に変換された文字列
console.log(Math.PI); //Math.PI は円周率 //3.141592653589793(数値) console.log(Math.PI.toExponential()); //3.141592653589793e+0(文字列) console.log(Math.PI.toExponential(0)); //3e+0 console.log(Math.PI.toExponential(2)); //3.14e+0 console.log(Math.PI.toExponential(7)); //3.1415927e+0 console.log(Math.PI.toExponential(20)); //3.14159265358979311600e+0
Number.toFixed() メソッド
数値を小数点表示で文字列に変換します(固定小数点表記を用いてフォーマット)。値は四捨五入されます。
number.toFixed(fractionDigits)
- 引数 fractionDigits
- 小数点以下の桁数を指定するパラメータで、0から 20の数値で指定でき、省略可能。
- 戻り値
- 変換された文字列
console.log(Math.PI); //Math.PI は円周率 //3.141592653589793(数値) console.log(Math.PI.toFixed()); //3(文字列) console.log(Math.PI.toFixed(0)); //3 console.log(Math.PI.toFixed(2)); //3.14 console.log(Math.PI.toFixed(7)); //3.1415927 console.log(Math.PI.toFixed(20)); //3.14159265358979311600 console.log(1.26.toFixed(1)); //1.3 console.log(1.24.toFixed(1)); //1.2
Number.toPrecision() メソッド
指定された精度(桁数)で表した文字列を返します。値は四捨五入されます。
number.toPrecision(precision)
- 引数 precision
- 有効桁数を指定するパラメータで、1から21までの数値で指定できます。
- 戻り値
- 変換された文字列
console.log(Math.PI); //Math.PI は円周率 //3.141592653589793(数値) console.log(Math.PI.toPrecision()); //3.141592653589793(文字列) console.log(Math.PI.toPrecision(2)); //3.1(2桁) console.log(Math.PI.toPrecision(7)); //3.141593(7桁) console.log(Math.PI.toPrecision(20)); //3.1415926535897931160(20桁) console.log(1.26.toPrecision(2)); //1.3 console.log(1.24.toPrecision(2)); //1.2
Number.toString() メソッド
数値から文字列(n 進数の値)への変換
number.toString(radix)
- 引数 radix
- 省略可能な引数。数値の変換で使用する底。2から36までの整数を指定。 この引数を指定しなかった場合は、10(進数)と見なされます。 このパラメータは、整数を変換する際に使われるのが一般的ですが、整数以外でも利用可能です。
- 戻り値
- 指定された数値に相当する文字列
Number オブジェクトの toString() メソッドは、Number オブジェクトを、指定された radix の数値に相当する文字列に変換します。 radix を指定しなかった場合は、10(進数)と見なされます。
var num = 255; console.log(num.toString()); //255 console.log(num.toString(2)); //11111111 console.log(num.toString(8)); //377 console.log(num.toString(16)); //ff
- Number.NaN
-
Number.NaN は、何らかの演算の結果や関数の実行結果が数値以外の値(Not-a-Numberの略)になったという意味を表す特殊な値です。parseInt() や parseFloat() から NaN が返された場合、これは指定された文字列の解析に失敗したことを意味します。通常数値を返す何らかの関数でエラーが発生したときに、エラーの発生を通知するために Number.NaN が使われます。
JavaScript は Number.NaN を NaN として出力します。NaN はすべての数値と等しくならないため、NaN であるかどうかを調べる場合は、isNaN() 関数を使用します。
Math オブジェクト
Math オブジェクトは数学演算の機能を提供します。
Math オブジェクトが提供するメンバは、全て静的メソッド・プロパティになります。以下の形式で Math オブジェクトが提供するメンバにアクセスします。
Math.プロパティ名 Math.メソッド名(引数)
また、Math オブジェクトは new 演算子でインスタンス化することはできません。
以下は Math オブジェクトで利用可能なメンバの一部です。
メンバ | 概要 |
---|---|
abs(num) | 絶対値を返す |
max(num1,num2) | num1,num2 の大きい値を返す |
min(num1,num2) | num1,num2 の小さい値を返す |
pow(base,p) | べき乗(bass の p 乗) |
random() | 0~1 の擬似乱数を発生 |
ceil(num) | 小数点以下を切り上げ |
floor(num) | 小数点以下を切捨て |
round(num) | 小数点以下を四捨五入 |
sqrt(num) | num の平方根を返す |
PI | 円周率(定数) |
SQRT2 | 2の平方根(定数) |
Math.random()
0 以上 1 未満の範囲で疑似乱数を返します。
var rand = Math.random();
0以上10未満のランダムな整数(0~9)を取得する例。最小値が 0 で最大値だけ指定する場合は、最大値より1多い値を指定します。
var rand = Math.floor( Math.random() * 10 );
min から max までの整数の乱数を返す関数の例
function getRandom(min, max) { return Math.floor( Math.random() * (max - min + 1) ) + min; }
重複のない整数の乱数を生成して配列に格納して返す関数の例。この関数は引数 count の数だけ乱数を発生させます。
function generate_randomx(count) { //生成した乱数を格納する配列を初期化 var generated = new Array(); //生成した乱数を格納している配列の長さ(生成した乱数の数) var generatedCount = generated.length; //パラメータ count の数だけ Math.random()で乱数を発生 for(var i = 0 ; i < count; i++){ var candidate = Math.floor(Math.random() * count); //今まで生成された乱数と同じ場合は再度乱数を発生 for(var j = 0; j < generatedCount; j++) { if(candidate == generated[j]){ candidate = Math.floor(Math.random() * count); j= -1; } } generated[i] = candidate; generatedCount++; } return generated; } console.log(generate_randomx(10)); //例 Array [0,2,9,6,3,5,4,7,1,8 ]
浮動小数点数型の誤差
JavaScript で小数の計算をすると誤差が発生することがあります。
console.log(0.1 + 0.2); //0.30000000000000004
この誤差は10進数の「小数」を2進数に正しく変換できないために発生するようです。
この問題を回避するには以下のような方法があるようです。
- bignumber.js や math.js などの「数値計算ライブラリ」を使用
- 浮動小数点を10の累乗をかけて整数にして計算し、その値を10の累乗で割って浮動小数点数型に戻す
- 必要な桁数の位置で四捨五入する
以下は回避策の使用例です。
var val1 = 1.2345 - 0.5; console.log(val1); //0.7344999999999999 //10の累乗をかけて整数に var val2 = (1.2345 * 10 *10 *10 *10 - 0.5 * 10 *10 *10 *10) / (10 *10 *10 *10); console.log(val2); //0.7345 //10000をかけて整数に var val3 = (1.2345 * 10000 - 0.5 * 10000) / 10000; console.log(val3); //0.7345 // val1 を toPrecision で(四捨五入)有効桁数4桁の文字列にして、parseFloatで再度数値に変換 console.log(parseFloat(val1.toPrecision(4))); //0.7345 // val1 を toFixed で(四捨五入)小数点数以下4桁の文字列にして、parseFloatで再度数値に変換 console.log(parseFloat(val1.toFixed(4))); //0.7345
Date オブジェクト
Date オブジェクトにはリテラル表現がないので、オブジェクトの生成には必ずコンストラクタを使用します。Date オブジェクトを生成するには、次の4つの方法があります。
new Date(); new Date(value); new Date(datestring); new Date(year, month, day, hours, minutes, seconds, ms);
引数を指定しなかった場合は、現在の日付と時刻で Date オブジェクトが生成されます。
数字の引数(value)を1つだけ指定した場合、引数で指定した数字(1970年1月1日0:00 UTC からのミリ秒数を表す整数値)が新しいオブジェクトの時間(ミリ秒)になります。この時間は、getTime()メソッドが返して来る時間です。
日付を表す文字列の引数(datestring)を1つだけ指定した場合、引数で指定した日付と時刻が新しいオブジェクトの日付時刻になります。このときの引数は、Date.parse()メソッドで変換できる形式でなければなりません。但し、ブラウザごとに動作が異なり一貫性がないため、Date コンストラクタで日付文字列を解釈しないように推奨されています。
4つ目の場合は、2個から7個までの引数を取ることができます。year と month 以外はオプションです。
引数 | 概要 |
---|---|
なし | 引数を省略した場合、コンストラクタは現在の日付と地方時による時刻を表す JavaScript の Date オブジェクトを生成します。 |
value | 1970年1月1日午前0時(GMT)を基点とし、そこからの経過時間をミリ秒(タイムスタンプ値)で指定します。 |
datestring | 日付と時刻(省略可能)を文字列として指定します。この文字列は Date.parse() メソッドで変換できる形式でなければなりません。 |
year | 4桁の数字で年を指定します。例えば、2017と指定すると、これは2017年を意味します。 |
month | 1月を 0 とし、12月を 11 とする整数で月を指定します。基点が0であることに注意。 |
day | 1日を1とし、31日を31とする整数で日にちを指定します。基点が1であることに注意。これ以外の引数はすべて基点が0です。 |
hours | 午前0時を0とし、午後11時を23とする整数で時刻を指定します。 |
minutes | 0分を0とし、59分を59とする整数で分を指定します。 |
seconds | 0秒を0とし、59秒を59とする整数で秒を指定します。 |
ms | 0ミリ秒を0とし999ミリ秒を999とする整数でミリ秒を指定します。 |
Date を複数の引数を伴ってコンストラクタとして呼び出された場合、値が範囲より大きくても (month 値に 13 を与えたり、minute 値に 90 を与える等)、調整された値になります。つまり、new Date(2016, 13, 1) は、new Date(2017, 1, 1) と等しくなるように調整され、両者とも 2017-02-01 の日付を生成します (month は 0 を起点とします)。
現在の日付の設定
引数なしで Date オブジェクトのインスタンスを作成すると、年、月、日、時、分、秒、およびミリ秒を含む現在の日付と時刻を表す値が返されます。
以下は日付をインスタンス化して mm/dd/yyyy の形式で表示する方法の例です。
var date = new Date(); var month = date.getMonth() + 1; // getMonth() 「月」(0-11) を返す var day = date.getDate(); var year = date.getFullYear() console.log(month + "/" + day + "/" + year);
特定の日付の設定
コンストラクターに日付文字列を渡すことによって、特定の日付を設定できます。この文字列は Date.parse() メソッドで変換できる形式でなければなりません。
var date = new Date('9/7/1982'); console.log(date); //Date 1982-09-07T04:00:00.000Z //"Z" は UTC 時刻を表します。
コンストラクターに「年、月、日、時間、分、秒、ミリ秒」を渡しても特定の日付を設定できます。月は1月を 0 とし、12月を 11 とする整数で指定します。基点が0であることに注意。
var date = new Date(1982, 8, 7, 18, 33, 40); console.log(date); //Date 1982-09-07T22:33:40.000Z var date2 = new Date(1982, 8); console.log(date2); //Date 1982-09-01T04:00:00.000Z
メソッド | 概要 |
---|---|
getDate() | 指定された日時の「日」(1-31) を返す(ローカル時刻) |
getDay() | 指定された日時の「曜日」(0-6) を返す(ローカル時刻) |
getFullYear() | 指定された日時の「年」(4 桁までの年) を返す(ローカル時刻) |
getHours() | 指定された日時の「時」(0-23) を返す(ローカル時刻) |
getMilliseconds() | 指定された日時の「ミリ秒」(0-999) を返す(ローカル時刻) |
getMinutes() | 指定された日時の「分」(0-59) を返す(ローカル時刻) |
getMonth() | 指定された日時の「月」(0-11) を返す(ローカル時刻) |
getSeconds() | 指定された日時の「秒」(0-59) を返す(ローカル時刻) |
getTime() | 1970年1月1日00:00:00からのミリ秒単位の数値で返す(UTC) |
getTimezoneOffset() | 現地の時間帯のオフセットを分で返す |
getUTCDate() | 指定された日時の「日」(1-31) を返す(UTC) |
getUTCDay() | 指定された日時の「曜日」(0-6) を返す(UTC) |
getUTCFullYear() | 指定された日時の「年」(4 桁までの年) を返す(UTC) |
getUTCHours() | 指定された日時の「時」(0-23) を返す(UTC) |
getUTCMilliseconds() | 指定された日時の「ミリ秒」(0-999) を返す(UTC) |
getUTCMinutes() | 指定された日時の「分」(0-59) を返す(UTC) |
getUTCMonth() | 指定された日時の「月」(0-11) を返す(UTC) |
getUTCSeconds() | 指定された日時の「秒」(0-59) を返す(UTC) |
now() 関数 | 970年1月1日00:00:00から現在までの経過時間をミリ秒単位で返す |
parse() 関数 | 日時を表す文字列を解釈し、1970年1月1日00:00:00からの経過時間を表すミリ秒単位の数値を返す。但し、文字列を解釈できなかったり不正な日付 が指定された場合 NaN を返す |
setDate() | 指定された日時の「日」を設定(ローカル時刻) |
setFullYear() | 指定された日時の「年」を完全な形で設定(ローカル時刻) |
setHours() | 指定された日時の「時」を設定(ローカル時刻) |
setMilliseconds() | 指定された日時の「ミリ秒」を設定(ローカル時刻) |
setMinutes() | 指定された日時の「分」を設定(ローカル時刻) |
setMonth() | 指定された日時の「月」を設定(ローカル時刻) |
setSeconds() | 指定された日時の「秒」を設定(ローカル時刻) |
setTime() | 1970年1月1日00:00:00からのミリ秒単位の数で表された時刻に設定(UTC) |
setUTCDate() | 指定された日時の「日」を設定(UTC) |
setUTCFullYear() | 指定された日時の「年」を完全な形で設定(UTC) |
setUTCHours() | 指定された日時の「時」を設定(UTC) |
setUTCMilliseconds() | 指定された日時の「ミリ秒」を設定(UTC) |
setUTCMinutes() | 指定された日時の「分」を設定(UTC) |
setUTCMonth() | 指定された日時の「月」を設定(UTC) |
setUTCSeconds() | 指定された日時の「秒」を設定(UTC) |
toDateString() | Date オブジェクトの「日付」部を人間が読みやすい形式の文字列にして返す |
toISOString() | 日付を ISO 8601 Extended Format に準じた文字列に変換 |
toJSON() | JSON シリアル化の前にオブジェクト型のデータを変換するために使用 |
toLocaleDateString() | システム設定の日時の「日付」部を、地域の日付書式に従った文字列に変換して返す |
toLocaleTimeString() | システム設定の日時の「時刻」部を、地域の日付書式に従った文字列に変換して返す |
toLocaleString() | 日付を地域の日付書式に従った文字列に変換して返す |
toString() | 指定された Date オブジェクトを表す文字列を返す |
toTimeString() | Date オブジェクトの「時刻」部を人間が読みやすい形式の文字列にして返します |
toUTCString() | 日時を UTC タイムゾーンを使用する文字列に変換 |
UTC() 関数 | コンストラクタと同じ最も長い書式の引数を受け入れ、UTC 1970年1月1日00:00:00 からの経過時間を表す Date オブジェクトのミリ秒単位の数値を返す |
valueOf() | Date オブジェクトのプリミティブ値を返す |
- UTC 協定世界時
- Universal Time Coordinated の略で協定世界時と訳されます。原子時計を元に人工的に調整しています。GMT グリニッジ標準時とほぼ同じ。
Date.now()
Date.now() メソッドは、UTC(協定世界時)での1970年1月1日0時0分0秒から現在までの経過時間(タイムスタンプ)をミリ秒単位で返すメソッドです。
// 現在のタイムスタンプを取得 const now = Date.now(); console.log(now); //1674000197707 // Dateオブジェクトを使って現在の日付を取得 const nowDate = Date(Date.now()); console.log(nowDate) //Wed Jan 18 2013 09:03:17 GMT+0900 (日本標準時)
new Date().getTime() でも同様に現在のタイムスタンプを取得できますが、単に現在時刻をタイムスタンプで取得したい場合は、Date.now() の方が高速です。
// 現在のタイムスタンプを取得 const now1 = Date.now(); const now2 = new Date().getTime(); console.log(now1); //1674000708657 console.log(now2); //1674000708657 console.log(now1===now2); //true
日付と時刻の計算
日、月、および年の加算と除算
Date オブジェクトの getXXX メソッドと setXXX メソッドを使用して、特定の日付と時刻を設定することができます。
以下は日付を前日の日付に設定する例です。
var date = new Date("1/1/2000"); var dayOfMonth =date.getDate(); date.setDate(dayOfMonth - 1); console.log(date); //Date 1999-12-31T05:00:00.000Z
以下のように記述することもできます。
var date = new Date("1/1/2000"); date.setDate(date.getDate() - 1);
特定の要素に対する加算・減算の結果が有効範囲を超えてしまっても、Date オブジェクトは正しい日付に自動的に換算してくれます。この性質を利用すると、「来月の0日目」は Date オブジェクトでは「今月の最終日」となるので、その月の最終日を簡単に求めることができます。
var date = new Date(2017, 2, 3); console.log(date.toLocaleString()); //2017/3/3 0:00:00 date.setMonth(date.getMonth() + 1); date.setDate(0); console.log(date.toLocaleString()); //2017/3/31 0:00:00
以下は、翌月の最初の日から 1 日を減算することで日付を月の最後の日に設定する例です。
var date = new Date("5/1/2015") date.setMonth(date.getMonth() + 1); date.setDate (date.getDate() - 1); console.log(date.toLocaleString()); //2015/5/31 0:00:00
曜日の操作
getDay メソッドは、0 (日曜日) ~ 6 (土曜日) の数値として曜日を取得します。(getDate メソッドは、1~31 の数値として日付を取得)
米国の Daylight Saving Time(夏時間)は3月の第2日曜日の2:00am から始まりますが、以下はその年の夏時間の始まる日付を取得する例です。
var date = new Date(); date.setHours(2, 0, 0, 0); //3月1日に設定します date.setDate(1); date.setMonth(2); //日曜日を探します //設定されている日が日曜日でない場合は、日付を1ずつ増やします var sunday = 0; while(date.getDay() != sunday) { date.setDate(date.getDate() + 1); } //第2日曜日なので日付を1週間(7日)増やします date.setDate(date.getDate() + 7); console.log(date.toLocaleString()); //2017/3/12 1:00:00
以下は、年と月を渡すと、その月の最初の金曜日の日付を返す関数の例です。
function get1stFriday(year, month) { var date = new Date(year, month-1); date.setHours(0, 0, 0, 0); var sunday = 5; while(date.getDay() != sunday) { date.setDate(date.getDate() + 1); } return date.toLocaleString(); } console.log(get1stFriday(2017,3)); //2017/3/3 0:00:00
経過時間(日付・時刻の差分)の計算
経過時間を計算するためには、getTime メソッドを使用して開始時間と終了時間を設定し、差分を求めます。getTime メソッドは、経過ミリ秒を返します。
以下は2017年1月1日から現在まで何日が経過しているかを求める例です。ミリ秒数を日数に変換するには、値を 86,400,000 (1000 ミリ秒 x 60 秒 x 60 分 x 24 時間) で除算します。
var date1 = new Date(2017,0,1); var date2 = new Date(); var diff_msec = date2.getTime() - date1.getTime(); var diff_days = Math.floor(diff_msec/(1000 * 60 * 60 * 24)); console.log(diff_days);
年齢の計算
現在の年から誕生年を減算し、現在の年の誕生日がまだ来ていない場合はさらに 1 を減算します。
var birthday = new Date("9/7/1982"); var today = new Date(); var age = today.getFullYear() - birthday.getFullYear(); //誕生日の年を今年に設定します birthday.setFullYear(today.getFullYear()); //今日の日付と比較して誕生日がまだきていない場合は1を引きます if (today < birthday) { age--; } console.log(age); //以下は上記を関数にした例です。 function getAge(year, month, day) { var birthday = new Date(year, month, day); var today = new Date(); var age = today.getFullYear() - birthday.getFullYear(); birthday.setFullYear(today.getFullYear()); if (today < birthday) { age--; } return age; } console.log(getAge(1982, 9, 7));
また、年齢は以下の方法でも取得できます。
(今日の日付 - 誕生日の日付)/10000 //小数点以下切捨て
上記を利用した年齢を取得する関数の例です。getMonth() メソッドの返す値は 0-11 なので取得した値に1を加えています。
function getAge(year, month, day) { var birthday = year * 10000 + month * 100 + day; var date = new Date(); today = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate(); var age = Math.floor((today - birthday)/10000); return age; } console.log(getAge(1982, 9, 7));
以下は、設立年月日から現在何年経過しているかを求める関数の例です。前述の年齢を求める関数とほぼ同じですが、引数には8桁の年月日の数値を取ります。
function getYears(establish_date) { var today = new Date(); today = today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate(); return (Math.floor((today - establish_date) / 10000)); } console.log(getYears(19820907));
日付の比較
JavaScript の日付を比較するときに、演算子の両側の日付が同じオブジェクトを参照している場合にだけ == 演算子が true を返します。 そのため、2 つの別々の Date オブジェクトが同じ日付に設定されている場合、date1 == date2 は false を返します。 また、時刻なしで日付だけで設定された Date オブジェクトは、その日付の午前 0 時に初期化されます。
var date1 = new Date(2017, 3, 4); var date2 = new Date(2017, 3, 4); console.log(date1 == date2); //false console.log(date1.getTime() == date2.getTime()); //true
関数
プログラムの中で定義されている一連の実行可能なコードのことを関数と呼びます。関数を一度定義するとプログラム中から何度でも呼び出すことができます。
関数には引数を渡すことができます。関数は引数を受け取って処理を行います。必要に応じて戻り値を返します。以下は「n」という引数を受け取り、その値の二乗を計算して返す「square」という名前の関数の例です。
function square(n) { return n * n ; } console.log(square(6)); //36
JavaScript では関数もデータ型の1つで、関数を値として扱えます。変数や配列、オブジェクトに関数を格納したり、関数を引数として他の関数に渡したりできます。
他の値と同様にオブジェクトのプロパティに関数を代入でき、あるオブジェクトのプロパティに関数を代入した場合、その関数はメソッドと呼ばれます。
また、関数はオブジェクトでもあります。
JavaScript で関数を定義するには、以下のような方法があります。
function 文による定義
関数を定義する場合の最も基本的な方法です。
function 関数名([引数1 [, 引数2 [, ……]]) { 関数内で実行される任意の処理 [return 戻り値;] }
この方法は関数宣言と呼ばれ、関数定義用に特別に用意された構文です。普通の文とは違い文末にセミコロンが要りません。
また、関数宣言で定義された関数はコンパイル時に生成されるので宣言前からでも使えます。
定義済みの関数は以下の形式で呼び出します(引数がない場合でもカッコは省略できません)。
関数名([引数 ……]);
以下の関数 multiply は、与えられた引数 x と y を掛け算した結果を返す関数です。
function multiply(x, y) { return x * y; } alert(multiply(3, 7)); //21
以下の場合にも同じ結果が得られます。
alert(multiply(3, 7)); //21 function multiply(x, y) { return x * y; }
関数を呼び出した時点では、まだ関数は定義されていないのでエラーになりそうですが、問題なく実行されます。
function 文は動的に実行される文ではなく、function 文はコードがコンパイルされるタイミングで関数を登録しているのでどこからでも呼び出すことができます。
また、以下のようにカッコを省略して呼び出してアラート表示すると、関数オブジェクトの内容をダイアログ表示します。
function multiply(x, y) { return x * y; } alert(multiply); //alert(multiply.toString());
これはオブジェクトの toString() メソッドを呼び出したのと同じです。
JavaScript では関数はオブジェクトであり、関数を定義するのは「関数名という名前(この例では multiply)の変数に関数オブジェクトを格納している」のと同じ意味になります。
function multiply(x, y) { return x * y; } alert(multiply(5, 7)); // 35 multiply = "Multiply"; //[1]文字列を代入 alert(multiply); // [2]Multiply alert(multiply(5, 7)); //[3]下記エラー発生(何も表示されない) //TypeError: multiply is not a function
上記は最初に変数 multiply(関数名)に関数オブジェクトが格納されています。これが[1]の代入式で上書きされ、文字列「Multiply」が格納されます。[2]の時点では変数 multiply の最新の値「Multiply」が表示されます。次の[3]では文字列を「multiply(5, 7)」のような式で評価しようとしたため TypeError(multiply は関数ではありません) が発生しています。
関数リテラル(関数式)による定義
以下は前述の関数 multiply を関数リテラル(関数式)によって定義した例です。
var multiply = function(x, y) { return x * y; }
multiply という変数を定義し、2つの値を乗算する関数を代入(格納)しています。
代入式の右辺が関数リテラルです。
以下が関数リテラルの書式です。
function([引数……]) { 関数本体 } function 関数名([引数……]) { 関数本体 }
関数リテラルは4つのパーツで構成されています。
- function という予約語(function 演算子)。
- 関数の名前(これは省略可能)。名前をつけなかった場合、その関数は無名関数(匿名関数)と呼ばれます。
- 括弧で囲まれた関数のパラメータ。
- 中括弧で囲まれた命令文の集合体で、関数の本体。その関数が呼び出された際に実行されます。
function で始まる関数リテラル式は関数を返します。つまり左辺の変数には、関数オブジェクトの参照が代入されます。
関数の呼び出しは以下のようにします。function 文による関数の呼び出しと同じです。
alert(multiply(3,7));
また、関数リテラルを用いた関数定義では、function 演算子式の実行時に関数が生成されます。そのため、関数の定義より前では関数を使う事が出来ません。
alert(multiply(3,7)); //表示されない var multiply = function(x, y) { return x * y; } //TypeError: multiply is not a function が発生
Function コンストラクタによる定義
関数もオブジェクトなのでコンストラクタを使って関数を生成することができます。以下が書式です。
var 変数名 = new Function([引数....], 関数の本体);
コンストラクタに引数リストや関数の本体を文字列で渡します。
以下は前述の関数 multiply をコンストラクタを使って定義した場合です。
var multiply = new Function('x', 'y', 'return x * y'); alert(multiply(3,7)); //21
引数は、以下のように1つの文字列として記述することもできます。
var multiply = new Function('x, y', 'return x * y');
コンストラクタを用いた関数定義では、関数リテラルと同じように実行時に関数が生成されます。そのため、関数の定義より前では関数を使う事が出来ません。
alert(multiply(3,7)); //表示されない //TypeError: multiply is not a function が発生 var multiply = new Function('x, y', 'return x * y');
Function コンストラクタを利用する方法は実行時にコードの解析から関数オブジェクトの生成までを行うため、他の方法より効率が悪くパフォーマンスの低下の原因になる可能性があります。
そのため、while や for を使ったループ処理や、頻繁に呼び出される関数の中で使用するのは避けたほうが良いです。特別な理由がない限り、Function コンストラクタを利用するメリットはありません。
但し、Function コンストラクタを利用する方法は「引数や関数本体を文字列として定義できる」という特徴があり、以下のようなことが可能です。
var param = "x, y"; var formula = "return x * y"; var multiply = new Function(param, formula); alert(multiply(3,7)); //21
これを利用するとスクリプト上で文字列を連結して、関数を動的に生成することができます。
var op = "+"; var param = "x, y"; var formula = "return x" + op + "y"; var myFunc = new Function(param, formula); alert(myFunc(3,7)); //10
関数リテラルと Function コンストラクタにおけるスコープの違い
関数内で入れ子にして関数リテラルと Function コンストラクタを使用した場合には、スコープの解釈が異なります。
ローカル変数は、宣言された関数内で有効な変数です。以下は関数内で入れ子にした関数リテラルと Function コンストラクタを使用した例です。
var scope = "Global"; //グローバル変数 function getScope() { var scope = "Local"; //ローカル変数 // 関数リテラル var myScope1 = function() { alert(scope); }; // Function コンストラクタ var myScope2 = new Function("", "alert(scope);"); myScope1(); // "Local" myScope2(); // "Global" } getScope();
関数リテラル(myScope1 )も Function コンストラクタ(myScope2)も関数内部で定義しているので、いずれも変数 scope はローカル変数を参照するように思いますが、実際には Function コンストラクタはグローバル変数を参照しています。(これが仕様のようです)
即時関数
即時関数は関数を定義すると同時に実行するための関数で、名前の通り即時実行される無名関数です。即時関数を使えばグローバルな名前空間での変数名やプロパティ名の衝突を気にせずに、ローカルのスコープを作り出しそのまま処理を実行することができます。
以下が即時関数の書式です。
(function(){ // 処理を記述 })(); /*以下のようにカッコの位置を変えてもOKです。*/ (function(){ // 処理を記述 }());
以下を実行するとコンソールに「即時関数」と出力されます。
(function(){ console.log("即時関数"); }());
以下を実行すると「21」と出力されます。
console.log(function(){ return 3 * 7; }());
また、即時関数も引数を取ることができます。
(function(x, y){ console.log(x * y); // 30 })(5, 6); //または、(カッコの位置が違う) (function(x, y){ console.log(x * y); // 30 }(5, 60));
引数 /Arguments オブジェクト
関数の引数
JavaScript は引数のデータ型をチェックしないので引数のデータ型を知りたい場合は、typeof 演算子を使って自分でチェックする必要があります。必要に応じて、引数の型が間違っている場合には例外をスローするようにします。(参考:例外処理)
var square = function(x){ if(typeof x !== 'number'){ throw new Error("引数は数値でなければなりません"); } return x * x; } try { square("three"); } catch(e) { document.writeln ("Error Message: " + e.message); }
また JavaScript は引数の個数もチェックしません。必要な個数より多かった場合、余分な値は無視され、少なかった場合はその部分の値は未定義値とみなされます。
var square = function(x){ return x * x; } console.log(square()); //NaN(乗算しているので NaN) console.log(square(3)); //9 console.log(square(3,7)); //9(7は無視される)
但し、多かった引数は切り捨てられるわけではなく、内部的には「引数情報」の一つとして保持され後から利用できる状態になっています。この「引数情報」を管理するのが Argument オブジェクトで、関数を定義する本体部分でのみ利用できる特別なオブジェクトです。
省略可能な引数
引数の数をチェックしないということは、JavaScript では全ての引数を省略可能であるということになります。但し多くの場合、引数が省略されると正しく動作しないことになります。
そこで、引数が省略された場合(もしくは null/undefined が指定された場合)に、省略された引数に適切なデフォルト値が設定されるようにする必要があります。
また、省略可能な引数を持つ関数を定義する場合、省略可能な引数は引数リストの最後に記述する必要があります。
以下は第1引数にオブジェクトを、第2引数に配列を受け取り、オブジェクトのプロパティ名を指定された配列に格納する関数の例です。第2引数が省略された場合、空の配列を生成するようにしています。
var o1 = {x:0, y:1, z:2}; var o2 = {a:3, b:4}; function copyPropNamesToArray(obj, arry){ //未定義値またはnullの場合は、空の配列を生成。 arry = arry || []; //if(!arry) arry = []; と同じ for(var prop in obj) arry.push(prop); return arry; } var result = copyPropNamesToArray(o1); //引数を省略 document.write(result, "<br>"); //x,y,zと表示 result = copyPropNamesToArray(o2, result); document.write(result); //x,y,z,a,bと表示
Arguments オブジェクト
arguments は Arguments オブジェクトを参照する特別なプロパティで、関数呼び出しのタイミングで生成され、呼び出し元から与えられた引数の値を保持します。この arguments プロパティを利用することで実際に与えられた引数の数などをチェックすることができます。
- 関数に渡された引数値は arguments[] 配列の要素に格納され、数値インデックスで参照できます。
- Arguments オブジェクトには callee プロパティも存在します。
- Arguments オブジェクトは「配列のような」オブジェクトです。(※)
- 呼び出し時に渡された引数の個数は arguments.length で参照できます。
※ arguments は本物の配列ではなく、「配列のような」オブジェクトです。arguments には length プロパティは存在しますが、 配列のメソッドはいずれも使うことができません。
以下の関数 square は、与えられた引数が1つでない場合に例外をスローするようにしています。arguments.length は実際に関数に渡された引数の個数を表します。
var square = function(x){ if(arguments.length != 1) { throw new Error("引数の数が間違っています: " + arguments.length); } return x * x; } try { square(3, 5); }catch(e) { alert(e.message); }
上記では、単に引数の個数をチェックしているだけですが、引数のデータの型や値の有効範囲などの妥当性をチェックすることもできます。
可変長引数の関数
可変長引数の関数とは、予め引数の個数が決まっていない関数のことです。arguments を利用することで可変長引数の関数を定義することができます。
以下は任意個の引数を受け取って最大の引数値を返す関数の例です。関数に渡された引数値は arguments[] 配列の要素に格納され、数値インデックスで参照できます。
arguments から i 番目の要素を取り出すには、arguments[i] のように記述します。
function max(){ var m = Number.NEGATIVE_INFINITY; for(var i=0; i<arguments.length; i++){ if(arguments[i] > m) m = arguments[i]; } return m; } document.write(max(20, 9999, 108, 333333,77)); //333333と表示
以下は引数に与えられた数値の合計する関数の例です。この例では isNaN を使って取得した要素が数値であるかを確認して、数値でない場合(isNaN が true を返す場合)には例外をスローしています。
function sum() { var result = 0; for(var i = 0; i < arguments.length; i++) { var val = arguments[i]; if(isNaN(val)) { throw new Error("値が数値ではありません: " + val); } result += val; } return result; } try { document.write(sum(1,9,15,87)); //112 }catch(e) { alert(e.message); }
callee プロパティ
Arguments オブジェクトには配列要素の他に callee プロパティ(arguments.callee)が定義されています。callee プロパティは現在実行中の関数を参照します。
また、callee プロパティは呼び出された関数そのものへの参照を取得できます。this キーワードは現在実行している関数を参照するものではないので、 関数本体の内部でその関数を参照するには callee プロパティを使用します。
callee プロパティを利用すれば、再帰呼び出しの処理を簡単に記述できます。
以下は与えられた数値 n の階乗を求める関数の例です。
function factorial(n){ if(n <= 1) return 1; return n * arguments.callee(n - 1); //return n * factorial(n - 1); でも同じ結果 } console.log(factorial(5)); //120
return n * arguments.callee(n - 1) の代わりに、return n * factorial(n - 1) としても同じ結果になりますが、関数名(変数名)が変更された場合本体の記述も修正しなければなりません。
また、無名(匿名)関数として記述した場合には、再帰呼び出しするための関数名がないので、callee プロパティを使用する必要があります。
//再帰的に呼び出す無名関数の例 function (n){ if(n <= 1) return 1; return n * arguments.callee(n - 1); }
引数にオブジェクトを使う
引数の数が増えてくると、引数の順番を間違いやすくなります。そのような場合、引数にオブジェクトを利用するとわかりやすくなります。
引数にオブジェクトを使用することで、引数の順番を気にしないで指定することができますし、名前と値のペアなので引数の意味が明確になります。
以下の例は敢えて引数をオブジェクトにする必要はありませんが、引数にオブジェクトを利用する例です。
また、以下では引数の値が未定義値または null の場合のデフォルトの値(10)を設定しています。
function getArea(args){ var width = args.width || 10; var height = args.height || 10; return width * height; } alert(getArea({width: 8, height: 12}) ); //96 alert(getArea({height: 50}) ); //500
引数の型
JavaScript は関数の定義時に引数の型を宣言することができません。また、関数に渡された値についても、型のチェックは行われません。必要に応じて、引数の型が間違っている場合には例外をスローするようにします。
以下の関数 flexSum() は任意個の引数を受け取り、各引数について型を調べて数値でない値を受け取った場合は、数値への変換を試みてその合計を返す関数です。数値への変換ができない場合はエラーをスローします。
function flexSum(arg) { var total = 0; for(var i = 0; i < arguments.length; i++) { var element = arguments[i]; //各引数を変数 element に格納 if(!element) continue; //null, undefined は無視 var n; switch(typeof element) { //引数の型により、引数を数値に変換 case "number": n = element; break; case "object": if(element instanceof Array) { //配列の場合は再帰処理 n = flexSum.apply(this, element); }else{ //配列以外のオブジェクトはvalueOf()で値に変換 n = element.valueOf(); } break; case "string": n = parseFloat(element); break; case "boolean": n = NaN; break; } if(typeof n == "number" && !isNaN(n)) { total += n; //値(n)が数値の場合は total に加算 }else{ throw new Error(element + " を数値に変換できません") } } return total; } var obj = {a:0, b:2, c:3}; alert(flexSum(obj.b, obj.c, [10,20], "100x")); //135
データとしての関数
JavaScript の関数はデータとしても扱うことができます。変数に代入したり、オブジェクトのプロパティや配列の要素に格納したり、 他の関数に渡したりすることができます。
以下は function 文による関数 square() の定義です。
function square(x) { return x * x ; }
上記コードは、新たに関数オブジェクトを生成し、square という名前の変数に格納しています。関数の名前は、関数を参照する変数名と考えることができます。関数を別の変数に代入しても、そのまま関数として使用することができます。
function square(x) { return x * x ; } var a = square(3); //a は9 //関数を変数に代入:b は square と同じ関数を参照 var b = square; var c = b(5); //c は25
関数をオブジェクトのプロパティに格納した場合、関数ではなく、メソッドと呼びます。
var obj = new Object; obj.square = function (x){ return x * x;}; //関数リテラル x = obj.square(11); //x は121
関数を配列の要素に格納しておけば、関数の名前を指定しなくても関数を呼び出すことができます。
var a = new Array(3); a[0] = function(x){ return x * x; } //関数リテラル a[1] = 20; a[2] = a[0](a[1]); //関数の呼び出し alert(a[2]); //400
以下は、関数をデータとして利用する例です。関数を引数として、他の関数に渡すことができます。
//簡単な関数を定義して、オブジェクトリテラルに格納 var operators = { add: function(x, y) {return x + y;}, subtract: function(x, y) {return x - y;}, multiply: function(x, y) {return x * y;}, divide: function(x, y) {return x / y;}, max_val: Math.max, //組み込み関数も利用可能 min_val: Math.min } //以下の関数は演算の名前を引数に受け、該当する演算関数を operators から探して実行 function operate(operator,x,y) { if(typeof operators[operator] == "function") { return operators[operator](x,y); }else{ throw new Error("不明な演算子です"); } } alert(operate("add", 10,20)); //30 alert(operate("max_val", 100,20)); //100 alert(operate("subtract", 10,20)); //-10 alert(operate("add", "hello" ," universe")); //hello universe
以下のように、引数に直接匿名関数を指定することもできます。
function operate(func,x,y) { if(typeof func == "function") { return func(x,y); }else{ throw new Error("不明な演算子です"); } } alert(operate(function(x,y){return x * y; }, 10,20)); //200 alert(operate(function(x,y){return x + y; }, 10,20)); //30 alert(operate(Math.max, 10,20)); //20
メソッドとしての関数
オブジェクトのプロパティに格納されている関数で、このオブジェクトを介して呼び出される関数のことをメソッドと呼びます。
また、メソッドを呼び出す時に使用したオブジェクトが、メソッドの本体の中で this キーワードの値になります。
var greeting = { //オブジェクトリテラル val1 : "Hello ", val2 : "Universe!", sayHello : function() { //メソッド alert("Greeting: " + this.val1 + this.val2); } } greeting.sayHello(); //Greeting: Hello Universe!
メソッドとして使用する関数には、通常の引数とは別に、呼び出す時に指定したオブジェクトという引数(this)が渡されるということになります。
別の言い方をすると、メソッドを呼び出すときに使用したオブジェクトを、メソッド本体の中で this キーワードで参照できます。
this キーワード
this キーワードは呼びだされたコンテキストによって参照先が変わります。基本的にはメソッド内で this キーワードが呼び出された場合はそのメソッドが格納されているオブジェクトが参照先となり、格納されているオブジェクトが存在しない場合は window オブジェクトが参照先となります。
strict mode ではない場合、this が undefined の場合は、this がグローバルオブジェクトを参照するように変換されます(JavaScript Primer 関数宣言や関数式におけるthis)。
また、関数内でもその関数の呼び出し方法で参照先オブジェクトが変わってくるので注意が必要です。以下は this キーワードの参照の規則です。
- トップレベルコードの this キーワードの参照先:グローバルオブジェクト(Window)
- 関数内の this キーワードの参照先:呼び出し方法で異なる(以下表参照)
関数の呼び出し方法 | this キーワードの参照先 |
---|---|
コンストラクタ呼び出し | 生成したオブジェクト |
メソッド呼び出し | メソッドを呼び出すときに使用したオブジェクト |
apply / call 呼び出し | apply / call の引数で指定したオブジェクト |
上記以外の呼び出し | グローバルオブジェクト |
console.log(this); //Window (グローバルオブジェクト) var func1 = function(){ console.log(this); }; func1(); //Window(strict mode では undefined) function func2(){ console.log(this); } func2(); //Window(strict mode では undefined) var func3 = function(){ // グローバル関数を定義 var func3_1 = function(){ // ローカル関数を定義 console.log(this); //Window(strict mode では undefined) }; func3_1(); function func3_2(){ // ローカル関数を定義 console.log(this); //Window(strict mode では undefined) } func3_2(); }; func3(); var obj = { func4: function(){ // メソッド func4を定義 console.log(this); //Object } }; obj.func4(); //Object { func4: obj.func4() } var parent = {}; // オブジェクト parent を定義 parent.child = {}; // parent の中にさらにオブジェクト child を定義 parent.child.func5 = function(){ // child の中にメソッド func5 を定義 console.log(this); //Object }; parent.child.func5(); //Object { func5: parent.child.func5() }
関数内の this キーワードの参照先は、関数の書き方や、定義、宣言方法で変わるのではなく、関数の呼び出し方によって変わります。同じ関数でも呼び出し方が異なれば this キーワードは異なるオブジェクトを参照します。
var greeting = { val1 : "Hello ", val2 : "Universe!", sayHello : function() { alert("Greeting: " + this.val1 + this.val2); } } greeting.sayHello(); //Greeting: Hello Universe! //greeting.sayHelloが参照する関数オブジェクトをグローバル変数 hello に代入 var hello = greeting.sayHello; //関数内の this の参照先はグローバルオブジェクト(プロパティが未定義) hello(); //Greeting: undefinedundefined //グローバルオブジェクト(Window)にプロパティを定義 var val1 = "Global Hello "; var val2 = "Global Universe!"; hello(); //Greeting: Global Hello Global Universe! //call(), apply() を利用 hello.call(greeting); //Greeting: Hello Universe! hello.apply(greeting); //Greeting: Hello Universe!
メソッド内から他のメソッドを呼ぶ場合
また、メソッド内から別のメソッドを呼ぶには、以下のように this キーワードを使って別のメソッドを呼び出す必要があります。
this.sayBye() の代わりに単に sayBye() と記述するとグローバル関数 sayBye() を探すため「sayBye is not defined」とエラーになります。
var greeting = { val1 : "Hello ", val2 : "Universe!", sayHello : function() { alert("Greeting: " + this.val1 + this.val2); this.sayBye(); //this を使って別のメソッドを呼び出す }, sayBye : function() { alert("Bye, " + this.val2); } } greeting.sayHello(); //Greeting: Hello Universe! //Bye, Universe!
また、メソッドとして呼び出された関数中に入れ子にした別の関数が関数として呼び出された場合もグローバルオブジェクトを参照します。つまり、入れ子にしている側の関数(メソッド)では this キーワードはオブジェクトを参照していますが、入れ子にされた関数の本体では this キーワードはグローバルオブジェクトを参照します。
var obj = { func: function(){ // メソッド func を定義 console.log(this); //Object { func: obj.func() } var _func = function(){ // ローカル関数 _func を定義 console.log(this); // Window }; _func(); } }; obj.func(); // {func: ƒ} (オブジェクト) // Window (グローバルオブジェクト)
ローカルオブジェクトを生成して、その中でメソッドを定義した場合の this の参照先はそのローカルオブジェクトになります。
var obj = { func: function(){ // メソッド func を定義 var _obj = { // ローカルオブジェクト _obj を定義 _func: function(){ // メソッド _func を定義 console.log(this); // Object } }; _obj._func(); } }; obj.func(); // Object { _func: obj.func/_obj._func() }
this キーワードは現在実行している関数を参照するものではないので、 関数本体の内部でその関数を参照するには callee プロパティを使用します。
参考になるサイト:JavaScript Primer 関数とthis
アロー関数
アロー関数は ES2015(ES6)から導入された新しい JavaScript の構文の一つです。(IE11 には対応していません。caniuse ES6)
アロー関数を使うと、function 式より短い構文で同様な内容を記述することができます。アロー関数は関数リテラルを簡潔に記述する方法です。
以下が書式(構文)です。
([arg] [, arg]) => { statements }
一見しただけでは何のことだかわかりませんが、以下は関数リテラルとアロー関数で同じ関数を定義した例です。
//関数リテラル var multiply = function(x, y) { return x * y; } console.log("3 x 6 = " + multiply(3,6)); //3 x 6 = 18 //アロー関数 var multiply = (x, y) => { return x * y; }; console.log("3 x 6 = " + multiply(3,6)); //3 x 6 = 18
アロー関数式を使うと、「function」を記述する必要がなく、より短く関数を記述することができます。
(引数,...) => { 処理の記述 }
また、アロー関数は条件によっては更に簡潔に記述することができます。
関数が1つの文のみであれば { } と return も省略可能
関数が1つの文のみの場合 {} と return が省略可能です。 {} を省略した場合、return を記述するとエラーになります。
var multiply = (x, y) => { return x * y; //関数が1つの文のみの場合 }; //以下のように記述できます。 var multiply = (x, y) => x * y;
引数が1つの場合、() を省略可能
引数が1つの場合、引数をくくるカッコ () も省略できます。
var square = function(x) { //引数が1つの場合 return x * x; } console.log("3 x 3 = " + square(3)); //3 x 3 = 9 //以下のように記述できます。 var square = x => x * x; console.log("3 x 3 = " + square(3)); //3 x 3 = 9
引数がない場合、() を省略できない
引数が1つの場合は、()を省略できますが、引数をとらない場合は () が必要です。
var hello = function() { //引数がない場合 return "Hello Arrow!"; } console.log(hello()); //Hello Arrow! //以下のように記述できます。 var hello = () => "Hello Arrow!"; console.log(hello()); //Hello Arrow!
this を束縛しない
以下は「ECMAScript 2015以降のJavaScriptの this を理解する/Web Scratch 」からの引用です。とても詳しく解説されています。
Arrow Functionとthis
Arrow Functionで定義された関数やメソッドにおけるthisがどの値を参照するかは関数の定義時(静的)に決まります。 一方、Arrow Functionではない関数においては、thisは呼び出し元に依存するため関数の実行時(動的)に決まります。
Arrow Functionとそれ以外の関数で大きく違うことは、Arrow Functionはthisを暗黙的な引数として受け付けないということです。 そのため、Arrow Function内にはthisが定義されていません。このときのthisは外側のスコープ(関数)のthisを参照します。
これは変数におけるスコープチェーンの仕組みと同様で、そのスコープにthisが定義されていない場合には外側のスコープを探索するのと同じです。 そのため、Arrow Function内のthisの参照先は、常に外側のスコープ(関数)へとthisの定義を探索しに行きます(詳細はスコープチェーンを参照)。 また、thisは読み取り専用のキーワードであるため、ユーザーがthisという変数を定義できません。
const this = "thisは読み取り専用"; // =&t; SyntaxError: Unexpected token this
これにより、Arrow Functionにおけるthisは通常の変数と同じように、どの値を参照するかは静的に決まるという性質があります(詳細は静的スコープを参照)。 つまりArrow Functionにおけるthisの参照先は「Arrow Function自身の外側のスコープにあるもっとも近い関数のthisの値」となります。
以下はアロー関数で this を使ういくつかの例です。
以下はオブジェクトのメソッドにアロー関数を利用した場合の例です。
この例の場合は、アロー関数を使うとオブジェクトのプロパティを参照できません。「Arrow Function自身の外側のスコープにあるもっとも近い関数のthisの値」が存在しないためと思われます。
var objectX = { //通常の関数 myProp: 'My Property', func: function(){ console.log(this.myProp); } } objectX.func(); //My Property オブジェクトのプロパティ var objectY = { //アロー関数を利用 myProp: 'My Property', func: () => { console.log(this.myProp); } } objectY.func(); //undefined 取得できない
以下のように、グローバル変数に同じ名前があれば、それを参照します。
var myProp = "Global"; //グローバル変数 var objectY = { //アロー関数を利用 myProp: 'My Property', func: () => { console.log(this.myProp); } } objectY.func(); //Global
以下は「アロー関数/MDN」の例からの引用です。(少し変更してあります)
function Person(name) { this.name = name; this.age = 0; setInterval(function () { // 非 strict モードでは、この関数は this をグローバルオブジェクトとして定義する。 // Person() コンストラクタが定義した this とは違う this.age++; console.log(this.age); //NaN が毎秒出力される(this を参照できない) }, 1000); } var p = new Person("Michael"); console.log(p.name); //Michael console.log(p.age); //0
上記の例では console.log(this.age) の出力は「NaN」が毎秒出力されてしまいます。これは、setInterval() に渡される関数は、実行時の呼び出し元オブジェクトがグローバルオブジェクトになるためですが、これを解決するには、以下のような方法があります。
this の値をスコープ内の変数(that)に代入する方法
function Person(name) { var that = this; that.name = name; that.age = 0; setInterval(function () { that.age++; console.log(that.age); //毎秒数値がカウントアップされる }, 1000); } var p = new Person("Michael"); console.log(p.name); //Michael console.log(p.age); //0
bind() 関数の引数に this を渡して、呼び出し元のオブジェクトをそのオブジェクトに確定する方法
function Person(name) { this.name = name; this.age = 0; setInterval(function () { this.age++; console.log(this.age); //毎秒数値がカウントアップされる }.bind(this), 1000); //bind(this) を記述すると this を確定できる } var p = new Person("Michael"); console.log(p.name); //Michael console.log(p.age); //0
アロー関数を使う方法
アロー関数を使えば、setInterval に渡される関数の this の値は、外部関数の this と同じ値になります。(Arrow Function自身の外側のスコープにあるもっとも近い関数のthisの値)
function Person(name) { this.name = name; this.age = 0; setInterval( () => { //アロー関数を使用 this.age++; console.log(this.age); //毎秒数値がカウントアップされる }, 1000); } var p = new Person("Michael"); console.log(p.name); //Michael console.log(p.age); //0
イベントハンドラにアロー関数を利用する場合の注意
以下のようなボタンがある場合、jQuery のクリックイベントを設定する場合の例です。
<button id="foo">Submit</button>
以下のように記述して、ボタンがクリックされるとそのテキスト(Submit)が表示されます。
$("#foo").on('click', function() { console.log($(this).text()); //Submit と表示 });
アロー関数を使って以下のように記述すると、想定しているようなテキストは取得できません。
$("#foo").on('click', () => { console.log($(this).text()); //想定していないもの(ページ全体のテキスト)が表示される });
以下のように this の代わりにイベントのプロパティ(e.currentTarget)を使うと解決します。
$("#foo").on('click', (e) => { console.log($(e.currentTarget).text()); //Submit と表示 });
jQuery へのコールバックにアロー関数を利用する場合の注意
以下は、jQuery の each() を使用する場合の例です。以下の例の場合、全ての h3 要素のテキストが表示されます。
$('h3').each(function() { console.log($(this).text()); // 全ての h3 要素のテキストが表示される });
以下のようにアロー関数を使うと、イベントハンドラ同様に想定していないテキスト(ページ全体のテキストが h3 要素の数ぶん)が表示されます。
$('h3').each(() => { console.log($(this).text()); //想定していないもの(ページ全体のテキスト)が表示される });
each() の場合、第1引数には各要素のインデックス番号が、第2引数には繰り返し処理中の要素が渡されるので、第2引数を利用します。
$('h3').each((index, element) => { console.log($(element).text()); // 全ての h3 要素のテキストが表示される });
関数のプロパティとメソッド
関数に対して typeof 演算子をつかうと「function」という文字列が返されますが、関数は特殊なオブジェクトです。そして関数がオブジェクトということは、関数もプロパティやメソッドを持つことができるということになります。
length プロパティ
arguments[] 配列の length プロパティは、関数の本体では、その関数に実際に渡された引数の個数を表します。
関数自身の length プロパティは、関数に引き渡されるはずの引数の個数(引数リストで宣言された引数の個数)になります。
また、Function オブジェクトの length プロパティは、arguments.length プロパティとは異なり、関数の中でも外でも使用することができます。
以下の関数 checkArgs() は、別の関数(以下の例では foo())から arguments 配列が渡され、arguments.length プロパティ値と Function オブジェクトの length プロパティの値(arguments.callee.length)を比較して、引数の個数をチェックし、個数が異なれば例外をスローします。
function checkArgs(args) { var actual = args.length; var expected = args.callee.length; if(actual != expected) { throw new Error("引数の個数が異なります。必要な引数の個数:" + expected + " /渡された引数の個数:" + actual); } } function foo(x,y,z) { checkArgs(arguments); return x * y * z; } foo(2,3,4,5); //Error: 引数の個数が異なります。必要な引数の個数:3 /渡された引数の個数:4
prototypeプロパティ
どの関数にも、あらかじめ定義されたプロトタイプオブジェクトを参照する prototype プロパティがあります。
自分専用の関数プロパティの定義
Function オブジェクトにプロパティを追加することで、関数の実行を終了した後も値を保持できます。この値は、次に関数が呼び出された時に使うことができます。グローバル変数に値を格納しても同じことができますが、名前の衝突などに配慮しなければならないので、関数の中だけでしか使用しない場合は、 こちらの方が便利です。
//「静的」プロパティ(関数プロパティ)を生成し初期値を設定 //関数宣言のコードは実行より前に処理されるので、関数宣言の前に代入が可能。 uniqueInteger.counter = 0; //呼び出される度に1ずつ増える異なる整数を返す //自身の「静的」プロパティを使用して、渡した履歴を保持 function uniqueInteger(){ return uniqueInteger.counter++; } for(var i = 0; i < 10; i++){ document.write(uniqueInteger()); } //0123456789
call() と apply()
call() と apply() は関数(Function)オブジェクトの prototype プロパティ(Function.prototype)に用意されたメソッドです。この2つのメソッドを利用すると、あるオブジェクトのメソッドであるかのように関数を呼び出すことができます。
call() と apply() の違いは呼び出す関数の引数の指定方法だけで、引数を個別に指定する(call)か,配列でまとめて指定する(apply)かの違いです。
- call() : 第2引数以降は呼び出される関数の引数に渡される
- apply() : 第2引数には配列を渡し,その配列が呼び出される関数の引数に展開される
以下が書式です。
fun.call(thisArg [, arg1[, arg2[, ...]]]) fun.apply(thisArg [, argsArray])
- 第1引数(thisArg)
- 呼び出す関数の対象となるオブジェクト(ある関数を実行させたいオブジェクト)
このオブジェクトは、呼び出す関数の本体の this キーワードの値になる
undefined や null を指定すると、暗黙的にグローバルオブジェクト(Window)に変換される(明示的に window を指定することもできる)
- 第2引数
- call()メソッド:2番目以降の引数は呼び出す関数に渡される引数のリスト(第2引数以降は呼び出される関数の引数に渡されます)
apply()メソッド:関数に渡す引数を配列で指定
以下の場合、オブジェクト foo には、メソッド func が定義されていますが、オブジェクト bar には定義されていません。そのため、bar.func(); とするとエラーになりますが、call()/apply() を利用すると foo の func を呼び出すことができます。
var foo = { name : "foo", func : function() { console.log(this.name); } } foo.func(); //foo var bar = { name : "bar" } bar.func(); //エラー(TypeError: bar.func is not a function) foo.func.call(bar); //bar foo.func.apply(bar); //bar
以下は配列の中の最大値・最小値を求めるために Math.max/Math.min ビルトイン関数を利用する例です。
var numbers = [3, 61, 25, 333, 70]; var max = Math.max.apply(null, numbers); console.log(max); //333 var min = Math.min.apply(null, numbers); console.log(min); //3
良く使われる例としては、「配列のようなオブジェクト」は配列ではないので、配列のメソッドは使えませんが、call()/apply() を利用すればそれが可能になります。
「配列のようなオブジェクト」である arguments には join() メソッドがないので、以下の場合エラーになります。
function join_args() { alert(arguments.join()); } join_args("a","b","c"); //エラー //TypeError: arguments.join is not a function
call() や apply() を利用して Array.prototype.join.call(arguments) とすることで配列の join() メソッドを arguments に対して使用することができます。
function join_args() { alert(Array.prototype.join.call(arguments)); } join_args("a","b","c"); //a,b,c
getElementsByTagName()、getElementsByName()、getElementsByClassName() などのメソッドは NodeList オブジェクトを返しますが、NodeList オブジェクトは、配列のようなオブジェクトなので配列のメソッドは使用できません。この場合も、call() や apply() を使って配列のメソッドを利用することができます。
以下は、クラス属性が foo の要素を取得して、先頭の要素を取り出すコードです。配列の slice() メソッドを使ってコピーした要素リスト(foo_array)は、通常の配列になっているので、shift() で先頭の要素を取り出すことができます。
var foo = document.getElementsByClassName("foo"); var foo_array = Array.prototype.slice.call(foo); var foo_first = foo_array.shift(); console.log(foo_first.innerHTML);
スコープチェーン
JavaScript には、グローバルとローカルの 2 つのスコープがあります。関数定義の外部で宣言された変数はグローバル変数になり、プログラム内のどこからでも値の参照や変更を行うことができます。関数定義内で宣言された変数はローカル変数になります。関数の外部のコードからローカル変数にアクセスすることはできません。
スコープとは、変数の有効範囲のことで、プログラムのどの場所から参照できるかを決める概念のことです。(グローバル・スコープとローカル・スコープ)
グローバル変数 | ローカル変数 |
---|---|
トップレベル(関数の外)で宣言した変数 | 関数の中で宣言した変数, 関数の仮引数 |
プログラム全体から参照可能 | その関数の中でのみ参照可能 |
グローバルスコープ | ローカルスコープ |
---|---|
プログラム全体 | 変数が宣言された関数の中 |
グローバルオブジェクト
グローバルオブジェクトとは、グローバル変数やグローバル関数を管理するためのオブジェクトです。グローバル変数を定義するということは、実際にはグローバルオブジェクトのプロパティを定義するということになります。
Call オブジェクト
Call オブジェクトは、関数が呼び出される度に内部的に自動生成されるオブジェクトで、関数内で定義されたローカル変数を管理するオブジェクトです。Call オブジェクトは、生成したり呼び出したりすることはできないオブジェクトで、直接操作することはできません。(Call オブジェクトは Activation オブジェクトとも呼ばれます)
関数が実行されたタイミングで、関数内で var で宣言された変数と実行時に渡された引数などが、Call オブジェクトに格納されます。
Call オブジェクトの初期化時に、arguments という名前で Arguments オブジェクトを参照するプロパティが生成されます。
初期化が済むと、関数の名前付き引数が Call オブジェクトに追加され、var 文で宣言されたローカル変数も、 この Call オブジェクト中に定義されます。
- スコープチェーン
- Call オブジェクト、グローバルオブジェクトを生成の順に連結したリストで、変数の値を調べる(名前解決)ために使います。
JavaScript インタプリタは、関数が呼び出されると、関数のスコープにまず関数定義時のスコープチェーンを設定します。そして次に、Callオブジェクトを生成しスコープチェーンの先頭に追加します。
Call オブジェクトはスコープチェーンの先頭にあるので、ローカル変数や関数の引数、Arguments オブジェクトが全て関数のスコープに含まれることになります。
JavaScript では変数を解決する際に、このスコープ・チェーンの先頭に位置するオブジェクトから順にプロパティを検索して、合致したプロパティが見つかればその値を、見つからなければ次のオブジェクトを検索します。
例えば、変数 x の値を探す(変数名を解決する)場合、スコープチェーンにつながる先頭のオブジェクトから探し始めます。
先頭のオブジェクトにxという名前のプロパティがあれば、そのプロパティの値が使用され、なければ、 スコープチェーンにつながる次のオブジェクトで探索が続けられます。
関数外で定義されたコードの場合、スコープチェーンにつながるオブジェクトはグローバルオブジェクトのみです。グローバルオブジェクトに変数が存在しない場合は未定義になります。
(入れ子でない)関数内で定義された変数の場合、 スコープチェーンにはその関数の Call オブジェクトとグローバルオブジェクトの2つがあり、 関数から変数を参照すると、最初に Call オブジェクト(ローカルスコープ)がチェックされ、 次にグローバルオブジェクト(グローバルスコープ)がチェックされます。
入れ子型の関数の場合、スコープチェーンには3つ以上のオブジェクトが存在します。
以下の例では、入れ子になっている関数 innerF() には、自身の Call オブジェクト、外側の関数 outerF() の Call オブジェクト、そしてグローバルオブジェクトの3つのオブジェクトがスコープチェーンに存在します。また、関数内からはグローバル変数 a, b は同名の変数が関数内に存在するので隠蔽されています。
var a = "Global A"; var b = "Global B"; var c = "Global C"; function outerF() { var a = "Local A"; console.log(b); //4. Global B(グローバル変数) function innerF() { var b = "Local B"; console.log(a); //5. Local A(outerF の Call オブジェクト) console.log(b); //6. Local B(自身の Call オブジェクト) console.log(c); //7. Global C(グローバル変数) console.log(d); //8. ReferenceError: d is not defined } innerF(); } console.log(a); //1. Global A(グローバル変数) console.log(b); //2. Global B(グローバル変数) console.log(c); //3. Global C(グローバル変数) outerF();
- 関数外で定義されたコード→グローバルオブジェクト(Global A)
- 関数外で定義されたコード→グローバルオブジェクト(Global B)
- 関数外で定義されたコード→グローバルオブジェクト(Global C)
- 自身の Call オブジェクト(該当なし)→グローバルオブジェクト(Global B)
- 自身の Call オブジェクト(該当なし)→outerF の Call オブジェクト(Local A)
- 自身の Call オブジェクト(Local B)
- 自身の Call オブジェクト(該当なし)→outerF の Call オブジェクト(該当なし)→グローバルオブジェクト(Global C)
- 自身の Call オブジェクト(該当なし)→outerF の Call オブジェクト(該当なし)→グローバルオブジェクト(該当なし)→未定義
クロージャー
クロージャーとは、ローカル変数の状態を保持できる関数(ローカル変数を参照している関数内の関数)のことです。別の言い方をすると、関数自身が定義された環境を、ローカル変数も含めて持ち運ぶことのできる仕組みとも言えます。
以下は簡単なクロージャーの例です。
function closure(n) { //外側の関数で変数を定義 var count = n; // 入れ子になった関数を戻り値として返す return function() { // この匿名関数がクロージャ //内側の関数から、外側の変数を参照 return count ++; }; } // closure()を呼び出し、戻り値 (関数オブジェクト) を変数に格納 var myCounter = closure(3); console.log(myCounter()); // 3 console.log(myCounter()); // 4 console.log(myCounter()); // 5
関数 closure() は、引数 n を受け取り、その値をインクリメントする匿名関数(関数オブジェクト)を戻り値として返します。
通常、関数の中で使用されたローカル変数(この例では count)は、関数の処理が終了した時点で破棄されます。
しかし、この例の場合、関数 closure() から返された匿名関数(変数 myCounter に格納)がローカル変数(count)を参照し続けているので、関数 closure() の終了後もローカル変数(count)は保持され続けます。
内側の関数は、スコープチェーンによって、外側の関数の Call オブジェクトを参照し続けることができます。
この例では、戻り値となる関数が作成された瞬間に、変数オブジェクトの連鎖(スコープチェーン)が作成されます。そのスコープチェーンの中には、関数 closure() の変数オブジェクトも含まれており、ローカル変数 n が格納されています。このスコープチェーンは、作成された関数がプログラム中に生存している間保持されます。これにより、関数 closure() 内で宣言されたローカル変数 n を、クロージャからずっと参照し続けられることになります。
つまり、変数 myCounter に格納された匿名関数の以下のスコープチェーンが、匿名関数が有効である間保持されることになります。
- 匿名関数の Call オブジェクト
- 関数 closure() の Call オブジェクト
- グローバルオブジェクト
上記の例では、クロージャーとして匿名関数(無名関数)を使用しましたが、名前を付けた関数でも同じことが可能です。
function closure(n) { //外側の関数で変数を定義 var count = n; function inner() { return count ++; } // 入れ子になった関数を戻り値として返す return inner; } // closure()を呼び出し、戻り値 (関数オブジェクト) を変数に格納 var myCounter = closure(100); console.log(myCounter()); // 100 console.log(myCounter()); // 101 console.log(myCounter()); // 102
スコープチェーンは独立したもの
以下のように関数 closure() の実行を複数行った場合、同じローカル変数 count を参照しているにも関わらず、異なる変数を参照しているかのように独立してインクリメントした値を得ます。
function closure(n) { var count = n; return function() { return count ++; }; } var myCounter1 = closure(0); var myCounter2 = closure(100); console.log(myCounter1()); // 0 console.log(myCounter2()); // 100 console.log(myCounter1()); // 1 console.log(myCounter2()); // 101
これは Call オブジェクトが関数の呼び出しの度に生成されるためです。それぞれのスコープチェーンは独立したものなので、その中で管理されるローカル変数(count)も独立しているため別物になります。