2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのthisについて調べてみた

Last updated at Posted at 2024-10-26

■はじめに

こんにちは。白水(しらみず)と申します。
普段は不動産スタートアップでフロントエンドエンジニアとしてお仕事しています。
今回は「JavaScriptのthis」について記事にしました。
良ければご覧ください。

Shiramizu_Junya lit.link(リットリンク)

■オブジェクトのメソッド呼び出しでのthisの動作

メソッドは、オブジェクトのプロパティとして関数が定義されている場合、それをメソッドと呼びます。
メソッド内のthisは、呼び出し元のオブジェクトを指します。
thisは "ドット以前 "のオブジェクトであり、メソッドを呼び出すのに使われるオブジェクトを表します。

◆メソッドの定義方法について

const bird = {
	name: 'Crow',
	color: 'black'
}

// 関数式を使って関数を作成し、オブジェクトのプロパティ に割り当てています。
// オブジェクトのプロパティとなっている関数を、*メソッド* と呼ばれます。
bird.fly = function() {
	alert('空を飛ぶ');
}

bird.fly();

まず、関数式を使って関数を作成し、オブジェクトのプロパティ に割り当てています。

オブジェクトのプロパティとなっている関数を、メソッド と呼ばれます。

const bird = {
	name: 'Crow',
	color: 'black'
}

// 関数宣言
function fly() {
	alert('空を飛ぶ');
};

bird.fly = fly;

bird.fly();

メソッドは、関数宣言をオブジェクトのプロパティとしてセットする事もできます。

const bird = {
	name: 'Crow',
	color: 'black',
	fly: function() {
		alert('空を飛ぶ');
	}
}

const bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert('空を飛ぶ');
	}
}

メソッドは、: functionという部分をなくして、省略して書くこともできます。

アロー関数としては、メソッドを定義しない方が良いです。

◆なぜメソッド内でthisが必要なのか?

const bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`Crowが空を飛ぶ`);
	},
};

bird.fly();

オブジェクトのメソッドを実行するときに、オブジェクトのプロパティにアクセスしたいことがあります。

例えば、flyメソッド内で「Crowが空を飛ぶ」というアラートを出すようにしていますが、「Crow」はbirdオブジェクトが持つnameプロパティです。

現在はベタ書きで「Crow」と書いていますが、鳥の名前が変わったときに、nameプロパティとflyメソッド内を書き換えることが必要です。

let bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`${bird.name}が空を飛ぶ`);
	},
};

では、alert(${bird.name}が空を飛ぶ);と書き換えてみます。

これでも問題なく動作します。

let bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`${bird.name}が空を飛ぶ`);
	},
};

let copy = bird;
bird = null;
copy.fly();

しかし、letでbird変数を定義して、birdオブジェクトを変数copyに代入します。

bird変数をnullで上書きして、copy.fly();でcopy側のflyメソッドを呼びます。

変数copyには、birdをnullで上書きする前のオブジェクトが代入されているので、flyメソッドを呼び出すことはできます。

しかし、flyメソッド内での、bird.nameはbirdがnullなのでエラーになります。

thisを使わないと、動的に参照先のオブジェクトを変えることができないため、thisを使います。

◆非strict モードの場合

let bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`${this.name}が空を飛ぶ`);
	},
};

bird.fly();

そのため、動的にオブジェクトにアクセスするためにthisを使います。

this の値はメソッドを呼び出すのに使われた “ドットの前” のオブジェクトです。

この場合のthisは、.fly()の前にあるbirdです。

let bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`${this.name}が空を飛ぶ`);
	},
};

let copy = bird;
bird = null;
copy.fly();

メソッド内でthisを使うことによって、先程エラーになったコードも動作するようになりました。

◆strict モードの場合

'use strict';

let bird = {
	name: 'Crow',
	color: 'black',
	fly() {
		alert(`${this.name}が空を飛ぶ`);
	},
};

bird.fly();

'use strict';を使って厳格モードにしても、thisの向き先は呼び出し元のオブジェクトを指します。

◆thisの値は実行時に決まる = thisはバインドされていない

let bird1 = { name: 'Crow' };
let bird2 = { name: 'Pigeon' };

function fly() {
	alert(`${this.name}は飛びます`);
}

bird1.fly = fly;
bird2.fly = fly;

bird1.fly();
bird2.fly();

