JavaエンジニアのためのJavaScript入門(ES5)【前編】
【JavaScriptを体系的に学習したい】
そんな声が新入社員からあがりました。
JavaScriptについて体系的にまとまっている記事は意外に少なく、
この際なので弊社エンジニアに記事にまとめてもらいました。
ボリュームたっぷりになりましたので、前後編になります。
まずは前編からどうぞ。
はじめに
これはJavaの既存の知識ベースを元にJavaScriptを体系的に学習する為の記事です。
※この記事では、ES5までのJavaScriptについて書いています。
それでは「Javaとの違い、JavaScriptではこうなる」という形でまとめていきましょう。
本記事のECMAScriptのバージョンについて
ECMAScriptはEcma internationalが仕様を策定するJavaScriptの標準です。
このECMAScriptに則ることでその仕様に準拠する環境での動作が保証されます。
※繰り返しますが、今回は「ES5までのJavaScriptについて」まとめています。
ECMAScript5のstrict mode
コードの先頭行に以下のディレクティブを書くと、strict modeとなります。
サポートされていない環境では無意味な文として無視されます。
1 2 |
'use strict' |
strict modeによって、JavaScriptの言語仕様の落とし穴を防ぐことができるため、書くことをお勧めします。
strict modeの代表的な制約
- 暗黙のグローバル変数の禁止
- 関数内でthis参照がグローバルオブジェクトを参照しない
- NaN, Infinity, undefinedをRead Onlyに
- 同名のプロパティ名を禁止
- arguments.calleeアクセスの禁止
- Functionオブジェクトのcallerプロパティのアクセス禁止
変数と型
JavaScriptは動的型付けを行う言語です。
変数は型を持たず、格納される値を指し示すことを役割とします。
変数
JavaScriptにおける変数の特徴は以下になります。
- 変数は値に名前をつけるものです。
- 変数には型が存在しませんが、値には型が存在します。
- 変数は、下から上ではなく、内から外へ探索されます。
- 変数は宣言時にはundefined値を保持します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var s = "string" var n = 10 var b = true // ,で区切ることで一つの文で複数の変数を宣言することもできる var obj = {}, func = function() {}; s // → "string" n // → 10 b // → true obj // → {} func // → function() {} |
JavaScriptの型
JavaScriptの型は以下の特徴を持ちます。
- 値のみが型を持ち、組み込みの演算子などの挙動を決めます。
- 評価の際には相互に暗黙の型変換を行うことがあります
- 新たにユーザ定義型を追加することはできません。
JavaScriptの型一覧
JavaScriptに存在する型は以下の6つです。
- 文字列型
- 数値型
- 真偽値型
- object型
- null型
- undefined型
プリミティブ型とオブジェクト型
JSでは、値はプリミティブまたはオブジェクトです。
そしてオブジェクトは全てobject型です。
そしてobject型以外をプリミティブ型と呼びます。
プリミティブ型について
プリミティブ型であるとは、不変であることを意味します。
つまり文字列”alpha”と文字列”beta”を連結した時には、新たな文字列”alphabeta”が作られます。
これはちょうど数字の3と4を足した時に7が作られることと同様で、この場合元あった3や4とは異なる新たな値が作られています。
オブジェクト型について
JavaScriptでは、ユーザ定義型を作成することはできません。
よって、ユーザが作成する型は全てオブジェクト型となります。
オブジェクトの型が一つであることは、その機能を限定することを意味しません。
むしろ、型が一つであることでより自由になっていると言えます。
オブジェクトは、その時々で様々な値や形式をとることができる変幻自在なものです。
文字列
JavaScriptにおける文字列は、連続した文字です。
JavaScriptに文字型は存在しないため、単にunicodeのテキストを表します。
文字列リテラルの囲い文字について
JavaScriptの文字列リテラルの囲い文字は、’と”のどちらも利用でき、これらに区別はありません。
しかしどちらかといえば、個人的には、’をお勧めします。
なぜなら、JSON文字列を記述する際のプロパティ名の囲い文字は”であり、JSON文字列をエスケープすることなく記述する際には’を用いるからです。
数値
JavaScriptの数値の型は1つのみです。
整数型や浮動小数点型と行った区別は存在しません。
そのため、速度や精度が求められる数値演算には向きません。
数値リテラル
JavaScriptの数値リテラルは以下の4つです。
- 10進数
- 2進数
- 8進数
- 4進数
特殊な値
数値リテラルはではないですが、JavaScriptの数値演算の結果として、以下の値をとることがあります。
これらは基本的に無効な数値演算の結果です。
そのため、数値演算を行う際には以下の結果にならないよう気を使う必要があります。
無効な値
- Infinity
- -Infinity
- NaN
また、以上の無効な数値を判定する関数としては、以下が定義されています。
無効な値の判定のための関数
- isNaN
- isFinite
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 数値リテラル var count = 10; // 10進数 var blue = 0x0000ff; // 16進数 var umask = 0o0022; // 8進数 var roomTemp = 22.5; // 少数 var c = 3.0e6; // 指数表記 var e = -1.6e-19; // -の指数表記 // 無効な値 var inf = Infinity; // 無限 var ninf = -Infinity; // マイナスの無限 var nan = NaN; // Not a Number (数値ではない) // NaNかどうかを判定する関数 isNaN(count) // → false isNaN(nan) // → true isNaN(inf) // → false // 無効な値でないことを判定する関数 isFinite(count) // → true isFinite(nam) // → false isFinite(inf) // → false |
Numberオブジェクトのプロパティ
数値の中でも、重要な値はNumberオブジェクトのプロパティとなっています。
1 2 3 4 5 6 7 8 9 |
var small = Number.EPSILON // 1と1より大きい最小の値の差 var maxInt = Number.MAX_SAFE_INTEGER; // 表現できる最大の整数 var max = Number.MAX_VALUE; // 表現できる最大の数値 var minInt = Number.MIN_SAFE_INTEGER; // 表現できる最小の整数 var min = Number.MIN_VALUE; // 表現できる最小の数値 var inf = Number.POSITIVE_INFINITY; // 無限 var ninf = Number.NEGATIVE_INFINITY; // マイナスの無限 var nan = Number.NaN; // Not a Number (数値ではない) |
真偽値
真偽値はtrueまたはfalseの値をとります。
Cなどの言語では、0をfalseとして扱い、それ以外の全ての数値をtrueとして扱います。
JavaScriptもこれと少し似た挙動で、一部の値をfalseとして扱い、それ以外の全ての値をtrueとして扱います。
falseとして扱われる値
falseとして扱われる値を以下に列挙します。逆に言えば、これ以外は全てtrueになります。
- 数値0
- 数値NaN
- null値
- undifined値
- 文字列”(空文字列値)
nullとundefined
nullとundefinedはどちらも存在しないものを表しています。
null型がとる値はnull一つで、undefined型がとる値はundefined一つとなります。
これらの違いを簡単に言ってしまうと、nullは開発者が設定する値で、undefinedはJavaScriptの処理系が設定する値です。
例えば、変数宣言時に値を設定していない場合、undefinedとなります。
値を初期化したいが、該当する値が存在しない場合は、自分自身でnullを設定します。
そのため、初期化の意味で自分でundefinedを設定しないでください。
他の開発者が、値が一度も設定されていないのだと勘違いする恐れがあります。
オブジェクト
型の視点から見たとき、JavaScriptのオブジェクトは特別な型です。
プリミティブ型は一つの値しか表現せず、不変ですが、オブジェクトは複数の値や複雑な値を表すことができ、処理の進行に伴って値も変化することができます。
基本的にはオブジェクトは入れ物として考えることができ、内容は時間とともに変化します。
しかし内容が変わってもそれは同じオブジェクトです。
オブジェクトに記録されるものをプロパティと呼びます。
typeof演算子
typeof演算子で型の名称の文字列値を取得でき、これにより型の判定ができます。
以下にその実例を列挙します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var s = 'str' var n = 100 var b = true var o = {} var f = function() {} var undef = undefined var nul = null typeof s // → 'string' typeof n // → 'number' typeof b // → 'boolean' typeof o // → 'object' // 関数はobject型だが'function'と判定される typeof f // → 'function' typeof undef // → 'undefined' // nullはnull型だが'object'と判定される typeof nul // → 'object' |
値の同値比較
同値比較の際は、==演算子または===演算子を用います。
前者は暗黙の型変換を行なった後に比較し、後者はそのまま比較します。
!==演算子と!-演算子の評価結果は、ちょうど上記の否定となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
1 == '1' // → true 1 === '1' // → false 1 != '1' // → false 1 !== '1' // → true // 宣言のみの変数の判定 var x; x === undefined // → true // 値がnullかの判定 var y = null; y === null // → true |
数値の同値比較の注意点
JavaScriptの数値は、倍精度浮動小数点数です。
倍精度は近似値のため、倍精度の値を比較した際の結果が誤差によって期待通りにならない場合があります。
1 2 3 4 5 6 7 8 9 |
var x = 0; while(true) { x += 0.1; console.log(x); // 3回目のループではxが0.3となることを期待しているが、そうならない。 // そのためにこのループは無限ループとなる if (x === 0.3) break; } |
このような場合は、近似値による比較を行うことで、正常に動作させることができます。
Number.EPSILONという定数があり、これは二つの数値が等しいとみなすことができる差を表しています。
1 2 3 4 5 6 7 8 |
var x = 0; while(true) { x += 0.1; console.log(x); // xと0.3の差をとって、その絶対値がNumber.EPSILONより小さい場合、等しいとみなします。 if (Math.abs(x - 0.3) < Number.EPSILON) break; } |
型変換
JavaScriptにおいては、式の評価の際に暗黙の型変換が行われます。
例えば、文字列’1’と数値1を+演算子で演算した際の結果は、文字列’11’となります。
1 2 |
1 + '1' // → '11' |
暗黙の型変換を利用したイディオム
JavaScriptにはこの暗黙の型変換を利用したイディオムが多く存在します。
以下は、undefined値を論理演算するとfalseになることを利用したイディオムです。
1 2 |
x = x || 0 // → xがundefinedの場合0を、そうでなければxをxに代入。 |
こうすることで、xが初期化されていなければ初期値を、初期化されていればそのままその値を使います。
このように、状況に応じて、明示的な型変換と暗黙的な型変換を適切に利用することが必要です。
明示的な型変換と暗黙の型変換
以下に基本型についての明示的な型変換と暗黙の型変換の例を示していきます。
文字列から数値
文字列から数値への型変換には以下があります
- 明示的
- Number関数
- parseInt関数
- parseFloat関数
- 暗黙的
- 加算以外の算術演算
- 単項+演算子
文字列から数値への変換で気をつけるべきは、NaNです。
文字列に対し、算術演算を行った場合、評価結果はNaNとなります。
NaNは数値ですが、一度NaNになった値はどんな演算をしてもNaNのままです。
参考までに、NaNかどうかを判定するときは、isNaN関数を用います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Number('100') // → 100 Number('100x') // → NaN parseInt('100') // → 100 parseInt('100x') // → 100 parseFloat('100') // → 100 parseFloat('100x') // → 100 '10' - 1 // → 9 '10' * 1 // → 10 '10' / 1 // → 10 '10' % 1 // → 0 var x = '30' +s // → 30 var y = '30x' isNaN(+y) // → true |
数値から文字列
数値から文字列への型変換には以下があります。
- 明示的
- String関数
- toString関数
- 暗黙的
- 文字列連結演算
1 2 3 4 5 6 7 |
String(100) // → '100' (100).toString() // → '100' // 暗黙の型変換を利用した文字列への変換のイディオム var n = 100 n+'' // → 100 |
真偽値から数値
真偽値から数値は、三項演算子を使った以下のイディオムで変換できます。
多用するのであれば、関数として定義しても良いかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var b = true; var c = false; var n = b ? 1 : 0; n // → 1 var m = c ? 1 : 0; m // → 0 function boolToNum(b) { return b ? 1 : 0; } boolToNum(b) // → 1 boolToNum(m) // → 0 |
真偽値型への型変換
論理演算が行われるときや、論理判定が行われる時に、真偽値型への変換が行われます。
具体的には、if文やwhile文にて、真偽値型の型変換が起こります。
以下に真偽値型への変換方法を示します。
- 明示的
- Boolean関数
- 暗黙的
- !!演算子
- 論理演算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Boolean('') // → false Boolean('true') // → true Boolean('false') // → true Boolean(true) // → true Boolean(false) // → false !!0 // → false !!NaN // → false !!null // → false !!undefined // → false !!'' // → false !!{} // → true Boolean({}) // → true Boolean(0) // → false // オブジェクトは全てtrueになるので注意 var b = new Boolean(false) !!b // → true // 論理演算による型変換 b || false // → true b && false // → false |
その他の型変換
1 2 3 4 5 6 7 8 9 10 11 |
// その他の値の数値への変換 +true // → 1 +false // → 0 +null // → 0 +undefined // → NaN // オブジェクトが適切な数値へ変換できない場合はNaNとなる var obj = {} obj++ // → NaN |
演算子
JavaScriptの演算子には、いくつかJavaには存在しないものもあります。
- カンマ演算子
- void演算子
カンマ演算子
カンマ演算子を使うことで、複数の式を組み合わせることができます。
カンマ演算子は、オペランドである2つの式を評価して、2つ目の結果を返します。
つまり、複数のカンマ演算子で3つ以上の式を組み合わせた時は最後の結果を返します。
1 2 3 4 |
var x = 1, y = 2, z; z = ++x, y++; z // → 2 |
void演算子
void 演算子は、評価結果としてundefinedを返します。
リテラル表記
豊富かつシンプルなリテラル表記はJavaScriptの特徴の一つです。
平易で汎用性が高いその便利さはJSONの普及にも裏付けされています。
JavaScriptのリテラルはそれ単体でプログラム中に存在できます。
JavaScriptのリテラルには以下の特徴があります。
- 文字列や数値、真偽値だけでなく、オブジェクトや関数もリテラルとして記述できます
- リテラルは独自の型を持ち、変数に格納されることで参照されます。
- リテラルには入れ子にして記述することができるものもあります
オブジェクトリテラル
{}でブロックを作り、:で区切ったkeyとvalueのペアを,で列挙することで平易に記述できます。
1 2 3 4 5 |
{ name: 'k4h4shi', age: 23 } |
オブジェクトリテラルの利用
オブジェクトリテラルを使って、値の受け渡しの表現が容易に記述できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// オブジェクトリテラルを変数に代入 var person = {name: 'k4h4shi', age: 23} // 関数の返り値でオブジェクトリテラルを返す function createPerson() { return { name: 'k4h4shi', age: 23 } } // 引数にオブジェクトリテラルを渡し、名前付きオプションを渡す function operation({ name: 'k4h4shi', age: 23, married: false }) |
関数リテラル
関数リテラルによって、ブロックでまとめたひとまとまりの処理が平易に記述できます。
1 2 3 4 |
function() { console.log('hello world'); } |
関数リテラルの利用
関数リテラルを使うことで、処理の受け渡しの表現が記述できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 関数リテラルを変数に代入 var func = function() { console.log('hello') } // 関数の返り値で関数を返す function returnFunction() { return function() { console.log('it will be returned.') } } // 定義済みの関数の引数に関数を渡し、処理を行う function executeFunction(function() { console.log('it will be executed') }) |
オブジェクト指向
JavaScriptにおけるオブジェクト思考は、プロトタイプベースです。
クラスベースのオブジェクト指向に馴染みのある方にとっては、その型にはまらないやり方に驚くこともあるでしょう。
しかし、知識を深めるにつれて、実は同じ問題を全く違う方法で解決しているのだと気付くと思います。
JavaScriptにおけるオブジェクト指向には以下の特徴があります。
- オブジェクトは本質的に名前を持ちません。
- オブジェクトの階層関係は、実行時に子から親へ参照を辿ることで実現されます。
- オブジェクトのプロパティやインターフェースは、実行時に変更可能で流動的です。
オブジェクトの生成
オブジェクトリテラルによる生成
オブジェクトリテラルを使って、状態と振る舞いを持つオブジェクトを定義すると以下の様になります。
1 2 3 4 5 6 7 8 9 10 11 12 |
var person = { name: 'k4h4shi', age: 23, greeting: function() { console.log('Hello!') } } person.name // → 'k4h4shi' person.age // → 23 person.greeting // → function() { console.log('Hello!') } |
コンストラクタとnew式による生成
関数によってオブジェクトを定義することもできます。
定義された関数は、new式とともにコンストラクタとして呼び出されます。
慣習的にコンストラクタとしての関数名は大文字から始めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Person(name, age) { this.name = name this.age = age this.greeting = function() { console.log('hello world.') } } var person = new Person('john', 23); person.name // → 'k4h4shi' person.age // → 23 person.greeting // → function() { console.log('Hello!') } person.greeting() // コンソールにHello!と出力する |
オブジェクトのプロパティアクセス
オブジェクトのプロパティには通常.演算子を用いることでアクセスできます。
JavaScriptでは上記に加え文字列のプロパティ名と[]演算子を用いることもできます。
.演算子と[]演算子の使い分け
通常は平易な.演算子を用い、.演算子ではプロパティにアクセスできない場合に飲み[]演算子を用いると良いとされています。
以下に.演算子を使えない場合を列挙します。
- 識別子に使えないプロパティ名を使う場合
- 変数の値をプロパティ名に使う場合
- 式の評価結果をプロパティ名に使う場合
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 識別子に使えないプロパティ名を使う場合 var obj = { 'foo-bar': 5} obj['foo-bar'] // → 5 // obj.foo-bar // obj.fooとbarの減算と解釈されるためにエラー // 変数の値をプロパティ名に使う場合 var obj = { x: 10 } var key = 'x'; obj[key] // → 10 // 式の評価結果をプロパティ名に使う場合 // TBD |
プロパティの存在確認及び追加と削除
JavaScriptのオブジェクトは連想配列と考えることもできます。
そのため、実行時にプロパティの存在確認及び追加と削除を行うことができます。
連想配列はkeyとvalueのペアを要素に持つ構造体で、JavaにおけるMapと同義です。
連想配列に対する操作
- プロパティの存在確認: in演算子を用いる
- プロパティの追加: オブジェクトに存在しないプロパティを代入文の左辺にする
- プロパティの削除: delete演算子の右辺に既存のプロパティを指定する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var person = { name: 'k4h4shi', age: 23, greeting: function() { console.log('Hello!') } } person.married = false person.married // → false 'married' in person // → true delete person.married // → true 'married' in person // → false |
自身のプロパティの存在確認
in演算子でプロパティの存在確認を行う場合、プロトタイプ継承をしたプロパティが存在する場合も真となります。
オブジェクト自身のプロパティの存在確認を行いたい場合は、hasOwnProperty関数を使います。
1 2 3 4 5 6 7 8 9 10 |
var obj = { prop: 'ownProperty' } // in演算子 'prop' in obj // → true 'toString' in obj // → true // hasOwnProperty関数 obj.hasOwnProperty('toString') // → false obj.hasOwnProperty('prop') // → true |
for in 文でプロパティ名の列挙
for in 文はJavaScriptのオブジェクトのプロパティ名を列挙するための構文です。
オブジェクトのプロパティの数だけ、ループ変数にプロパティ名を代入しつつブロック内の処理を繰り返します。
以下のように記述を行います。
1 2 3 4 5 6 7 |
var obj = { id: 0, name: "unknown" } for (var prop in obj) { console.log(prop) // id, nameが出力される。 console.log(obj[prop]) // 0, "unknown"が出力される } |
for in 文の注意点
- ループ変数に格納される値の順番は保証されません。
- 列挙できないプロパティも存在します。
- プロトタイプ継承したプロパティも列挙します。
オブジェクトの不変化
JavaScriptのプロパティには、アクセス修飾子といった概念はありません。
そのため、オブジェクトを不変にしたい場合はObjectが持つ関数を用います。
以下の関数にオブジェクトを渡す事で、オブジェクトを不変に出来ます。
- Object.preventExtentions
- Object.seal
- Object.freeze
this参照の規則
thisの参照先は、thisが参照される場所によって異なります。
以下にその対応をまとめます。
- トップレベル: グローバルオブジェクト
- コンストラクタ: 生成対象オブジェクト
- メソッド: メソッドのレシーバーオブジェクト
- applyまたはcall: 引数に渡したオブジェクト
プロトタイプ継承
JavaScriptはクラスベースではなく、プロトタイプベースのオブジェクト指向言語です。
オブジェクト間の共通化は、型ではなくプロトタイプから性質を引き継ぐことで実現します。
プロトタイプチェーン
プロトタイプ継承による引き継ぎは、プロトタイプチェーンによって実現しています。
その前提は以下の二つです。
- 全ての関数(関数オブジェクト)はprototypeというプロパティを持つ
- 全てのオブジェクトは、生成に使ったコンストラクタのprototypeへの暗黙リンクを持つ
オブジェクトのプロトタイプ読み込みの際には、以下の順にプロパティを探します
1. オブジェクト自身のプロパティ
2. 暗黙リンクの参照オブジェクト
3. 2のオブジェクトの暗黙リンクの参照オブジェクトのプロパティ
4. 3を探索が終わるまで続ける(終端はObject.prototypeオブジェクト)
プロトタイプによる振る舞いの設定
プロトタイプ継承により、プロトタイプ経由でオブジェクトに振る舞いを持たせることができます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function MyObject(x, y) { this.x = x this.y = y } MyObject.prototype.show = function() { console.log(this.x, this.y) } var myInstance = new MyObject(0, 1) // プロトタイプから振る舞いを引き継いでいるため、0 1 と出力することができる myInstance.show() |
プロトタイプによる親子関係の実現
プロトタイプ継承によって親子関係を実現することもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 親となるオブジェクト function Parent() { this.type = 'parent' this.isParent = function() { return this.type === 'parent' } } // 子となるオブジェクト function Child() { this.type = 'child' } // 子のプロトタイプに親を設定し引き継ぐ Child.prototype = new Parent() // 親子関係にあるインスタンスを生成 var parent = new Parent(); var child = new Child(); parent.isParent() // → true // プロトタイプから振る舞いを引き継いでいるため呼び出せる child.isParent() // → false // インスタンスのconstructorプロパティが適切になるよう設定する child.constructor // → [Function: Parent] Child.prototype.constructor = Child child.constructor // → [Function: Child] |
型判定
プログラミングにおける型とは、その値に何ができるかを規定するためにあります。
JavaScriptにおける型は曖昧な概念であり、あらかじめ規定しておくことはできません。
そのため、プログラマが適切に見立てる必要があります。
constructorプロパティの参照先
オブジェクトのconstructorプロパティには生成時のコンストラクタの参照が格納されます。
これにより、そのオブジェクトの振る舞いの一端を知ることができます。
1 2 3 4 5 |
var date = new Date() date.constructor // → [Function: Date] var obj = {} obj.constructor // → [Function: Object] |
instanceof演算とisProtoTypeOfメソッド
constructorの値を判定するよりも一般的な方法として以下の2つがあります。
これらはプロトタイプチェーンで拡張継承している場合も判定できます。
- instanceof演算
- isProtoTypeOfメソッド
instanceof演算
左辺にオブジェクト参照、右辺にコンストラクタを指定する判定方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var date = new Date() date instanceof Date // → true date instanceof Object // → true function Parent() {} function Child() {} Child.prototype = new Parent() var child = new Child() child instanceof Child // → true child instanceof Parent // → true child instanceof Object // → true |
isPrototypeOfメソッド
ObjectのisPrototypeOfメソッドを利用する方法です。
コンストラクタのprototypeプロパティをレシーバーオブジェクトとして呼び出し、引数にインスタンスを渡して判定します。
1 2 3 4 5 6 7 8 9 |
function Parent() {} function Child() {} Child.prototype = new Parent() var child = new Child() Child.prototype.isPrototypeOf(child) // → true Parent.prototype.isPrototypeOf(child) // → true Object.prototype.isPrototypeOf(child) // → true |
ダックタイピング
ダックタイピングとは、以下の言葉で表される動的型付け言語における型の作法です。
もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである
あるオブジェクトがあるインターフェースを宣言的に実装していなくとも、そのインターフェースに求められる全ての振る舞いを行うことができるならば、それは実行時にそのインターフェースを実装しているとみなせるということです。
JavaScriptにおいては、実行時にオブジェクトのプロパティとして振る舞いを追加していくことは日常茶飯事のため、その時点での振る舞いを知る必要があります。
in演算によるダックタイピング
in演算子の右辺に文字列のプロパティ名、右辺にオブジェクト参照を指定することで、あるオブジェクトがプロパティを保持しているかが判定できます。
そのプロパティが関数であれば、そのプロパティ名を持つ関数を実行できるということを意味します。
1 2 3 4 5 6 7 8 |
var obj = {} obj.operate = function() { console.log('operate!') } if ('operate' in obj && typeof obj.operate === 'function') { // 'operate!'と出力する obj.operate() } |
グローバルオブジェクト
グローバルオブジェクトとは、起動時から暗黙的に存在するオブジェクトです。
ホスト環境から受け継いだホストオブジェクトのことを指します。
ホスト環境とホストオブジェクト
JavaScriptは拡張言語として開発されてきた歴史があります。
拡張言語は、実行時のコンテキストをホスト環境から受け継ぎます。
JavaScriptでは実行コンテキストをホストオブジェクトとして受け取ります。
クライアントサイドJavaScriptを例にとります。
ホスト環境はWebブラウザであり、ホストオブジェクトはwindowオブジェクトです。
よって、グローバルオブジェクトはwindowオブジェクトとなります。
グローバルオブジェクトへの参照とプロパティ
トップレベルにてthis参照をするとグローバルオブジェクトにアクセスできます。
グローバル変数とグローバル関数
トップレベルにて宣言された変数をグローバル変数、関数をグローバル関数と呼びます。
グローバル変数とグローバル関数はグローバルオブジェクトのプロパティです。
実はJavaScriptには型名と言った概念は存在せず、全て変数です。
ObjectやStringも型名ではなく、全てグローバルオブジェクトのプロパティとなります。
このグローバルオブジェクトがどんなプロパティを持つかは、実行環境に依存します。
まとめ
今回は基礎的な知識についてまとめました。
後編でも、引き続きJavaScriptの基礎知識についてまとめていきます。
☆☆ウィズテクノロジーでは、大阪、東京を中心としたシステム開発を行っております
システムのご相談はもとより、一緒に働くエンジニアもお待ちしております。☆☆