01647

ustreamer-01647

Javascriptのthisでコケた話

スコープの問題だろう.

初版

function(data)のthisはfunction(data)を指していて,コンストラクタのthisではない.

/**
 * TSVファイルのパース結果を持つクラス
 * @param {undefined} characterClass キャラクタクラス
 * @returns {undefined}
 */
function Tsv(characterClass) {
	this.filePath = TABLE_DIR + characterClass + TABLE_EXTENTION;
	$.ajax(this.filePath, {
		dataType: 'text',
		error: function(jqXHR, textStatus, errorThrown) {
			var message = "file not found: " + this.filePath;
			console.log(message, textStatus);
		},
		success: function(data) {
			this.data = $.parse(data).results;
		},
		async: false
	});
}

Tsv.prototype = {
	/**
	 * table要素として出力する
	 * @returns {undefined}
	 */
	show: function() {
		var tableData = this.data;
		var table = $("<table />").appendTo($("div#showTable"));
		// table caption
		table.append($("<caption />", {"text": this.filePath}));
		// table header
		var thead = $("<thead />");
		thead.appendTo(table);
		var tr = $("<tr />");
		tr.appendTo(thead);
		this.data.fields.forEach(function(col) {
			tr.append($("<th />", {"text": col}));
		});
		// table body
		this.data.rows.forEach(function(row) {
			var tr = $("<tr />");
			tr.appendTo(table);
			this.data.fields.forEach(function(col) {
				tr.append($("<td />", {"text": row[col]}));
			});
		});
	}
};

改訂

コンストラクタを変えた.function(data)中,ローカル変数tableDataで受けておいて,function(data)後,コンストラクタ終了直前にthis.dataへ代入する.show()でのthis.dataアクセスは3箇所あるが,初め2個が成立した.ただ,最後が良くなかった.コケた.

/**
 * TSVファイルのパース結果を持つクラス
 * @param {undefined} characterClass キャラクタクラス
 * @returns {undefined}
 */
function Tsv(characterClass) {
	this.filePath = TABLE_DIR + characterClass + TABLE_EXTENTION;
	var tableData;
	$.ajax(this.filePath, {
		dataType: 'text',
		error: function(jqXHR, textStatus, errorThrown) {
			var message = "file not found: " + this.filePath;
			console.log(message, textStatus);
		},
		success: function(data) {
			tableData = $.parse(data).results;
		},
		async: false
	});
	this.data = tableData;
}

第2訂

2重forEach内部のthis参照先が期待通りでない.ああ,そうか.初版と同様,funtion(row)で切り離されたんだ.show()にも手を加えた.先頭でtableDataに受けておいて,これを使用する.解決した.

Tsv.prototype = {
	/**
	 * table要素として出力する
	 * @returns {undefined}
	 */
	show: function() {
		var tableData = this.data;
		var table = $("<table />").appendTo($("div#showTable"));
		// table caption
		table.append($("<caption />", {"text": this.filePath}));
		// table header
		var thead = $("<thead />");
		thead.appendTo(table);
		var tr = $("<tr />");
		tr.appendTo(thead);
		tableData.fields.forEach(function(col) {
			tr.append($("<th />", {"text": col}));
		});
		// table body
		tableData.rows.forEach(function(row) {
			var tr = $("<tr />");
			tr.appendTo(table);
			tableData.fields.forEach(function(col) {
				tr.append($("<td />", {"text": row[col]}));
			});
		});
	}
};

不安

コンストラクタのthis.data = tableData;について,参照渡しになっている点.clone()で複製すべきではなかろうか,と.コンストラクタを抜けた後もthis.dataがtableDataを保持している事自体が異様に感じる.参照先が解放されて,this.dataも値参照できなくなるのではないかと.

var clone = function(obj){
    var object = {};
    for(var i in obj){
        object[i] = obj[i];
    }
    return object;
}