thisの値は常に決まっているわけではなく、実行したときに呼び出し方で変わります。

このことを「thisはバインドされていない」や「thisは実行時にコンテキストに応じて評価される」といいます。

bird1とbird2にflyメソッドを定義して、bird1.fly();bird2.fly();で実行してみます。

すると、メソッドを呼び出した呼び出しもとのオブジェクトに応じて実行内容が変わります。

このように、「thisの値は実行時に決まる = thisはバインドされていない」ということがわかります。

let bird1 = { name: 'Crow' };
let bird2 = { name: 'Pigeon' };

function fly() {
	alert(`${this.name}は飛びます`);
}

bird1.fly = fly;

bird1['fly']();

メソッドはプロパティの値とした関数をセットしているだけなので、bird1['fly']();という呼び出し方も可能です。

◆まとめ

JavaScriptでは、 this は 自由に決まります。
関数を宣言したときに、関数の中で this を使うことができますが、 関数内での this は関数が呼び出されるまで何になるかわかりません。

実行時に評価され、メソッドが宣言されている場所には依存せず、 “ドットの前の” オブジェクトが何であるかに依存します。

■thisを単独で呼び出した場合の動作

◆非strict モードの場合

const that = this;
console.log(that) // Window

thisが単独で使われた場合、グローバル・オブジェクトを指す。

thisがグローバル・スコープで実行されているからだ。

ブラウザでは、グローバルオブジェクトはWindowオブジェクトです:

◆strict モードの場合

'use strict';
let that = this;
console.log(that); // Window

厳格モードで、thisを単独で使った場合も、グローバルオブジェクトを参照します。

◆モジュールの場合

<script type="module">
  console.log(this); // undefined
</script>

type="module"にして最上位でthisを参照すると、undefinedになります。

◆まとめ

thisを単独で呼び出した場合は、厳密モードでも非厳密モードでも同じようにグローバルオブジェクトを参照します。

■function関数定義でthisを呼び出した場合の動作

◆非strict モードの場合

function getThis() {
	return this;
}

console.log(getThis()); // Window

関数内では、グローバルオブジェクトがthisのデフォルトバインディングとなります。

ブラウザでは、グローバルオブジェクトはWindowオブジェクトになります。

◆strict モードの場合

'use strict';

function getThis() {
	return this;
}

console.log(getThis()); // undefined

strict モードでは this は undefined になります。

そのため、this.nameのようにプロパティにアクセスすると、エラーになります。

◆まとめ

関数定義内でthisを使う場合は、非strict モードとstrict モードで動作が違うので注意が必要です。

アロー関数では、動作が違うので、関数定義とアロー関数は別で考える必要があります。

(別途記載したアロー関数を参照ください。)

■イベントハンドラ内でthisを呼び出した場合の動作

◆非strict モードの場合

// html
<div>
    <button id="js-btn-1" class="btn1">
        ボタン1
    </button>
    <button id="js-btn-2" class="btn2">
        ボタン2
    </button>
    <button id="js-btn-3" class="btn3">
        ボタン3
    </button>
</div>

// JavaScript
document.querySelector('#js-btn-1').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-1" class="btn1"> ボタン1 </button>
});

document.querySelector('#js-btn-2').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-2" class="btn2"> ボタン2 </button>
});

document.querySelector('#js-btn-3').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-3" class="btn3"> ボタン3 </button>
});

イベント・ハンドラにおけるthisは、イベントが発火したHTML要素を指します。

イベント・ハンドラにおけるthisが、イベントが発火したHTML要素を指すようにするには、関数定義(function関数)を使う必要があります。

アロー関数の場合は、イベントが発火したHTML要素を指しません。

// HTML
<button id="js-btn-1" class="btn1">
    ボタン1
</button>

// JavaScript
document.querySelector('#js-btn-1').addEventListener('click', () => {
	console.log(this); // Window
});

イベントハンドラでアロー関数を使った場合は、このパターンだとグローバルオブジェクトを指します。

◆strict モードの場合

// html
<div>
    <button id="js-btn-1" class="btn1">
        ボタン1
    </button>
    <button id="js-btn-2" class="btn2">
        ボタン2
    </button>
    <button id="js-btn-3" class="btn3">
        ボタン3
    </button>
</div>

// JavaScript
'use strict';

document.querySelector('#js-btn-1').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-1" class="btn1"> ボタン1 </button>
});

