かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JS call apply vs bind アロー関数

javascriptの関数とthisとの関係のメモの続きです。
関数のthisを決めることができるcall()apply()で実行される関数がFunction.prototype.bindやアロー関数でthisが決められているものだった場合どうなるのか気になったので調べてみました。

call と apply

call()apply()this を指定して関数を実行するメソッド。
違いは関数に渡す引数の指定方法だけ。

call() は 関数に渡す引数を1つ1つ列挙していく

myFnc.call(this, param1, param2, param3);

apply() は 関数に渡す引数を第二引数に配列で渡す。実行される関数には引数は展開されて渡される。(argumentsでも参照できる)

myFnc.apply(this, [param1, param2, param3]);

bind (ES5)

Function.prototype.bind()新たな関数(a bound function)を生成して返すメソッド。

var fnc = function() { console.log(this); };
var bindFnc = fnc.bind(null);
console.log(bindFnc); // => [Function: bound fnc]

bind() 関数は、新たな関数(束縛された関数 = a bound function)を生成して返します。この新たな関数の本体(ECMAScript 5 の観点では内部の call プロパティ)は、call() 先の関数(束縛された関数のターゲット関数)【訳注: fun.bind(thisArg) の fun 】 と同じです。ターゲット関数の this の値が bind() に与えた第 1 引数になり(束縛され)、それを上書きすることはできません。
[出典] Function.prototype.bind() - JavaScript | MDN

bindで生成された関数は実行時に内部的にcallが呼ばれるという事だと考えられるので、この関数をcall()apply()で呼出しても、実行される関数内でthisbind()で指定したものに置き換えられるので、bind()で指定したものが実行される関数内のthisになる。

'use strict';
var global = Function("return this")(); // global オブジェクトを取得
global.name = 'global';
var obj1 = { name: "obj1" };
var obj2 = { name: "obj2" };

var callback = function() {
  console.log('> callback this is ', this.name);
};

var applyFnc = function(f) {
  if(typeof(f) === 'function') {
    let _this = this || global;
    console.log('this is', _this.name);
    f.apply(_this);
  }
};

applyFnc( callback );
// => this is global
// => > callback this is  global

// Function.prototype.bind()
applyFnc( callback.bind(obj1) );
// => this is global
// => > callback this is  obj1

applyFnc.call( obj2, callback.bind(obj1) );
// => this is obj2
// => > callback this is  obj1

アロー関数 ()=> (ES6)

アロー関数式 は、function 式 と比べてより短い構文を持ち、this の値を語彙的に束縛します (ただし、自身の this や arguments, super, new.target は束縛しません)。アロー関数は、常に 匿名関数 です。
call や apply からの呼び出し
this はすでに語彙的に束縛されているため、call() や apply() メソッドを介してアロー関数が呼ばれた場合、引数で渡されるだけなので this は影響を受けません
[出典] アロー関数 - JavaScript | MDN

アロー関数は記述が出てきた場所のスコープにあるthisを関数のthisに決定するというイメージです。
引用元にも書かれているようにcall()apply()thisの値を渡されようと、アロー関数作成時に決定したthisが実行される関数のthisになります。

'use strict';
var global = Function("return this")();
global.name = 'global';
var obj1 = { name: "obj1" };
var obj2 = { name: "obj2" };

var applyFnc = function(f) {
  if(typeof(f) === 'function') {
    let _this = this || global;
    console.log('this is', _this.name);
    f.apply(_this);
  }
};

obj1.fnc1 = function() {
  applyFnc(() => {
    console.log('> callback this is ', this.name);
  });
};
obj1.fnc1();
// => this is global
// => > callback this is  obj1


obj1.fnc2 = function() {
  applyFnc.call(obj2, () => {
    console.log('> callback this is ', this.name);
  });
};
obj1.fnc2();
// => this is obj2
// => > callback this is  obj1

 

概ね予測通りの結果でした。
.bind()が新しい関数を生成するというのは知らなかったので、この機会に知ることができて良かったです。同時にデフォルト引数を設定できることも初めて知り寧ろこちらの方に興味惹かれるものがありましたw
こんな事をしているからなかなか本が読み進められないわけです...


[参考]

オブジェクト指向JavaScriptの原則

オブジェクト指向JavaScriptの原則