Skip to content

Instantly share code, notes, and snippets.

@culage
Last active August 29, 2015 14:00
Show Gist options
  • Save culage/11205225 to your computer and use it in GitHub Desktop.
Save culage/11205225 to your computer and use it in GitHub Desktop.

モダンjavascript学習

[2014-01-23]

https://developer.mozilla.org/ja/docs/Web/JavaScript/New_in_JavaScript https://developer.mozilla.org/ja/docs/Web/JavaScript/New_in_JavaScript/1.7

レガシーとモダンの定義

ここでは javascript1.6 以降をモダンjavascriptとする。

IE6~IE8は、JavaScript1.5を採用しているため、レガシーブラウザでも通用するjavascriptは、javascript1.5と考えてよい。

http://ja.wikipedia.org/wiki/JavaScript#.E3.83.90.E3.83.BC.E3.82.B8.E3.83.A7.E3.83.B3.E3.81.A8.E3.83.96.E3.83.A9.E3.82.A6.E3.82.B6.E3.81.AE.E5.AF.BE.E5.BF.9C.E8.A1.A8

Array の拡張

一覧

  • javascript1.6

    • indexOf() - 与えられた項目が最初に出現するインデックスを返す。
    • lastIndexOf() - 与えられた項目が最後に出現するインデックスを返す。
    • every() - 配列の各項目において関数を実行し、訪れることのできたすべての項目について関数が true を返した場合に true を返す。※
    • some() - 配列の各項目において関数を実行し、訪れることのできたどれかの項目について関数が true を返した場合に true を返す。※
    • filter() - 配列の各項目において関数を実行し、関数が true を返した項目全体からなる配列を返す。※
    • forEach() - 配列の各項目において関数を実行する。※
    • map() - 配列の各項目において関数を実行し、その結果の配列を返す。※
  • javascript1.8

    • reduce() - 配列の全ての要素に関数を実行し、直前の呼び出しから結果を収集して、単一の値にする。※
    • reduceRight() - 添字最後から最初に向かって実行する以外は、reduceとまったく同じ。※
  • javascript1.8.5

    • Array.isArray() - 与えられた変数が配列であるかどうかを調べる。 ※Array.prototype.isArrayではないことに注意

※第2引数を指定すると、その内容をthisとして関数を実行する ※コールバックは、要素の値、要素のインデックス、走査されている Array オブジェクトの3つの値が引数に渡されて呼び出される

詳細

indexOf()

与えられた項目が最初に出現するインデックスを返す。

[1, 2, 3, 4, 5, 3].indexOf(3) // 2

lastIndexOf()

与えられた項目が最後に出現するインデックスを返す。

[1, 2, 3, 4, 5, 3].lastIndexOf(3) // 5

every()

配列の各項目において関数を実行し、訪れることのできたすべての項目について関数が true を返した場合に true を返す。

// 全て偶数か?
[2, 4, 12, 6, 30].every(function(i) { return i % 2 == 0 })  // true
[2, 5, 12, 6, 30].every(function(i) { return i % 2 == 0 })  // false

some()

配列の各項目において関数を実行し、訪れることのできたどれかの項目について関数が true を返した場合に true を返す。

// 偶数が混ざっているか?
[1, 3, 12, 7, 5].every(function(i) { return i % 2 == 0 })  // true
[1, 3, 13, 7, 5].every(function(i) { return i % 2 == 0 })  // false

filter()

配列の各項目において関数を実行し、関数が true を返した項目全体からなる配列を返す。

// 偶数のみを抜き出す
[2, 3, 12, 7, 30].filter(function(i) { return i % 2 == 0 })  // [2, 12, 30]

forEach()

配列の各項目において関数を実行する。 ループの関数内はその外側ブロックとローカル変数を共有するが、thisはグローバルオブジェクトになるため注意。

// 各要素をコンソールに出力
var self = this; // this
[2, 3, 12, 7, 30].forEach(function(i, counter) {
  console.log(self, counter, i);
});

// thisを引数指定することも可能
[2, 3, 12, 7, 30].forEach(function(i, counter) {
  console.log(this, counter, i);
}, this);