document.querySelector('#js-btn-2').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-2" class="btn2"> ボタン2 </button>
});

document.querySelector('#js-btn-3').addEventListener('click', function () {
	console.log(this); // <button id="js-btn-3" class="btn3"> ボタン3 </button>
});

strict モードであっても、イベント・ハンドラにおけるthisは、イベントが発火したHTML要素を指します。

// HTML
<button id="js-btn-1" class="btn1">
    ボタン1
</button>

// JavaScript
'use strict';

document.querySelector('#js-btn-1').addEventListener('click', () => {
	console.log(this); // Window
});

アロー関数を使った場合で、strict モードであっても、このパターンの場合はグローバルオブジェクトを指します。

◆まとめ

イベントハンドラ内でイベントが発火した要素を参照したい場合は、コールバック関数で関数定義の形で関数を渡す必要があります。

アロー関数ではうまく動作しないので注意が必要です。

■明示的なthisのバインディング

JavaScriptで定義されている、callやapplyやbindメソッドを明示的にthisを指定することができます。

これらはどちらも、別のオブジェクトを引数としてオブジェクトのメソッドを呼び出すために使用します。

これは「thisを束縛する」といいます。

◆callメソッド

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

Function.prototype.call() - JavaScript | MDN

●非strict モードの場合

const personInfo = {
	getInfo() {
		return `私の名前は、${this.firstName} ${this.lastName}です。`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

const person2 = {
	firstName: '佐藤',
	lastName: '康平',
};

console.log(personInfo.getInfo.call(person1)); // 私の名前は、田中 直樹です。
console.log(personInfo.getInfo.call(person2)); // 私の名前は、佐藤 康平です。

callメソッドを呼び出す際に、メソッド内でthisが参照するオブジェクトを引数として指定します。

すると、thisの向き先が、callメソッドに渡したオブジェクトを参照するようになります。

●strict モードの場合

'use strict';

const personInfo = {
	getInfo() {
		return `私の名前は、${this.firstName} ${this.lastName}です。`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

const person2 = {
	firstName: '佐藤',
	lastName: '康平',
};

console.log(personInfo.getInfo.call(person1)); // 私の名前は、田中 直樹です。
console.log(personInfo.getInfo.call(person2)); // 私の名前は、佐藤 康平です。

strict モードで実行しても結果は変わりません。

●callメソッドに引数を渡す

'use strict';

const personInfo = {
	getInfo(hometown) {
		return `私の名前は、${this.firstName} ${this.lastName}です。出身は${hometown}です。`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

console.log(personInfo.getInfo.call(person1, '東京都'));

callメソッドには、引数を渡すこともできます。

◆applyメソッド

applyメソッドを使った場合も、callと同様にオブジェクトに所属する関数やメソッドを、別なオブジェクトに割り当てて呼び出すことができます。

callメソッドとapplyメソッドの違いは、call() メソッドは引数を個別に受け取りますが、apply() メソッドは引数を配列として受け取ります。

●非strictモードの場合

const personInfo = {
	getInfo() {
		return `私の名前は、${this.firstName} ${this.lastName}です。`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

console.log(personInfo.getInfo.apply(person1)); // > 私の名前は、田中 直樹です。

applyメソッド実行する際に、メソッド内でのthisをperson1オブジェクトに束縛しています。

これだけ見ると、callと同じです。

●strict モードの場合

'use strict';

const personInfo = {
	getInfo() {
		return `私の名前は、${this.firstName} ${this.lastName}です。`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

console.log(personInfo.getInfo.apply(person1)); // > 私の名前は、田中 直樹です。

strictモードでも結果に変わりはありません。

●applyソッドに引数を渡す

const personInfo = {
	getInfo(hometown, age) {
		return `
		    私の名前は、${this.firstName} ${this.lastName}です。
			出身は${hometown}です。
			年齢は${age}歳です。
		`;
	},
};

const person1 = {
	firstName: '田中',
	lastName: '直樹',
};

console.log(personInfo.getInfo.apply(person1, ['東京都', 30]));

callメソッドと違って、applyメソッドでは、引数を配列で渡します。

●クラスで使う場合の使い方

// 汎用的に使えるメソッドを持つ
class Product {
	getProductDetails() {
		console.log(`商品名: ${this.name}, 価格: ${this.price}`);
	}
}

// 個別の商品
class Food {
	constructor(name, price) {
		this.name = name;
		this.price = price;
	}

	display() {
		new Product().getProductDetails.apply(this);
	}
}

const foodItem = new Food('りんご', 200);
foodItem.display();

Foodクラスと、Productクラスは継承関係にありません。

Productクラスで、汎用的に使えるメソッドを定義したとします。

Foodクラスのdisplayメソッド内で、ProductクラスのgetProductDetailsを呼び出していますが、通常通りnew Product().getProductDetails()と呼ぶと、getProductDetailsメソッド内のthisは、Productインスタンスを指します。

そうするとundefinedになって困るので、applyやcallを使ってthisを束縛することで、汎用的に使えるようになります。

// 汎用的に使えるメソッドを持つ
class Product {
	static getProductDetails() {
		console.log(`商品名: ${this.name}, 価格: ${this.price}`);
	}
}

// 個別の商品
class Food {
	constructor(name, price) {
		this.name = name;
		this.price = price;
	}

	display() {
		Product.getProductDetails.apply(this);
	}
}

const foodItem = new Food('りんご', 200);
foodItem.display();

staticメソッドを呼び出す際に、thisをバインドする事もできます。

Function.prototype.apply() - JavaScript | MDN

もう1つbindメソッドでthisを束縛することもできます。

Function.prototype.bind() - JavaScript | MDN

■アロー関数のthisの動作

◆アロー関数はthisを持たない

アロー関数は、通常のfunction関数とは違い、アロー関数自身でthisを持っていません。

thisを持たないアロー関数では、this を参照した場合、外側のスコープの “通常の” 関数(function関数)からthisを取得してきます。

アロー関数が定義された場所の外側のスコープのthisを使います。

オブジェクトリテラルの中で直接定義された場合は、そのオブジェクトの外側のスコープ(多くの場合はグローバルスコープ)を参照します。

const name = '田中';

const person = {
	name: '佐藤',
	// sayName: function() { }の省略なので、function関数と同じです。
	sayName() {
	  console.log(this); // personオブジェクト
		const arrow = () => {
		  console.log(this.name); // > 佐藤
		}
		arrow();
	},
};

person.sayName();

スクリーンショット 2024-10-26 11.28.40.png

let user = {
	name: 'John',
	// 通常の関数
	sayHi() {
		console.log(`Hi, ${this.name}!`); // "Hi, John!"
	},
	// アロー関数
	sayHiArrow: () => {
		console.log(`Hi, ${this.name}!`); // "Hi, undefined!"
	},
};

user.sayHi();
user.sayHiArrow();

◆use striceモードでのアロー関数

'use strict';

const checkThis = () => {
    console.log('関数内のthisの値:', this);
    // 非strictモード: window
    // strictモード: undefined
};
checkThis();

非strictモードの場合は、thisがwindowオブジェクトを指します。
strictモードの場合は、undefinedになります。

◆通常の関数(メソッド)とアロー関数(メソッド)の違い

const user = {
	name: 'John',
	// 通常の関数
	sayHi() {
		console.log(`Hi, ${this.name}!`); // "Hi, John!"
	},
	// アロー関数
	sayHiArrow: () => {
		console.log(`Hi, ${this.name}!`); // "Hi, undefined!"
	},
};

user.sayHi();
user.sayHiArrow();

通常の関数(メソッド)は、呼び出し元がthisになるので、この場合はuser.sayHi();userがthisになります。

しかし、アロー関数(メソッド)で定義すると、アロー関数は自分自身でthisを持たないため、レキシカルスコープ(自分の親スコープ)のthisを使います。

この場合、親のスコープはwindowオブジェクトなので、window.nameを参照して、undefinedになります。

// オブジェクトリテラルはスコープを作らない
let user = {
  name: "John",
  // このアロー関数のthisはグローバルスコープ(window)を参照します
  sayHiArrow: () => {
    console.log(this.name);  // undefined
  }
};

注意したいのは、オブジェクトリテラルはスコープを作りません。

そのため、レキシカルスコープがwindowオブジェクトになります。

let user = {
  name: "John",
  // 関数はスコープを作成
  sayHi() {
    const arrow = () => {
      console.log(this.name);  // "John"
    };
    arrow();
  }
};

対象的に、通常の関数(メソッド)スコープを作ります。

スコープを作るため、通常の関数(メソッド)の中にアロー関数を作ると、アロー関数のレキシカルスコープは、

通常の関数であるsayHi(){}スコープのthisを使います。

スコープを作るもの、作らないものに関しては、以下になります。

  • スコープを作成するもの:
    • 関数宣言
    • 関数式
    • クラス宣言
    • ブロック文(let/constの場合)
  • スコープを作成しないもの:
    • オブジェクトリテラル {}
    • 配列リテラル []
// グローバルスコープ
let name = "Global";

let user = {
  name: "John",
  // アロー関数は定義された場所のスコープのthisを使用
  // ここではグローバルスコープ
  arrowMethod: () => {
    console.log(this.name);  // "Global"(ブラウザの場合)
  },
  
  // 通常のメソッド(関数)はスコープを作成
  regularMethod() {
    // このスコープ内でのthisはuserを指す
    const arrow = () => {
      console.log(this.name);  // "John"
    };
    arrow();
  }
};

user.arrowMethod();
user.regularMethod();

スコープを作る または 作らないという内容と、thisの向き先を合わせて確認すると、↑の結果になります。

◆アロー関数がnew演算子を使えない理由

// コンストラクタ関数
function Person(name, age) {
	this.name = name;
	this.age = age;
}

const person = new Person('佐藤', 20);
console.log(person.name);

通常の関数では、new演算子を使って、インスタンス化することができます。

const Person = (name, age) => {
	this.name = name;
	this.age = age;
};

const person = new Person('佐藤', 20);
console.log(person.name); // TypeError: Person is not a constructor

しかし、アロー関数はnew演算子を使ってインスタンス化できません。

理由は、アロー関数が自分自身でthisを持たないからです。

thisを持たないと、通常関数のようにコンストラクタとして、this.name = name;this.age = age;という使い方ができません。

そのため、アロー関数ではnew演算子を使うことができません。

◆アロー関数でcall、apply、bindを使ってthisを束縛できるのか?

// グローバルスコープ
let name = "Global";

let user = {
  name: "John"
};

let admin = {
  name: "Admin"
};

// 通常の関数
function regularFunction() {
  console.log(this.name);
}

// アロー関数
const arrowFunction = () => {
  console.log(this.name);
};

// 通常の関数の場合
regularFunction.call(user);    // "John"
regularFunction.apply(admin);  // "Admin"
regularFunction.bind(user)();  // "John"

// アロー関数の場合
arrowFunction.call(user);    // "Global"
arrowFunction.apply(admin);  // "Global"
arrowFunction.bind(user)();  // "Global"

また、アロー関数は自分自身でthisを持たないので、callやapplyやbindでthisを束縛できません。

理由は、アロー関数は自分自身でthisを持たないので、持っていないものを束縛することはできません。

◆アロー関数はargumentsオブジェクトを持たない

function showArgs() {
	console.log(arguments); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}

showArgs(1, 2, 3);

argumentsオブジェクトは、関数内で自動的に利用できる特別なオブジェクトです。
argumentsオブジェクトは、引数を配列っぽい感じ(厳密には配列ではないので配列に変換が必要)で受け取れる特徴がありますが、現在ではレストパラメーターがあるので使うことはないとも思います。

関数に渡されたすべての引数を含んでいます。

const showArgs = () => {
	console.log(arguments); // ReferenceError: arguments is not defined
};

showArgs(1, 2, 3);

アロー関数にすると、参照エラーになります。

◆アロー関数の使用に適している場面

●コールバック関数内でthisを参照する場合

const user = {
	name: '太郎',
	
	// サンプル①
	// 通常の関数を使用したメソッド
	sayHiWithFunction() {
		// function関数なので、thisはグローバルオブジェクトを参照する
		setTimeout(function () {
			console.log('こんにちは、' + this.name); // "こんにちは、undefined"
		}, 1000);
	},
	
	// サンプル②
	// アロー関数を使用したメソッド
	sayHiWithArrow() {
		// アロー関数はレキシカルスコープを参照するので、sayHiWithArrowを呼び出したオブジェクトを参照する
		setTimeout(() => {
			console.log('こんにちは、' + this.name); // "こんにちは、太郎"
		}, 1000);
	},
	
	// サンプル③
	// 通常の関数で解決する方法1: 変数に保存
	sayHiWithSavedThis() {
		// 通常の関数の場合は、thisを変数に代入して、後で使い回す。
		const self = this;
		setTimeout(function () {
			console.log('こんにちは、' + self.name); // "こんにちは、太郎"
		}, 1000);
	},
	
	// サンプル④
	// 通常の関数で解決する方法2: bind
	sayHiWithBind() {
		setTimeout(
			function () {
				console.log('こんにちは、' + this.name);
			}.bind(this),
			1000
		); // "こんにちは、太郎"
	},
};

// 実行してみる
user.sayHiWithFunction(); // こんにちは、undefined
user.sayHiWithArrow(); // こんにちは、太郎
user.sayHiWithSavedThis(); // こんにちは、太郎
user.sayHiWithBind(); // こんにちは、太郎

class Calculator {
  constructor() {
    this.total = 0;
  }

  calculate() {
   // アロー関数なのでCalculatorインスタンスのthisを保持
    ['1', '2', '3'].forEach(num => {
      this.total += parseInt(num);
    });
  }
}

const instance = new Calculator();
instance.calculate();
console.log(instance.total); // 6

サンプル①のように、setTimoutなど、コールバック関数を実行する場合、コールバック関数にfunction関数を指定すると、function関数内でのthisはグローバルを参照するので、存在しないプロパティにアクセスすると、undefineになります。

そのため、アロー関数が無かったときは、サンプル③のようにthisを1度変数に代入して後で使うことでthisを束縛していました。

アロー関数ができてからは、サンプル②のように、レキシカルスコープのthisを参照してくれるので、サンプル③のようにthisを1度変数に代入する必要がなくなりました。

◆アロー関数の使用に適していない場面

●オブジェクトのメソッドとしてthisを使う

const obj = {
  x: 10,
  // これは避ける
  method: () => {
    console.log(this.x);// undefined
  }
};

obj.method()

オブジェクトのメソッドとして、アロー関数を使うと、レキシカルスコープのwindowオブジェクトを参照するので使わないほうが良いです。

この場合は、function関数を使うべきです。

●メソッドとしてthisを動的に変更したい場合:

const user1 = {
  name: "太郎",
  // 通常の関数で定義したメソッド
  greet: function() {
    console.log(`こんにちは、${this.name}さん`);
  },
  // アロー関数で定義したメソッド
  greetArrow: () => {
    console.log(`こんにちは、${this.name}さん`);
  }
};

const user2 = {
  name: "花子"
};

// 通常の関数は.call()で別のオブジェクトにthisを束縛できる
user1.greet.call(user2);  // "こんにちは、花子さん"

// アロー関数は.call()でthisを変更できない
user1.greetArrow.call(user2);  // "こんにちは、undefinedさん"

thisを動的に束縛したい場合、アロー関数はthisを持たないので束縛できません。

その場合は、function関数を使う必要があります。

●イベントハンドラーでthisを要素に束縛したい場合

// 通常の関数の場合
const button = document.querySelector('button');

// 通常の関数: thisはクリックされた要素(button)を指す
button.addEventListener('click', function() {
  console.log(this.tagName);  // "BUTTON"
  this.style.backgroundColor = 'red';  // ボタンの背景が赤くなる
});

// アロー関数: thisは外部スコープのthisを指す(多くの場合window)
button.addEventListener('click', () => {
  console.log(this.tagName);  // undefined
  this.style.backgroundColor = 'red';  // エラー
});

イベントリスナーの引数に指定した関数をfunction関数で書くと、thisはイベントが発火した要素を指します。

アロー関数だと、thisを持っていないので、windowオブジェクトを指します。

// 正しい使い方: イベント引数からターゲット要素を取得
button.addEventListener('click', (event) => {
  console.log(event.target.tagName);  // "BUTTON"
  event.target.style.backgroundColor = 'red';  // ボタンの背景が赤くなる
});

アロー関数を使いたい場合は、コールバック関数で引数(event)を受け取って、その引数に対して操作することで、this問題に関して回避できます。

◆問題

JavaScript "this" keyword and arrow function

ここまで見ると、上記の質問に回答できると思います。

ぜひ挑戦してみてください。

this - JavaScript | MDN

■参考資料

Javascript ‘This’. Who Is This?. This. Default, Implicit, Explicit, and… | by Zeeshan Shamsuddeen | The Startup | Medium

JavaScript this

Object methods, "this"

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?