From 3435c6bca342ea29b3c31dde4fa2f87a32c21ab7 Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Sat, 18 Sep 2021 11:00:30 +0900 Subject: Markdownへの変換準備 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../javascript/reference/operators/this/index.md | 486 +++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 files/ja/web/javascript/reference/operators/this/index.md (limited to 'files/ja/web/javascript/reference/operators/this/index.md') diff --git a/files/ja/web/javascript/reference/operators/this/index.md b/files/ja/web/javascript/reference/operators/this/index.md new file mode 100644 index 0000000000..eb3ac7425d --- /dev/null +++ b/files/ja/web/javascript/reference/operators/this/index.md @@ -0,0 +1,486 @@ +--- +title: this +slug: Web/JavaScript/Reference/Operators/this +tags: + - JavaScript + - Language feature + - Operator + - Primary Expressions + - Reference + - this + - 演算子 + - 言語機能 +translation_of: Web/JavaScript/Reference/Operators/this +--- +
{{jsSidebar("Operators")}}
+ +

関数の this キーワード は、JavaScript ではほかの言語と少々異なる動作をします。また、厳格モードと非厳格モードでも違いがあります。

+ +

ほとんどの場合、this の値はどのように関数が呼ばれたかによって決定されます(実行時結合)。これは実行時に割り当てできず、関数が呼び出されるたびに異なる可能性があります。ES5 では、関数が{{jsxref('Operators/this', "どのように呼ばれたかに関係なく this の値を設定する", 'The_bind_method', 1)}} {{jsxref("Function.prototype.bind()", "bind()")}} メソッドが導入され、ES2015 では、独自の this バインディングを行わないアロー関数が導入されました(これは包含するレキシカルコンテキストの this の値を保持します)。

+ +
{{EmbedInteractiveExample("pages/js/expressions-this.html")}}
+ + + +

構文

+ +
this
+ +

+ +

非厳格モードでは、実行コンテキスト (グローバル、関数、eval) のプロパティで、常にオブジェクトへの参照です。厳格モードではどのような値でも取り得ます。

+ +

解説

+ +

グローバルコンテキスト

+ +

グローバル実行コンテキスト (すべての関数の外側) では、厳格モードであるかどうかにかかわらず、this はグローバルオブジェクトを参照します。

+ +
// ウェブブラウザーでは window オブジェクトもグローバルオブジェクトです。
+console.log(this === window); // true
+
+a = 37;
+console.log(window.a); // 37
+
+this.b = "MDN";
+console.log(window.b)  // "MDN"
+console.log(b)         // "MDN"
+
+ +
+

メモ: コードが実行されている現在のコンテキストに関係なく、グローバルの {{jsxref("globalThis")}} プロパティを使用していつでも簡単にグローバルオブジェクトを取得できます。

+
+ +

関数コンテキスト

+ +

関数内での this の値は、関数の呼び出され方によって異なります。

+ +

下記のコードは厳格モードではないため、また呼び出し時に this の値が設定されないため、this は既定でグローバルオブジェクトとなり、これはブラウザーでは {{domxref("Window", "window")}} です。

+ +
function f1() {
+  return this;
+}
+
+// ブラウザー上で
+f1() === window; // true
+
+// Node 上で
+f1() === global; // true
+ +

ただし厳格モードでは、実行コンテキストに入るときに this 値が設定されていないと、以下の例のように undefined のままになります。

+ +
function f2() {
+  'use strict'; // 厳格モードにする
+  return this;
+}
+
+f2() === undefined; // true
+
+ +
二番目の例において、this が {{jsxref("undefined")}} となるのは f2 が直接呼び出されており、オブジェクトのメソッドやプロパティ (例えば window.f2()) ではないためです。この機能は初めて厳格モードへの対応が始まったとき、一部のブラウザーが実装していませんでした。その結果、これらのブラウザーは不正確に window オブジェクトを返していました。
+ +

