ForestEXOR Games Blog

引退プログラマーは最新技術に着いて行けない

JavaScriptのfunctionとラムダ式関数について

先日、腕試しでゲームを作っている際に、4つのゲーム画面に出してそれぞれ独立させる必要があった為、グローバルで使っていた入力処理をクラス内の処理に変える必要が出てきた。
その時に起きたバグについて書こうと思い、テストプログラムを組んでいる内に原因がわかってしまった。
けれどせっかくなので書き残して置こうと思う。


割りと適当だが普通に書くなら大体こうなると思う。

class CMouse{
	constructor(){
		this.L = false;
		this.x = 0;
		this.y = 0;
	}
}
let Mouse = new CMouse();

window.onload = function(){
	// キャンバス取得
	let canvas = document.getElementById("Canvas");
	
	// マウス左クリック取得
	canvas.onmousedown = function(){
		Mouse.L = true;
	}
	canvas.onmouseup = function(){
		Mouse.L = false;
	}
	// マウス座標取得
	canvas.addEventListener( "mousemove", function(e){
		let rect = e.target.getBoundingClientRect();
		Mouse.x = e.clientX - rect.left;
		Mouse.y = e.clientY - rect.top;
	});
}


またはこう。

	// マウス左クリック取得
	canvas.onmousedown = ()=>{
		Mouse.L = true;
	}
	canvas.onmouseup = ()=>{
		Mouse.L = false;
	}
	// マウス座標取得
	canvas.addEventListener( "mousemove", (e)=>{
		let rect = e.target.getBoundingClientRect();
		Mouse.x = e.clientX - rect.left;
		Mouse.y = e.clientY - rect.top;
	});


それをこんな感じに変更する必要があった。

class CGame{
	constructor( canvas ){
		this.canvas = canvas;
		this.Mouse = new CMouse();
		
		this.canvas.onmousedown = function(){
			this.Mouse.L = true;
		}
		this.canvas.onmouseup = function(){
			this.Mouse.L = false;
		}
		this.canvas.addEventListener( "mousemove", function(e){
			let rect = e.target.getBoundingClientRect();
			this.Mouse.x = e.clientX - rect.left;
			this.Mouse.y = e.clientY - rect.top;
		});
	}
}


ところがこのコード、エラーが起きるのである。
そしてそれを解決できたコードがこれ。

class CGame{
	constructor( canvas ){
		this.canvas = canvas;
		this.Mouse = new CMouse();
		
		this.canvas.onmousedown = ()=>{
			this.Mouse.L = true;
		}
		this.canvas.onmouseup = ()=>{
			this.Mouse.L = false;
		}
		this.canvas.addEventListener( "mousemove", (e)=>{
			let rect = e.target.getBoundingClientRect();
			this.Mouse.x = e.clientX - rect.left;
			this.Mouse.y = e.clientY - rect.top;
		});
	}
}


functionをラムダ式に変えただけである。

冷静に考えてみれば、functionはグローバルの関数を定義するわけであって、thisなんて使われても知らねーよ。ということになってエラーになるのは当たり前である。

問題はラムダ式、こちらはどうやらクラスの無名関数として扱われるらしいということ。


結論。
クラス内で使われるラムダ式はそのクラスの無名関数となる。


これを覚えて置かないと後々バグの温床になる可能性があるので忘れないようにしよう。