map()

配列の各項目において関数を実行し、その結果の配列を返す。

// 配列の数値を2倍する
[2, 3, 12, 7, 30].map(function(i) { return i * 2 })  // [4, 6, 24, 14, 60]

(javascript1.8)

reduce()

配列の全ての要素に関数を実行し、直前の呼び出しから結果を収集して、単一の値にする。 1回目は1番目の要素と2番目の要素を渡して関数が処理され、以降はその結果と次の要素が渡されて処理される。 関数によってsumやmaxなどを実現できる非常に汎用性の高いメソッド。

// 合計を求める
[2, 3, 12, 7, 30].reduce(function(i1, i2) { return i1 + i2; })  // 54

// 最大を求める
[2, 3, 12, 7, 30].reduce(function(i1, i2) { return Math.max(i1, i2); })  // 30

関数に続いて第二引き数を渡すと、1回目に渡された初期値と1番目の要素を渡して関数が処理される。

// 合計を求めるreduceは、初期値を文字列にすると文字列結合を意味するようにする
[2, 3, 12, 7, 30].reduce(function(i1, i2) { return i1 + i2; }, "")  // 2312730

reduceRight()

添字最後から最初に向かって実行する以外は、reduceとまったく同じ。

(javascript1.8.5)

Array.isArray()

与えられた変数が配列であるかどうかを調べる。

Array.isArray([1,2,3]); // true
Array.isArray("1,2,3"); // false

String の拡張

(javascript1.8.1)

基本

単純なメソッドの追加なので、詳細説明は無し。

trim() - 文字列の両端の空白を削除します。 trimLeft() - 文字列の左 の空白を削除します。 trimRight() - 文字列の右 の空白を削除します。

Object の拡張

(javascript1.9)

基本

  • create() - 指定されたプロトタイプオブジェクトおよびプロパティに基づくオブジェクトを生成します。バグ 492840
  • defineProperty() - 指定された記述に基づいた名称のプロパティを、オブジェクトに追加します。
  • defineProperties() - 指定された記述に基づく複数のプロパティを、オブジェクトに追加します。
  • getOwnPropertyDescriptor() - オブジェクトの指定された名前のプロパティの詳細を返します。バグ 505587
  • keys - オブジェクトのすべての列挙可能 (enumerable )なプロパティを配列の形式で返します。 バグ 307791
  • getOwnPropertyNames() - オブジェクトのすべてのプロパティを列挙可能の如何に関わらず (enumerable and non-enumerable)配列として返します。 バグ 518663
  • preventExtensions() - オブジェクトのいかなる拡張 (extensions)も禁止します。バグ 492849
  • isExtensible() - オブジェクトが拡張可能かどうかを判断します。バグ 492849
  • seal() - オブジェクトのプロパティが他のコードにより削除されるのを禁止(封印 (seal))します。バグ 492845
  • isSealed() - オブジェクトが封印されている (sealed)かどうかを判断します。バグ 492845
  • freeze() - オブジェクトを凍結 (Freeze)します。これにより、いかなるコードも凍結されたオブジェクトのプロパティの削除または変更ができなくなります。バグ 492844
  • isFrozen() - オブジェクトが凍結されているかどうかを判断します。バグ 492844

詳細

古いjavascriptではユーザがプロパティへのアクセスを制御することは出来なかったが、 モダンjavascriptでは読み取り専用プロパティなどを作成することが可能になっている。

とは言え、create以外はあまり使われないようだ。

create(prototype, props)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create

// 2 つのサンプルプロパティを持つオブジェクトを生成する例です
// (第 2 引数はキーを *プロパティディスクリプタ* に対応づけることに注意してください)
o = Object.create(Object.prototype, {
  // foo は通常の "値を持つプロパティ" です。
  foo: { writable:true, configurable:true, value: "hello" },
  // bar は getter および setter (アクセサ) プロパティです
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) { console.log("Setting `o.bar` to", value) }
})

プロパティディスクリプタ - Property Descriptor

for(i in obj)構文で列挙されるか、書き換えられるか、getter/setterメソッドは使われるかなどを 指示するための指定オブジェクト。

