【JavaScript】 関数(function)とメソッドの違い
更新日:2021/02/05
プログラム言語の関数とは一連の処理をひとまとめにして、簡単な記述で何度でも呼び出せるようにしたもの。
JavaScriptにもありますね。
しかし似たような機能でメソッドというものがあり、こちらも前述のことができます。
では関数とメソッドは何が違うのでしょうか。
JavaScriptのメソッドとは
結論から言うと、メソッドは関数と結びつけられたオブジェクトのプロパティです。
下の例は、同じプロパティに様々な値をセットしています。
obj.prop = 1;
obj.prop = "文字";
obj.prop = { data:123,text:"aaa" };
obj.prop = function(){ };
このとき、一番下のプロパティのみメソッドと呼びます。
多くのオブジェクト指向言語ではプロパティとメソッドは別の意味を持っています。
しかしJavaScriptはプロパティにセットされているのが、関数かどうかという違いでしかありません。
関数とメソッドの関係
JavaScriptで関数は次のように定義します。
function 関数名(){ …コード }
JavaScriptがスクリプトコードを読み込み、関数のコードを見つけると、それを関数オブジェクトに変換します。
関数オブジェクトは、関数内のコードを実行するために必要な仕組みを持っているオブジェクトで、いわば関数そのものです。
関数オブジェクトは、関数名の後に()を付けることで実行することができます。
関数名();
変数に代入した場合、変数名の後に()を付けることで実行することができます。
const hensu = 関数名;
hensu();
プロパティに代入した場合も、同じです。
obj.method = 関数名; obj.method();
同じ関数オブジェクトを代入した場合、全て同じオブジェクトを参照します。
つまり、関数名でも変数名でもプロパティ名でも、全て同じ関数に結びつけられています。
function 関数名(){ };は関数定義、変数名 = function(){}は関数式と呼び、機能が少し異なります。詳しくは次のページを見てください。
■【JavaScript】 変数・関数の巻き上げってなんだ?
関数とメソッドの違い
関数とメソッドは同じ関数オブジェクトを参照していますが、一部の機能に違いがあります。
メソッドは親オブジェクトを受け取る
メソッドは親となるオブジェクトをthis値という変数で受け取る機能を持っています。
関数にはないので、これが関数とメソッドの違いです。
親となるオブジェクトとは、自分自身を含めたオブジェクトです。
次の例のオブジェクトの場合、methodメソッドから見た親オブジェクトは、{ prop1:1,prop2:"abc",method:func(){ } } になります。
const obj = {
prop1:1,
prop2:"abc",
method:function(){ console.log( this ); }
};
メソッドを実行するときは、オブジェクト変数の後に"."(ドット)でメソッドをつなげるか[]で表記し、最後に()を記述します。
obj.method(); // または、obj["method"]()
上のコードを実行すると、まずobjが参照する{ prop1:1,prop2:"abc",method:func(){ } }が取得されます。
そして取得したオブジェクトからmethodというプロパティが検索され、実行されます。
関数には自動で作成されたthisという変数が渡されます。
このthisには、objが指すオブジェクトがセットされています。
const obj={
prop1:1,
prop2:"abc",
method:function(){ console.log( this ); }
};
obj.method(); // this:{
// prop1:1,
// prop2:"abc",
// method:func(){ console.log( this ); }
// }
メソッドはこの機能を利用してコードを組むことが多いです。
そのことから、メソッドはthisに親オブジェクトがセットされていることを前提に作成された関数であるともいえます。
メソッドの関数化によるエラー
メソッドを変数に代入すると関数になり、メソッドとしての機能が失われます。
const kansu = obj.method;
メソッドを関数にしたと言っても、参照している関数オブジェクトは同じものです。
しかし代入先の変数は、プロパティのように"."(ドット)や[]表記で呼び出すことができません。
そのため、thisを受け取ることができないのです。
次のコードは、やってしまいがちな間違いコードです。
やってしまいがちな間違いコード
const obj={
prop1:1,
prop2:"abc",
method:function(){
console.log( this.prop1 );
}
};
const objMethod = obj.method;
console.log( objMethod() ); // thisはグローバルオブジェクトまたはundefinedのため、this.prop1は参照できない
obj.methodを変数objMethodに代入しています。
代入しただけなので、そのまま実行できそうな気がします。
しかしobjMethodを実行すると、this値はグローバルオブジェクトまたはundefinedが渡されます。
渡されたthis値にpropプロパティがないため、参照するとエラーになります。
関数のthis値は、グローバルオブジェクトになります。
※strictモードの場合はundefined
また意図的にthis値を与える方法もあります。
アロー関数はthisを受け取らない
オブジェクトのプロパティにアロー関数をセットした場合でも、メソッドと呼ぶことができます。
しかしアロー関数は関数内部で変数thisを定義することができません。
そのため、メソッドでもthisで親オブジェクトを取り扱うことができません。
次のコードは、期待した動作をしません。
const obj={
prop1:1,
prop2:"abc",
method: () => {
console.log( this.prop1 );
}
};
obj.method();
結果はどうなるのでしょうか?
実は、このコードよりも前の内容で変わってきます。
次のコードを例に解説します。
const objA = {
prop1: 100, // (A)
method: function(){
const obj={
prop1:1, // (B)
prop2:"abc",
method: () => {
// アロー関数はthisを定義できない
console.log( this.prop1 ); // このprop1は、(A)
}
};
obj.method();
}
};
objA.method(); // 100
objA.methodは、通常の関数を持つメソッドです。
実行すると、this値としてobjAの指し示すオブジェクトを受け取ります。
objA.methodの内部コードで定義されている、obj.methodはアロー関数をメソッドのため、実行してもthisが定義されません。
しかしthis値は変数なので、他の変数と同様に外部を検索します。
その結果objA.methodのthisが参照され、obj.method内部のthis.prop1は100という値を取得します。
つまり、外部のthis値によって、結果が決まるのです。
thisを保持するメソッド
ここまで、オブジェクトのメソッドを変数として抜き出し関数化すると、this値が失われると解説してきました。
const obj = {
prop1:1,
method:function(){ console.log( this.prop1 ); }
};
obj.method(); // 1
const method = obj.method;
method(); // undefined : this値が失われた
しかしいくつかの手法を用いることで、this値を保持することができます。
方法1:クロージャの特徴を利用
一つ目は、クロージャの特徴を利用してオブジェクトをメソッドのprivate変数として所持する方法です。
const obj = (()=>{
const thisObj = {
prop1:1,
method:function(){ console.log( thisObj.prop1 ); }
};
return thisObj;
})();
obj.method(); // 1
const method = obj.method;
method(); // 1
関数化後のmethodを実行すると、this値はグローバルオブジェクトまたはundefinedといった、以前と同じ値を受け取ります。
しかし、関数内で参照しているのはthisではないため、エラーになりません。
方法2:bindメソッドを利用
方法1は厳密にはthisを保持していません。
メソッドコード内でthisを使用したいなら、後からbindメソッドでthis値を固定します。
const obj = {
prop1:1,
method : function(){ console.log( this.prop1 ); }
};
obj.method = obj.method.bind( obj ); // bindメソッドでthis値を固定
obj.method(); // 1
const method = obj.method;
method(); // 1
関数化後のmethodを実行すると、this値はbindメソッドで指定した値が渡されます。
更新日:2021/02/05
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。