関数の呼び出し時に this の値を特定の値に設定するには、以下の例のように {{jsxref("Function.prototype.call()", "call()")}} または {{jsxref("Function.prototype.apply()", "apply()")}} を使用します。

+ +

クラスコンテキスト

+ +

クラスは関数の機能であるため、クラスと関数の this の動作は似ています。ただし、いくつかの違いと注意点があります。

+ +

クラスのコンストラクター内では、this は通常のオブジェクトです。クラス内のすべての非静的メソッドは this のプロトタイプに追加されます。

+ +
class Example {
+  constructor() {
+    const proto = Object.getPrototypeOf(this);
+    console.log(Object.getOwnPropertyNames(proto));
+  }
+  first(){}
+  second(){}
+  static third(){}
+}
+
+new Example(); // ['constructor', 'first', 'second']
+
+ +
+

メモ: 静的メソッドは this のプロパティではありません。それらはクラス自体のプロパティです。

+
+ +

派生クラス

+ +

基本クラスのコンストラクターとは異なり、派生コンストラクターには初期の this バインディングがありません。{{jsxref("Operators/super", "super()")}} を呼び出すとコンストラクター内に this バインディングが作成され、基本的に以下のコードを評価する効果があります。ここで、Base は継承されたクラスです。

+ +
this = new Base();
+ +
+

警告: super() を呼び出す前に this を参照するとエラーが発生します。

+
+ +

派生クラスは、オブジェクトを return するか、コンストラクターを持たない場合を除き、super() を呼び出す前に return することはできません。

+ +
class Base {}
+class Good extends Base {}
+class AlsoGood extends Base {
+  constructor() {
+    return {a: 5};
+  }
+}
+class Bad extends Base {
+  constructor() {}
+}
+
+new Good();
+new AlsoGood();
+new Bad(); // 参照エラー
+ +

+ +

関数コンテキスト内の this

+ +
// オブジェクトを call や apply の最初の引数として渡すと、this がそれに結び付けられます
+var obj = {a: 'Custom'};
+
+// このプロパティはグローバルオブジェクトに設定されます
+var a = 'Global';
+
+function whatsThis() {
+  return this.a;  // this の値は関数の呼び出し方によって変わります
+function is called
+}
+
+whatsThis();          // 関数内の this として 'Global' は設定されていないので、デフォルトではグローバル/ウィンドウオブジェクトになります。
+whatsThis.call(obj);  // 関数内の this として 'Custom' が obj に設定されています
+whatsThis.apply(obj); // 関数内の this として 'Custom' が obj に設定されています
+
+ +

this とオブジェクト変換

+ +
function add(c, d) {
+  return this.a + this.b + c + d;
+}
+
+var o = {a: 1, b: 3};
+
+// 最初の引数は 'this' として使用する
+// オブジェクトで、続く引数は関数呼び出しの
+// 引数として使用されます。
+add.call(o, 5, 7); // 16
+
+// 最初の引数は 'this' として使用する
+// オブジェクトで、二番目の引数は関数呼び出しの
+// 引数として使用される配列です。
+add.apply(o, [10, 20]); // 34
+
+ +

なお、非厳格モードにおいて、callapply は、this として渡された値がオブジェクトではない場合、内部の ToObject 操作を利用してオブジェクトに変換しようします。7'foo' のようなプリミティブが渡された場合、関連するコンストラクターを使用してオブジェクトに変換されます。たとえば、プリミティブの数値である 7new Number(7) であるかのようにオブジェクトに変換され、文字列の 'foo'new String('foo') であるかのようにオブジェクトに変換されます。

+ +
function bar() {
+  console.log(Object.prototype.toString.call(this));
+}
+
+bar.call(7);     // [object Number]
+bar.call('foo'); // [object String]
+bar.call(undefined); // [object global]
+
+ +

bind メソッド

+ +

