見出し画像

Function#callを理解する

続いてFunction#call。これもいまだに使ったことない。

call() はあるオブジェクトに所属する関数やメソッドを、別なオブジェクトに割り当てて呼び出すことができます。

ええと、少しわかりにくいんだけど、「関数を別なオブジェクトに割り当てて呼ぶことができる」がポイント。

コードで表現。

function greet() {
 return `Hello, ${this.name}!`;
}

const john = {
 name: 'John',
};

console.log(greet.call(john)); // 'Hello, John!'

関数greetが仮引数を持っている場合は、callの第2引数以降にその実引数を渡してあげる。

function greet(msg) {
 return `${msg}, ${this.name}!`;
}

const john = {
 name: 'John',
};

console.log(greet.call(john, 'Hello')); // 'Hello, John!'
console.log(greet.call(john, 'Bonjour')); // 'Bonjour, John!'

これは、こう書いているのと同じ。

const john = {
 name: 'John',
 
 greet(msg) {
   return `${msg}, ${this.name}!`;
 }
};

console.log(john.greet('Hello')); // 'Hello, John!'
④:検証
function greet(msg) {
 return `${msg}, ${this.name}!`;
}

const john1 = {
 name: 'John',
};

const john2 = {
 name: 'John',
 
 greet(msg) {
   return `${msg}, ${this.name}!`;
 }
};

const result1 = greet.call(john1, 'Hello');
const result2 = john2.greet('Hello');

console.log(result1 === result2); // true

thisとレシーバ

関数greetには返り値にthisが含まれていました。

⑤:①の部分再掲
function greet() {
 return 'Hello, ' + this.name + '!';
}

thisの参照先は呼び出し場所によって決まります。グローバルで呼び出すと、グローバルオブジェクトを参照します。グローバルにnameプロパティはセットされていないので、greetのthis.nameはundefinedを返します。

function greet() {
 return 'Hello, ' + this.name + '!';
}

greet(); // 'Hello, undefined!'

greetが何らかのオブジェクトに対して、つまり{object}.greet()の形で呼ばれると、greet内のthisはそのオブジェクトを参照します。

function greet() {
 return `Hello, ${this.name}!`;
}

const john = {
 name: 'John',
};

john.greet(); // ?

ただし、この状態だと、johnがそもそもgreetメソッドを持っていないためgreet自体が呼び出せません。そこで①のようにcallを使ったコードが生まれるというわけです。

⑧:① 再掲
function greet() {
 return `Hello, ${this.name}!`;
}

const john = {
 name: 'John',
};

console.log(greet.call(john)); // 'Hello, John!'

callによってgreetのthisがjohnに設定されているので、this.nameが'John'を参照できているわけです。

※少なくとも自分の観測上、実際のコードではそのような書き方はまずしません。オブジェクトのメンバーとして書いてしまうのが明らかにわかりやすいからです。ここではあくまでcallの振る舞いを記述するための例としてこれを挙げています。

用語:レシーバ、束縛バインディング

john.greet();

のようにドット記法でメソッドの左側にあるオブジェクトのことを、そのメソッドのレシーバというようです。

また、thisの参照先をcallによって任意に変更するとき、thisを○○に束縛バインド すると表現するようです(bind, 束縛する、結びつける)。より一般的に、変数がメモリ上のどのアドレスを参照するのか、その参照先を結び付けて決める、ということのようですね。

よし、callはここで終わりにして、callerやcallee、applyにいこうかな。

この記事が気に入ったらサポートをしてみませんか?