4.1 JavaScript 言語の早いスタート

作者: リン・ハーン優しさ, 作成日:2019-04-26 11:46:12, 更新日:2019-04-27 11:53:43

バックグラウンド

このセクションでは JavaScript の背景を少し説明します.

JavaScript と ECMAScript の対比

ECMAScriptはJavaScriptの公式名称である.JavaScriptに商標があるため,新しい名称が必要になった (当初はSunが保有しており,現在はOracleが保有している).現在,Mozillaは,長い間ライセンスを受けていたため,JavaScriptの名称を公式に使用できる数少ない企業の一つです.一般的な使用のために,以下のルールが適用されます:

  • JavaScriptはプログラミング言語を意味します.
  • ECMAScriptは言語仕様によって使用される名前である.したがって,言語のバージョンを参照するたびに,人々はECMAScriptと言います.JavaScriptの現在のバージョンはECMAScript 5;ECMAScript 6が現在開発されています.

言語 の 影響 と 性質

JavaScriptの作成者,ブレンダン・アイヒは,言語を非常に早く作成するしかなかった (そうでなければ,他の,より悪い技術がネットスケープによって採用されていただろう).彼はいくつかのプログラミング言語から借りた:Java (構文,原始値対オブジェクト),SchemeとAWK (ファーストクラスの関数),Self (プロトタイプ継承),PerlとPython (文字列,配列,正規表現).

JavaScript は ECMAScript 3 まで例外処理をしていなかったため,言語が自動的に値を変換し,まず例外を投げることができませんでした.

JavaScript は,いくつかの機能が欠けています (ブロックスケープ変数,モジュール,サブクラスのサポートなど).一方で,これらの問題を回避するためのいくつかの強力な機能があります.他の言語では,言語の機能を学びます.JavaScript では,代わりにパターンを学びます.

影響力を考慮すると,JavaScriptが機能プログラミング (より高い順序の機能,内蔵マップ,reducerなど) とオブジェクト指向プログラミング (オブジェクト,継承) の混合であるプログラミングスタイルを可能にすることは驚くことではありません.

文法

このセクションではJavaScriptの基本的な文法原理を説明します.

構文の概要

構文の例をいくつか挙げます.

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

平等記号の2つの異なる用途に注意してください.

  • 変数に値を代入するために単一の等号 (=) が使われます.
  • 三重等号 (===) は2つの値を比較するために使用されます (平等演算子を参照).

発言 と 表現

JavaScriptの文法を理解するには,二つの主要な文法カテゴリがあることを知るべきです. 文言と表現:

  • 命令 do things. プログラムとは命令の連続である. 以下は変数 foo を宣言 (作成) する命令の例である.
var foo;
  • 式は,関数引数,割り当ての右側などで,値を生成します.
3 * 7

ステートメントとエクスプレッションの区別は,JavaScriptが if-then-else をステートメントとして実行する2つの異なる方法を持っているという事実によって最もよく示されています.

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

あるいは表現として:

var x = y >= 0 ? y : -y;

函数引数として後者を使用できます (前者ではありません):

myFunction(y >= 0 ? y : -y)

最後に,JavaScript がステートメントを期待する場所でも,表現を使用できます.例えば:

foo(7, 1);

