関数

関数は値

  • 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
    • 使える環境ではガンガン使っていこう

results matching ""

    No results matching ""