ECMAScript 5 で {{jsxref("Function.prototype.bind")}} が導入されました。f.bind(someObject) の呼び出しは、f と同じ内部とスコープを持つ新しい関数を生成し、ここが this が発生するオリジナルの関数ですが、関数がどのように使われるかにかかわらず、新しい関数では bind の最初の引数に永続的にバインドされます。

+ +
function f() {
+  return this.a;
+}
+
+var g = f.bind({a: 'azerty'});
+console.log(g()); // azerty
+
+var h = g.bind({a: 'yoo'}); // bind は一度しか機能しない
+console.log(h()); // azerty
+
+var o = {a: 37, f: f, g: g, h: h};
+console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
+
+ +

アロー関数

+ +

アロー関数では、this はそれを囲むレキシカルコンテキストの this の値が設定されます。グローバルコードでは、グローバルオブジェクトが設定されます。

+ +
var globalObject = this;
+var foo = (() => this);
+console.log(foo() === globalObject); // true
+ +
+

メモ: アロー関数の呼び出し時に this 引数が call, bind, apply に渡されても無視されます。呼び出しに引数を加えることはできますが、最初の引数 (thisArg) は null を設定してください

+
+ +
// オブジェクトのメソッドとして呼び出す。
+var obj = {func: foo};
+console.log(obj.func() === globalObject); // true
+
+// call を使用して this の設定を試みる
+console.log(foo.call(obj) === globalObject); // true
+
+// bind を使用して this の設定を試みる
+foo = foo.bind(obj);
+console.log(foo() === globalObject); // true
+ +

何があっても、foothis は生成されたときの値が設定されています (上記の例ではグローバルオブジェクトです)。同様のことが、ほかの関数内で生成したアロー関数にも適用されます。それらの this には、それを包含するレキシカルコンテキストのものになります。

+ +
// this を返す関数を返す bar メソッドを持つ
+// obj を生成します。返された関数はアロー関数
+// として生成されているため、その this は
+// それを包含する関数の this に永続的に拘束
+// されます。bar の値は呼び出し時に設定でき、
+// 返値の関数の値に順に設定します。
+var obj = {
+  bar: function() {
+    var x = (() => this);
+    return x;
+  }
+};
+
+// bar を obj のメソッドとして呼び出す際、その this を obj に設定します
+// 返値の関数への参照を fn に割り当てます。
+var fn = obj.bar();
+
+// 厳格モードでは、this を設定せずに fn を呼び出すと
+// 通常はグローバルオブジェクトか undefined が既定値となります。
+console.log(fn() === obj); // true
+
+// しかし obj のメソッドを call することなく参照するのは要注意です。
+var fn2 = obj.bar;
+// するとアロー関数の呼び出しで this は bar の
+// this に従うため window と同じになります。
+console.log(fn2()() == window); // true
+
+ +

上記では、関数 (この無名関数を A と呼びます) に obj.bar が返すアロー関数として生成されたほかの関数 (この無名関数を B と呼びます) を割り当てています。結果として、呼び出されたときに関数 B の this は、永続的に obj.bar (関数 A) の this が設定されます。返された関数 (関数 B) が呼びされるとき、その this は常に最初に設定されたものになります。上記のコード例では、関数 B の thisobj である関数 A の this が設定されているため、通常はその thisundefined かグローバルオブジェクト (または、以前の例のグローバルコンテキストのように、いずれかのメソッド) が設定されますが、obj の設定が残ります。

+ +

オブジェクトのメソッドとして

+ +

関数がオブジェクトのメソッドとして呼び出されるとき、その this にはメソッドが呼び出されたオブジェクトが設定されます。

+ +

次の例では、o.f() が起動したとき、関数内の this には、o オブジェクトが関連付けられます。

+ +
var o = {
+  prop: 37,
+  f: function() {
+    return this.prop;
+  }
+};
+
+console.log(o.f()); // 37
+
+ +