整列は文である (表現文と呼ばれる) が,関数呼び出し foo ((7, 1) は文である.

セミコロン

セミコンロンはJavaScriptではオプションです.しかし, JavaScript がステートメントの終わりを間違えて推測する可能性があるため,常にそれらを含むことをお勧めします.詳細は自動セミコンロイン挿入で説明されています.

セミコラムは,文末を終わらせるが,ブロックは終わらない.ブロックの後ろにセミコラムが表示されるケースがあります:関数式は,ブロックで終わる表現です.そのような表現が文末に来る場合,セミコラムが続く:

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

コメント

JavaScriptには,単行コメントと多行コメントの2種類のコメントがあります.単行コメントは // で始まり,行末に終了します:

x++; // single-line comment

多行コメントは, /* と */ で区切られています.

/* This is
   a multiline
   comment.
 */

変数 と 割り当て

JavaScript の変数は,使用する前に宣言されます.

var foo;  // declare variable `foo`

割り当て

変数を宣言し,同時に値を割り当てることもできます.

var foo = 6;

既存の変数にも値を代入できます.

foo = 4;  // change variable `foo`

複合割り当て事業者

+=のような複合割り当て演算子があります.次の2つの割り当ては等価です.

x += 1;
x = x + 1;

識別子と変数名

識別子とは,JavaScriptで様々な文法的な役割を演じる名前である.例えば,変数の名前が識別子である.識別子は小文字敏感である.

概して,識別子の最初の文字は,任意のユニコード文字,ドル記号 ($),または下記 (_) であり得る.次の文字は,さらに任意のユニコード数字である.したがって,以下のすべての法的な識別子である:

arg0
_tmp
$elem
π

次の識別子は,単語を予約し,文法の一部であり,変数名 (関数名やパラメータ名を含む) として使用することはできません.

img

次の3つの識別子は,予約された単語ではありませんが,それらがそうであるかのように扱うべきです:

img

最後に,標準グローバル変数の名前から遠ざかって下さい.何も壊さずにローカル変数で使うことができますが,コードは混乱します.

価値観

JavaScriptには,プログラミング言語から期待する多くの値があります.ブーリアン,数,文字列,配列,など.JavaScriptのすべての値にはプロパティがあります.各プロパティにはキー (または名前) と値があります.プロパティはレコードのフィールドのように考えることができます.プロパティを読み取るには,dot (.) オペレーターを使用します:

value.propKey

例えば文字列 abc は,長さの属性があります.

> var str = 'abc';
> str.length
3

前記は以下のように書ける.

> 'abc'.length
3
The dot operator is also used to assign a value to a property:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

メソッドを呼び出すことができます.

> 'hello'.toUpperCase()
'HELLO'

前例では,toUpperCase ((() というメソッドを値"hello"で呼び出した.

原始 的 な 価値 と 物

JavaScript は,値の間には,かなり任意の区分をします.

  • プリミティブ値はブーリアン,数,文字列,ゼロ,未定義です
  • 他のすべての値はオブジェクトです. この2つの間の大きな違いは,それらを比較する方法である.各オブジェクトは独自のアイデンティティを持ち, (厳格に) 自分自身に等しいだけである:
> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

対照的に,同じ値をコードするすべての原始値は同じと考えられる:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

次の2つのセクションでは,原始値とオブジェクトを詳細に説明します.

原始 的 な 価値観

以下は,すべての原始値 (簡略に原始値) です.

  • ブール数: true, false (ブル数参照)
  • 数:1736,1.351 (数 を 参照)
  • 弦: abc, abc (弦を参照)
  • 2つの nonvalues: undefined, null ( undefined と null を参照)

原始者は以下の特徴を持っています:

価値による比較

内容は以下のように比較されます.

> 3 === 3
true
> 'abc' === 'abc'
true

### いつも変わらない プロパティは変更,追加,または削除できません:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ⇒ no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ⇒ no effect, unknown property
undefined

(未知のプロパティを読み取ると,常に undefined を返します)

対象物

すべての非原始値はオブジェクトである.最も一般的なオブジェクトは:

  • 単一のオブジェクト (単一のオブジェクトを参照) によって作成できる単純なオブジェクト:
{
    firstName: 'Jane',
    lastName: 'Doe'
}

前回のオブジェクトには2つのプロパティがあります.プロパティ firstName の値は Jane,プロパティ lastName の値は Doeです.

  • 配列文字で作成できる配列 (配列を参照):
[ 'apple', 'banana', 'cherry' ]

前回の配列には,数値インデックスでアクセスできる3つの要素があります.例えば,appleのインデックスは0です.

  • 規則式は,規則式文字で作成できる (規則式を参照):
/^a+b+$/

対象は以下の特徴を有する.

参照による比較

各値が独自のアイデンティティを持っています.

> ({} === {})  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

デフォルトで変更可能

通常,プロパティを自由に変更,追加,削除することができます (単一のオブジェクトを参照してください):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

定義されていない

ほとんどのプログラミング言語には,欠落した情報を表示する値があります.JavaScriptには,このような2つの値があります.

  • undefined は 無値. 初期化されていない変数は undefined:
> var foo;
> foo
undefined

欠けているパラメータは定義されていません:

> function f(x) { return x }
> f()
undefined

定義されていないものになります.

> var obj = {}; // empty object
> obj.foo
undefined
  • null は,オブジェクトが期待されるときに (パラメータ,オブジェクトのチェーンで最後のものなど) ノンバリューとして使用されます.

警告

undefinedとnullにはプロパティがなく,toStringのような標準的なメソッドもありません.

定義されていないか NULL をチェックする

機能は通常, undefined または null を使って欠けている値を表示することを許可します.

if (x === undefined || x === null) {
    ...
}

また undefined と null は false とみなされるという事実も利用できます.

if (!x) {
    ...
}

警告

false, 0, NaN,および も false とみなされる (Truthy と Falsy を参照).

タイプとインスタンスの利用による値の分類

値の分類には2つの演算子があります. typeofは主に原始値に使用され,instanceofはオブジェクトに使用されます. 型はこんな感じです

typeof value

値の type を記述する文字列を返します.いくつかの例があります:

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

次の表では,Typeofのすべての結果が示されています.

img

typeof null return object は,既存のコードを壊すため修正できないバグです. null はオブジェクトであることを意味しません.

この例はこんな感じです

value instanceof Constr

値がコンストラクターConstrによって作成されたオブジェクトである場合は true を返します (Constructors: Factories for Objectsを参照してください). 以下はいくつかの例です:

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

ブール数

プリミティブ・ブーリアン型は true と false の値を構成する.次の演算子はブーリアン型を生成する:

  • バイナリ論理演算子:&& (And),汝 (Or)
  • 前置論理演算子:! (ない)
  • 比較演算子:平等演算子: ===,!==, ==,!=
  • 順序付け演算子 (文字列と数字の) >, >=, <, <=

真実 と 偽り

JavaScript がブール値 (if ステートメントの条件) を期待するときはいつでも,任意の値を使用できます. true または false として解釈されます.以下の値は false として解釈されます:

  • 定義されていない,ゼロ
  • ブル語: false
  • 番号: 0 NaN
  • 文字列:

他のすべての値 (すべてのオブジェクトを含む!) は真とみなされます. 誤りとして解釈された値は誤り,真として解釈された値は真実と呼ばれます. 函数として呼ばれるブーリアン (Boolean))) は,そのパラメータをブーリアンに変換します. 値をどのように解釈するかテストするためにそれを使用できます:

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

バイナリ論理演算子

JavaScript のバイナリ論理演算子はショート回路である.つまり,最初の演算子が結果を決定するのに十分である場合,第2の演算子は評価されない.例えば,次の式では,関数 foo() は決して呼び出されない:

false && foo()
true  || foo()

さらに,バイナリ論理演算子は,そのオペランドのいずれかを返します.これはブーリアンである場合もあるし,ない場合もある.どのオペランドが正しいかを判断するために,真性のチェックが使用されます.

そして (&&)

最初のオペランドが false ならば返します.そうでなければ,第2オペランドを返します.

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

または (あなた)

最初のオペランドがtruthyなら返します.そうでなければ2番目のオペランド返します.

> 'abc' || 123
'abc'
> '' || 123
123

平等事業者

JavaScriptには2種類の等式があります

  • 正常,または優しく, (不平等): == と!=
  • 厳格 (不平等) === と!==

正常等式は,多くの値が等しいと考えられる (詳細は正規 (緩い) 等式 (==,!=) で説明されている),これはバグを隠すことができる.したがって,常に厳格な等式を使用することが推奨される.

数字

JavaScript のすべての数字は浮動小数点で:

> 1 === 1.0
true

特別番号には以下の番号があります:

NaN (数ではない) エラー値:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

無限 誤差値も多い:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Infinityは他の数よりも大きい (NaNを除く).同様に, -Infinityは他の数よりも小さい (NaNを除く).これはこれらの数をデフォルト値として有用にする (例えば,最小値や最大値を探しているとき).

事業者

JavaScriptには以下の算術演算子 (算術演算子を参照) があります.

  • 追加:数1 +数2
  • 減算:数1 -数2
  • 掛け算:数1 *数2
  • 部門:第1号 /第2号
  • 残り:数1 %数2
  • 増量: ++変数,変数++
  • デクレメント: 変数,変数
  • 負: -値
  • 数値に変換する: +値

グローバルオブジェクト数学 (数学を参照) は関数を通じて,より多くの算術操作を提供します.

JavaScriptにはビットバイスオペレーション用のオペレータも含まれています (例えば,ビットバイス And,ビットバイスオペレータを参照).

文字列は文字列リテラルを使用して直接作成できます. これらのリテラルは単一のまたは二重引数で境界されます. バックスラッシュ () は文字を逃れ,いくつかの制御文字を生成します. 以下はいくつかの例です:

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

単一の文字は括弧でアクセスできます.

> var str = 'abc';
> str[1]
'b'

文字列の文字数を数えるプロパティの長さ:

> 'abc'.length
3

すべてのプリミティブと同様に 文字列は不変です 既存の文字列を変更するには 新しい文字列を作成する必要があります

文字列演算子

文字列はプラス (+) オペレーターによって連結され,他のオペランドが文字列である場合,他のオペランドを文字列に変換します.

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

複数のステップで文字列を連結するには, += オペレーターを使用します:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

文字列方法

文字列には多くの有用なメソッドがある (文字列プロトタイプメソッドを参照). 以下はいくつかの例です:

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

声明

JavaScript の条件式とループは,次のセクションで紹介されています.

条件

if 文には then 項と任意の else 項があり,ブル式条件によって実行されます.

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

私は常に括弧を使用することをお勧めします (それらはゼロまたはそれ以上の文のブロックを表します). しかし,条項が単一の文である場合,そうする必要はありません ( for と while の制御フロー文も同じです):

if (x < 0) return -x;

実行されるケースを fruit の値が決定します.

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

operand の後記は任意の式であり, === を使ってスイッチのパラメータと比較する.

ループ

for ループは次の形式があります.

for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
    «statement»

init はループの開始時に実行されます. condition はループの繰り返しの前にチェックされます. false になる場合,ループは終了します. post_iteration はループの繰り返しの後に実行されます.

この例では,コンソール上の配列 arr のすべての要素をプリントします.

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

状態が保持される間,ループはその体の上にループを継続します.

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

do-whileループは,その条件が保持される間,その体の上にループを継続する.条件がボディに続くため,ボディは常に少なくとも一度実行されます:

do {
    // ...
} while (condition);

すべてのループで:

  • 断片はループを残します.
  • ループの繰り返しを開始します

機能

関数を定義する一つの方法は,関数宣言によるものです.

function add(param1, param2) {
    return param1 + param2;
}

2つのパラメータ,param1とparam2を持つ関数を定義し,両方のパラメータの和を返します.

> add(6, 1)
7
> add('a', 'b')
'ab'

add (add) を定義するもう1つの方法は,変数add (add) に関数式を代入することです.

var add = function (param1, param2) {
    return param1 + param2;
};

関数式は値を生成し,したがって関数を他の関数に引数として直接渡すのに使用できます.

someOtherFunction(function (p1, p2) { ... });

職務 宣言 が 掲げ られ て い ます

関数宣言は,全部を現在の範囲の初めに移動します.これは,後で宣言される関数を参照することを可能にします:

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

var 宣言も掲げられているが (変数も掲げられているを参照),それらによって実行される割り当ては掲げられていないことに注意してください.

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

特殊変数の引数

JavaScript の任意の数値で任意の関数を呼び出すことができます. 言語は決して文句を言うことはありません. しかし,すべてのパラメータを特殊変数引数で利用できます. arguments は配列のように見えますが,配列メソッドはありません:

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

論議 が 多い か 少ない か

JavaScript で too many または too few パラメータがどのように処理されているかを調べるために,次の関数を使用しましょう (toArray(関数はArray に引数を変換する に示されています):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

追加パラメータは無視されます (引数を除く):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

パラメータが欠落すると,値は未定義になります.

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

選択可能なパラメータ

以下は,パラメータにデフォルト値を代入するための一般的なパターンです.

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

(1) 行では,x がtruthy (null, undefined, etc.) でない場合,x を返します.そうでない場合は,次の2番目のオペランドを返します.

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

アリティ を 強制 する

arity (特定のパラメータ数) を強制したい場合は, arguments.length をチェックできます:

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

引数を配列に変換する

arguments は配列ではありません.ただ配列のようなものです (Array-Like Objects and Generic Methods を参照してください). 属性長度があり,その要素は平方括弧のインデックスでアクセスできます. ただし,要素を削除したり,配列方法のいずれかを呼び出すことはできません. したがって,時には配列に引数を変換する必要があります.これは次の関数で実行されます (Array-Like Objects and Generic Methods で説明されています):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

例外処理

例外を扱う最も一般的な方法 (14章参照) は以下のとおりです.

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

try 条項は,重要なコードを囲み, catch 条項は try 条項内に例外が投げ込まれると実行されます.前記コードを使用すると:

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

厳格モード

厳格モード (厳格モードを参照) は,より多くの警告を有効にし,JavaScriptをよりクリーンな言語にします (非厳格モードは時にスロッピーモードと呼ばれます).それをオンにするには,JavaScriptファイルまたは

'use strict';

また,関数ごとに厳格モードを有効にすることもできます.

function functionInStrictMode() {
    'use strict';
}

変数範囲と閉鎖

変数を使用する前に var を使って宣言します

> var x;
> x
undefined
> y
ReferenceError: y is not defined

1 つのvar文で複数の変数を宣言し初期化できます.

var x = 1, y = 2, z = 3;

しかし,変数ごとに1つの文を使用することをお勧めします (その理由はSyntaxで説明されています).

var x = 1;
var y = 2;
var z = 3;

ハイスト (variables are hoisted) のため,通常は関数の開始時に変数を宣言するのが最善です.

変数 は 機能 範囲 に ある

変数の範囲は常に完全な関数 (現在のブロックとは異なり) である.例えば:

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

変数 tmp は (1) 行から始まるブロックに限定されないので,関数の終わりまで存在します.

変数 は 上げ られ て い ます

各変数宣言は上げられる:宣言は関数の初めに移動するが,その実行する割り当ては置かれ.例として,次の関数の行 (1) の変数宣言を考慮する.

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

内部では,前項の関数は次のように実行されます.

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

閉鎖

各関数は,その周囲の関数の変数と接続され続けます.例えば:

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

(1) 行から始まる関数は,作成された文脈を離れますが, start のライブバージョンに接続されます.

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

閉じは,関数と周囲の範囲の変数との接続である.したがって,createIncrementor() が返すものは閉じである.

IIFE パターン:新しい範囲を導入する

時には,新しい変数の範囲を導入したい場合もあります.例えば,変数がグローバルになるのを防ぐために.JavaScriptでは,ブロックを使用することはできません.関数を使用する必要があります.しかし,ブロックのような方法で関数を使用するためのパターンがあります.それはIIFE (即座に呼び出された関数表現,発音iffy):

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

前の例を正確に表示されているように入力してください (コメントを除いて). IIFE は,定義した直後に呼び出される関数式です.関数内に新しい範囲が存在し,tmp がグローバルになるのを防ぎます. IIFE の詳細については,IIFE による新しい範囲の導入を参照してください.

IIFEの利用例: 閉鎖による無意識の共有

外部変数への接続を保ちます これは望ましくない場合もあります

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)

行 (1) に返される値は,常に i の現在の値であり,関数が作成されたときの値ではありません.ループが完了した後,i は 5 の値を持ち,配列内のすべての関数がその値を返します.行 (1) の関数が i の現在の値のスナップショットを受信することを望む場合は,IIFE を使用できます:

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

物 と 構成 物

このセクションでは,JavaScriptの2つの基本的なオブジェクト指向メカニズム,単一のオブジェクトとコンストラクター (他の言語のクラスに似ているオブジェクトの工場) をカバーします.

単一の目的

すべての値と同様に,オブジェクトにはプロパティがあります.実際には,オブジェクトをプロパティのセットと考えることができます.各プロパティは (キー,値) ペアです.鍵は文字列であり,値は任意のJavaScript 値です.

直接オブジェクト文字を使って シンプルオブジェクトを作成できます

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

前のオブジェクトにはプロパティの名前と記述があります. (get) と (set) のプロパティを読み書きすることができます:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

describe のような関数値のプロパティはメソッドと呼ばれます.

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

in オペレーターは,プロパティが存在するかどうかをチェックします:

> 'newProperty' in jane
true
> 'foo' in jane
false

存在していないプロパティを読み取ると,定義されていない値が得られます.したがって,前回の2つのチェックも次のように実行できます:

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

削除演算子はプロパティを削除します:

> delete jane.newProperty
true
> 'newProperty' in jane
false

任意の所有鍵

プロパティキーは任意の文字列であることができる.これまで,オブジェクトリテラルとドットオペレータの後にプロパティキーは見ました.しかし,識別子である場合にのみ,その方法でそれらを使用できます (識別子と変数名を参照してください).他の文字列をキーとして使用したい場合は,オブジェクトリテラルで引用し,プロパティを取得し,設定するために四角括弧を使用する必要があります.

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

また,プロパティの鍵を計算することもできます.

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

抽出方法

メソッドを抽出すると,そのメソッドはオブジェクトとの接続を失います.それ自体では,関数はメソッドではなく,この値は undefined (厳格モード) です.

前のオブジェクト Jane に戻りましょう.

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

jane から describe メソッドを抽出し,変数 func に入れ,それを呼び出したい.しかし,それは機能しません:

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

解は,すべての関数が持つbind() メソッドを使用することです.これは常に与えられた値を持つ新しい関数を作成します:

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

方法 の 中 の 機能

すべての関数は独自の特殊変数 this を持っている.これは,関数をメソッド内に嵌めると不都合です.なぜなら,関数からメソッド this にアクセスできないからです.次の例では,関数で繰り返す function forEach を呼び出します:

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

ログHiToFriends を呼び出すとエラーが発生します.

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

まず,これを別の変数に保存します.

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

この2つのパラメータは,

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

関数式は,JavaScript での関数呼び出しでしばしば引数として使用されます.これらの関数式からこれを参照するときは常に注意してください.

建築家:物件製造工場

これまでのところ,JavaScript オブジェクトは文字列から値へのマップにすぎないと考えられ,これは他の言語のマップ/辞書リテラルに似ているJavaScriptのオブジェクトリテラルによって示唆されている考えである.しかし,JavaScript オブジェクトは,本当にオブジェクト指向の機能である継承もサポートしている.このセクションはJavaScript 継承の仕組みを完全に説明していないが,開始するための簡単なパターンを示している.もっと知りたい場合は第17章を参照してください.

函数とは,実物関数や方法であるだけでなく,JavaScriptでも別の役割を果たします.新しい演算子で呼び出す場合,オブジェクトのコンストラクターになる.コンストラクターは,他の言語のクラスとほぼ類似しています.従来のように,コンストラクターの名前は大文字で始まります.例えば:

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

コンストラクタには2つの部分があることがわかります.まず,関数Pointはインスタンスのデータを設定します.次に,プロパティPoint.prototypeにはメソッドのオブジェクトが含まれています.前者データは各インスタンスの特異で,後者はすべてのインスタンスの間で共有されます.

ポイントを使うには new operator を使って呼び出します

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p は Point のインスタンスの 1 つです.

> p instanceof Point
true

配列

配列はゼロから始まる整数インデックスでアクセスできる要素の配列です

アレイリテラル

配列文字は配列を作成するのに便利です.

> var arr = [ 'a', 'b', 'c' ];

先行配列には 3 つの要素があります: 文字列 a, b, c. 整数インデックスでアクセスできます:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

length プロパティは,配列にどれだけの要素があるかを示します.それを用いて要素を追加したり,要素を削除したりできます:

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

配列にも動作します.

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

配列はオブジェクトであり,したがってオブジェクト属性を持つことができることに注意してください:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

配列方法

配列には多くの方法がある (配列プロトタイプ方法を参照). 以下はいくつかの例である:

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

配列を繰り返す

要素を繰り返すための数列メソッド (Iteration (Nondestructive) を参照) がいくつかあります.最も重要な2つは forEachとmapです.

forEachは配列を繰り返して,現在の要素とそのインデックスを関数に渡します:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

前記コードは次の出力を生成します.

0. a
1. b
2. c

線 (1) の関数は,引数を無視する自由があることに注意してください.例えば,パラメータ elem をのみ持っています.

map は,既存の配列の各要素に関数を適用することで新しい配列を作成します.

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

規則 的 な 表現

JavaScript には正規表現のサポートが内蔵されています.

/^abc$/
/[A-Za-z0-9]+/

メソッドテスト:マッチは?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

メソッド exec ((): マッチとキャプチャグループ

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

返された配列には,インデックス 0 の完全なマッチ,インデックス 1 の最初のグループのキャプチャなどが含まれます.このメソッドを繰り返し呼び出す方法 (RegExp.prototype.exec: Capture Groups で議論されています) があり,すべてのマッチを取得できます.

メソッド replace ((): 検索とReplace

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

replace の最初のパラメータは /g フラグ付きの正規式でなければならない.そうでなければ,最初の発生のみが置き換えられる.また, (String.prototype.replace: Search and Replace で議論されているように) 置き換えを計算するために関数を使用する方法もあります.

数学

数学とは算数関数を持つ物体である.いくつかの例は以下のとおりである.

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

もっと