以下のプロパティを持つ。

  • value - プロパティの値。既定値は undefined。
  • get - そのプロパティにアクセスしたときに呼ばれる関数. getter と同じ。既定値は undefined。
  • set - そのプロパティに代入したときに呼ばれる関数. setter と同じ。既定値は undefined。
  • writable - 値を変更可能か (bool)。既定値は false。
  • configurable - Property Descripter を変更可能か (bool)。既定値は false。
  • enumerable - 列挙可能か (for/in でループされるかどうか) (bool) 。既定値は false。

Array および String の汎用化

(javascript1.6)

Array と String のメソッドを Array と String オブジェクトではないものに適用したい場合は、 prototype から関数オブジェクトを参照して call や apply メソッドを使う必要があった。

Array と String については、これの簡易的な記法が可能になった。 文字列が全て小文字で構成されているかを判定する場合、を例にする。

// 汎用化前
Array.prototype.every.call("TestString", function(c){ return "a" <= c && c <= "z"; });    // false
Array.prototype.every.call("teststring", function(c){ return "a" <= c && c <= "z"; });    // true

// 汎用化後
Array.every("TestString", function(c){ return "a" <= c && c <= "z"; });   // false
Array.every("teststring", function(c){ return "a" <= c && c <= "z"; });   // true

const

(javascript1.5)

基本

javascript1.5から使えるらしいが、あんまり使わないので一応ここ書いておく。

定数を宣言する命令。

const prefix = '212';

for each ... in 構文

(javascript1.6)

基本

javascript の for ... in は、オブジェクトのキーを列挙する。

var sum = '';
var obj = {prop1: 5, prop2: 13, prop3: 8};
for (var item in obj) {
  sum += item;
}
console.log(sum); // "prop1prop2prop3",

それに対して、javascript の for each ... in は、オブジェクトの値を列挙する。 (正直、書き方の差が微妙すぎる気がするが)

var sum = 0;
var obj = {prop1: 5, prop2: 13, prop3: 8};
for each (var item in obj) {
  sum += item;
}
console.log(sum); // "26",

yield

(javascript1.7)

基本

yield は、一言で言うと一時停止・再開ができる関数を作成するための命令。

内部にyieldが書かれた関数を実行すると、その内部の処理が実行されない。 代わりに、その内部の処理を一時停止・再開しながら実行するための「ジェネレータオブジェクト」を戻り値として戻す。

ジェネレータは、イテレータ(iterator)の一種で、next()メソッドを持つオブジェクトである。 next()を実行することで、 最後に一時停止した箇所(初回は関数の最初)から処理が再開され、 次のyieldが出現する箇所までの実行が行われて一時停止される。 yieldが最後に出現するところまで実行した後で、さらにnext()を実行するとStopIterationエラーがthrowされる。

function generatorFactory() {
  var i = 0;
  
  i = i + 1;
  yield i;  // [A]

  i = i + 1;
  yield i;  // [B]

  i = i + 1;
  yield i;  // [C]
}

var g = generatorFactory();
g.next();  // [A]までの内容が実行されて一時停止される
g.next();  // [B]までの内容が実行されて一時停止される
g.next();  // [C]までの内容が実行されて一時停止される
g.next();  // StopIterationエラーがthrowされる。

foreach との組み合わせ

ジェネレータに限った話ではないが、イテレータは for (x in obj) 構文によって イテレータの持つ全アイテムに対して処理を行う事ができる。

next()メソッドを使う方法と違ってエラーが投げられないため、こっちを使ったほうが便利。

var g = generatorFactory();
for (var i in g) {    // 1, 2, 3 と出力される
  console.log(i);
}

配列内包

(javascript1.7)

基本

PythonとかCoffeeScriptとかにある、ループの結果として配列を作成するような書式のアレ。 CoffeeScriptと違って、囲む記号は[]なので注意。CoffeeScriptは()。

[i * 2 for each (i in [1,2,3,4,5])] // [2,4,6,8,10]

フィルタを付ける