この振る舞いは、関数定義の方法や場所に全く影響を受けないことに注意してください。前述の例では、o の定義中に f メンバーとして関数をインラインに定義しています。しかし、関数を最初に定義して、後から o.f に付け足すことができます。その結果は同じ振る舞いになります。

+ +
var o = {prop: 37};
+
+function independent() {
+  return this.prop;
+}
+
+o.f = independent;
+
+console.log(o.f()); // 37
+
+ +

これは、関数が of のメンバーとして呼び出されることだけが重要なことを示しています。

+ +

同様に、this の関連付けは、最も直近のメンバー参照にのみ影響を受けます。次の例では、関数が呼び出すとき、オブジェクト o.bg メソッドとして呼び出しています。実行時に、関数内の thiso.b を参照します。オブジェクト自体が o のメンバーであるという事実は何の意味もありません。最も直近の参照のみが重要なのです。

+ +
o.b = {g: independent, prop: 42};
+console.log(o.b.g()); // 42
+
+ +

オブジェクトのプロトタイプチェーン上の this

+ +

同じ概念が、オブジェクトのプロトタイプチェーンのどこかに定義されたメソッドにも当てはまります。メソッドがオブジェクトのプロトタイプチェーン上にあった場合、メソッドがオブジェクト上にあるかのように、this はメソッドを呼び出したオブジェクトを参照します。

+ +
var o = {f: function() { return this.a + this.b; }};
+var p = Object.create(o);
+p.a = 1;
+p.b = 4;
+
+console.log(p.f()); // 5
+
+ +

この例では、変数 p に割り当てられたオブジェクト自身は f プロパティを持たず、プロトタイプから継承しています。しかし、f に対する検索が、最終的に o でその名前を持つメンバーを見つけることは重要ではありません。検索は p.f への参照から開始されるため、関数内の thisp として参照されるオブジェクトの値を取ります。fp のメソッドとして呼ばれたため、その thisp を参照します。これは、JavaScript のプロトタイプ継承の興味深い機能です。

+ +

ゲッター/セッターと this

+ +

再度、同じ概念が、ゲッターやセッターから呼ばれる関数にも当てはまります。ゲッターやセッターとして使用される関数は、このプロパティを設定するか、または得られている元のオブジェクトに関連付けられている this を持ちます。

+ +
function sum() {
+  return this.a + this.b + this.c;
+}
+
+var o = {
+  a: 1,
+  b: 2,
+  c: 3,
+  get average() {
+    return (this.a + this.b + this.c) / 3;
+  }
+};
+
+Object.defineProperty(o, 'sum', {
+    get: sum, enumerable: true, configurable: true});
+
+console.log(o.average, o.sum); // 2, 6
+
+ +

コンストラクターとして

+ +

関数がコンストラクターとして ({{jsxref("Operators/new", "new")}} キーワードとともに) 使用されたとき、その this は生成された新しいオブジェクトに関連付けられます。

+ +
+

コンストラクターの既定では、this で参照されるオブジェクトを返しますが、代わりにほかのオブジェクトを返すことができます (返値がオブジェクトではない場合、this オブジェクトが返されます)。

+
+ +
/*
+ * Constructors work like this:
+ *
+ * function MyConstructor(){
+ *   // Actual function body code goes here.
+ *   // Create properties on |this| as
+ *   // desired by assigning to them.  E.g.,
+ *   this.fum = "nom";
+ *   // et cetera...
+ *
+ *   // If the function has a return statement that
+ *   // returns an object, that object will be the
+ *   // result of the |new| expression.  Otherwise,
+ *   // the result of the expression is the object
+ *   // currently bound to |this|
+ *   // (i.e., the common case most usually seen).
+ * }
+ */
+
+function C() {
+  this.a = 37;
+}
+
+var o = new C();
+console.log(o.a); // 37
+
+
+function C2() {
+  this.a = 37;
+  return {a: 38};
+}
+
+o = new C2();
+console.log(o.a); // 38
+
+ +

最後の例 (C2) では、構築中にオブジェクトを返しているので、this が結び付けられている新しいオブジェクトは単に破棄されています。(これは根本的に "this.a = 37;" ステートメントを死んだコードにしてしまっています。これは実行されるので、正確には死んだコードではありませんが、外部への影響がありません。)

+ +

DOM イベントハンドラーとして

+ +

関数がイベントハンドラとして使用された場合、その this はリスナーが配置されている要素に設定されます ({{domxref("EventTarget/addEventListener", "addEventListener()")}} 以外のメソッドで動的に追加されたリスナーについては、この規約に従わないブラウザー-もあります)。

+ +
// リスナーとして呼び出された場合は、関連づけられた要素を青にする
+function bluify(e) {
+  // 常に true
+  console.log(this === e.currentTarget);
+  // currentTarget と target が同じオブジェクトであれば true
+  console.log(this === e.target);
+  this.style.backgroundColor = '#A5D9F3';
+}
+
+// 文書内の各要素の一覧を取得
+var elements = document.getElementsByTagName('*');
+
+// クリックリスナーとして bluify を追加することで、
+// 要素をクリックすると青くなるようになる
+for(var i = 0 ; i < elements.length; i++){
+  elements[i].addEventListener('click', bluify, false);
+}
+ +

インラインイベントハンドラー内

+ +

コードがインラインの on-イベントハンドラーから呼び出されたとき、その this にはリスナーが配置されている DOM 要素が設定されます。

+ +
<button onclick="alert(this.tagName.toLowerCase());">
+  Show this
+</button>
+
+ +

上記のアラートは button と表示します。ただし、外側のコードがこのように設定された this を持っているだけだということに注意してください。

+ +
<button onclick="alert((function() { return this; })());">
+  Show inner this
+</button>
+
+ +

この場合、内側の関数の this は設定されていないので、グローバルの window オブジェクトを返します (つまり、this が呼び出しによって設定されていないので、非厳格モードの既定オブジェクトです)。

+ +

クラスの中の this

+ +
+ +

通常の関数と同様に、メソッド内の this の値は、どのように呼び出されるかによって異なります。クラス内の this が常にクラスのインスタンスを参照するように、この動作をオーバーライドしておくと便利な場合もあります。これを実現するには、コンストラクターでクラスのメソッドをバインドします。

+ +
class Car {
+  constructor() {
+    // 違いを示すために sayHi ではなく sayBye をバインドする
+    this.sayBye = this.sayBye.bind(this);
+  }
+  sayHi() {
+    console.log(`Hello from ${this.name}`);
+  }
+  sayBye() {
+    console.log(`Bye from ${this.name}`);
+  }
+  get name() {
+    return 'Ferrari';
+  }
+}
+
+class Bird {
+  get name() {
+    return 'Tweety';
+  }
+}
+
+const car = new Car();
+const bird = new Bird();
+
+// メソッドの 'this' の値は呼び出し元に依存します
+car.sayHi(); // Hello from Ferrari
+bird.sayHi = car.sayHi;
+bird.sayHi(); // Hello from Tweety
+
+// バインドされたメソッドの場合、'this' は呼び出し元に依存しません
+bird.sayBye = car.sayBye;
+bird.sayBye();  // Bye from Ferrari
+ +
+

メモ: クラスは常に厳格モードのコードです。これを定義せずに this でメソッドを呼び出すとエラーが発生します。

+
+ +

仕様

+ + + + + + + + + + + + +
仕様書
{{SpecName('ESDraft', '#sec-this-keyword', 'The this keyword')}}
+ +

ブラウザーの互換性

+ +

{{Compat("javascript.operators.this")}}

+ +

関連情報

+ + -- cgit v1.2.3-54-g00ecf