関数
関数は値
- JSでは関数は値
Function
クラスのオブジェクト- 第一級のオブジェクトというやつ
- 変数に代入できる
- 関数の引数として関数を渡せる
// 変数に代入できる
var fun = function (msg) {
console.log(`${fun.bar} ${msg}`);
};
// プロパティを持てる
fun.bar = 'foo';
fun('bar'); // 'foo bar'
// 関数の引数として関数を渡せる
var say = function (callback) {
console.log(`SAY ${callback()} !!!`);
};
var ho = function () {
return 'HO';
};
say(ho); // SAY HO !!!
より一般的な例。
[1, 2, 3].forEach(function (i) {
console.log(i * 2);
});
関数の定義
関数宣言と関数式がある。
関数宣言
- 関数を定義し、関数名と同じ名前の変数に代入する
- 変数の宣言と違い、関数本体も巻き上げられる
function add (x, y) {
return x + y;
}
関数式
- 関数を定義して返す
- 変数に代入したり、コールバック関数として関数の引数に渡して使う
var add = function (x, y) {
return x + y;
};
関数宣言と関数式の使い分け
これらは、定義した関数を呼べるようになるタイミングが異なる。
関数宣言: 宣言の位置より上で関数を呼べる
foo(); // 'Hi' function foo () { console.log('Hi'); }
関数式: 変数へ代入するまで呼べない
foo(); // ReferenceError: foo is not defined var foo = function () {};
引数の受け取り
- かっこの中に書く
- 値を返すときはreturn必須
function add (v1, v2) {
return v1 + v2;
}
this
キーワード
this
という、暗黙的に渡される引数のようなものがある- 普通はレシーバーが渡される
- レシーバー:
foo.bar()
のfoo
のこと
- レシーバー:
var a = {
foo : function () {
console.log(this === a);
} ,
};
a.foo(); // true (this は a を指す)
a.foo.call({}); // false (this は {} を指す)
ハマりどころ
this
の値は呼び出し時に決定する- オブジェクトのプロパティに持たせた関数を別の関数のコールバックに設定したら、
this
が期待する値にならなくてハマる
var obj = {
name: "Hatena",
sayMyName: function () {
alert(this.name);
},
};
// "Hatena" が alert される
obj.sayMyName();
// `function() { alert(this.name) };` だけを渡していることになり、
// "Hatena" でない文字列が alert される
setTimeout(obj.sayMyName, 100);
// この様に `call` で `name`プロパティを含む `this`を指定してあげると、"Hatena-kyoto"という文字列が alert される
setTimeout(function () {
obj.sayMyName.call({name: "Hatena-kyoto"});
} , 1000);
// applyでも良い
setTimeout(function () {
obj.sayMyName.apply({name: "Hatena-kyoto"});
}, 1000);
this
を書き換える方法
- apply / call
- 関数の
this
を書き換えて呼び出す
- 関数の
- bind
- 関数の
this
を書き換えた関数を返す
- 関数の
var nodes = document.querySelectorAll('div'); // nodes[0] とかあるのに、sliceが使えない!
// apply / call をつかう
[].slice.call(nodes, 0, 3); // [] (Array クラスのインスタンス) になりすます
[].slice.apply(nodes, [0, 3]); // 上と同じ結果
// bind をつかう
var sliceNodes = [].slice.bind(nodes);
sliceNodes(0, 3);
Arrow Function
- ES2015で導入
- メリット
- 書きやすい
- 定義したスコープの
this
が渡される
書きやすい
// 基本形
var add = (x, y) => {
return x + y;
};
// {} を省略
var add = (x, y) => x + y;
// 引数が1個なら括弧を省略できる
var square = x => x * x;
[1, 2, 3].map(x => x * x); // [1, 4, 9]
定義したスコープの this
が渡される
前項で述べた this
の扱いが簡単になります。
var obj = {
name : 'foo',
oldFunc : function () {
setTimeout(function () {
console.log(this.name); // this === window
}, 1000);
},
newFunc : function () {
setTimeout(() => {
console.log(this.name); // this === (newFunc の this)
}, 1000);
} ,
};
obj.oldFunc(); // undefined
obj.newFunc(); // 'foo' (newFunc の this は obj)
var f = obj.newFunc;
f(); // undefined
- 以降の説明では、なるべくArrow Functionを使います
- 課題でも使ってOK
- 使える環境ではガンガン使っていこう