条件に一致したもののみを配列として抽出するには、ifを使う。 CoffeeScriptと違って、フィルタは if なので注意。 CoffeeScriptはwhen。(注意:CoffeeScriptには 後置if構文 があるので、whenと書くべきところを間違えてifと書いても構文エラーにはならない)

[i for each (i in [1,2,3,4,5]) if (i % 2 == 0)] // [2, 4]

Range は組み込みオブジェクトとしては存在しない

……ので、自前で作ってやる必要がある。

Range = function(start,end){ for (var i=start; i<end; i++) yield i; };

[i for (i in Range(0, 5))] // [0,1,2,3,4]

let

(javascript1.7)

より良いvar。

var はどこで宣言しても関数スコープになってしまうが、let はスコープを明示的に制限することができる。 let には、以下の3つの使い方がある。

let 文

let の後に続く1命令、またはブロックで囲んだ範囲をスコープとする。

var x = 1;
var y = 2;

// 1命令
console.log( let (x = 3, y = 4) x + y );  // 7

console.log(x + y);  // 3

// ブロック
let ( x = 3, y = 4 ) {
  console.log(x + y);  // 7
}

console.log(x + y);  // 3

let 定義

var と同じ書式で利用するのだが、let はそれが宣言された位置に対して「期待通り」のスコープを持つ。

// ブロック内をスコープとする場合
if (x > y) {
  let gamma = 12.7 + y;
  gamma = gamma * x;
  console.log(gamma);
}

// for条件で使うことも可能
let i = 99;
for (let i=1; i<=5; i++) {
  console.log(i); // 1..5
}
console.log(i); // 99

またループの中で使われる let 定義は、各ループごとに別の変数として扱われる。 これはクロージャから参照される場合などに大きく影響する。

HTML
<input id="btn1" type="button" value="btn1">
<input id="btn2" type="button" value="btn2">
<input id="btn3" type="button" value="btn3">

// 各ボタンにイベントを設定(var版)
// 各イベントハンドラは同じ変数「i」を参照するため、全てのボタンで4が表示される
for (var i=1; i<=3; i++) {
  $("#btn" + i).click(function(){ alert(i); });
}

// 各ボタンにイベントを設定(let版)
// 各イベントハンドラは各ブロックの変数「x」を参照するため、ボタンごとに違う数値が表示される。
for (var i=1; i<=3; i++) {
  let x = i;
  $("#btn" + x).click(function(){ alert(x); });
}

ちなみに、for 条件内の let は for ブロック内ではなくループ全体をスコープとするので 以下の記述ではボタンごとに違う数値が表示される動作にならない。

for (let i=1; i<=3; i++) {
  $("#btn" + i).click(function(){ alert(i); });
}

ラムダ式 (式クロージャ)

(javascript1.8)

基本

関数が短く書けるようになった。 ……が、CoffeeScriptとかを見慣れていると正直そんなに短くなってない気がする。

以下の2つが等価である。

f = function(x) { return x * x; }
f = function(x) x * x;

ECMA Script 6 (2014年1月現在、策定中)

さらに短く書けるようになり、ほぼCoffeeScriptと同じになる。

以下の3つが等価である。

f = function(x) { return x * x; }
f = function(x) x * x;
f = (x)=>x*x;

その他

Date.toJSON

Dateオブジェクトを、JSON形式の文字列として返します。 toStringの変形版のようなもの。

(new Date()).toJSON()    // "2014-04-23T06:50:34.776Z"
(new Date()).toString()  // "Wed Apr 23 2014 15:53:36 GMT+0900"

function.bind

引数で指定したオブジェクトを this として function を実行する関数を作成し、戻り値として戻す。 説明文を読むよりも、例をみたほうが分かりやすいと思う。

// bind メソッドで生成された関数オブジェクトの呼び出し
var func = function(){ console.log(this); };

var boundFunc = func.bind(boundObj);
obj.boundFuncMethod = boundFunc;

boundFunc();                // ... thisは、boundObj
obj.boundFuncMethod();      // ... thisは、boundObj
boundFunc.call(targetObj);  // ... thisは、boundObj
boundFunc.apply(targetObj); // ... thisは、boundObj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment