JavaScriptのthisの挙動について

このQ&Aのポイント
  • JavaScriptでthisの挙動について調査しました。コードを実行した際に問題が発生し、thisが保持できていないことが原因でした。
  • コード中でthisをobj.listに入れているにも関わらず、obj.listと同期してthisが消える現象が起きました。
  • コード中で先頭に#を付けた部分を変更すると期待通りの動作になりましたが、もっと簡潔な書き方や迅速な解決方法があれば教えていただきたいです。
回答を見る
  • ベストアンサー

JavaScriptのthisの挙動について

JavaScriptで次のCodeのようなコードを書いて実行したところ、Outputのようになります。 原因は、thisが保持できていなかったからです。 ※先頭に#が付いているのが問題の箇所です。 Code: Array.prototype.rs = function() // rs = random sort { var ret = { list: new Array(), length: new Number() }; # var obj = { # list: this # }; var tmp = { index: new Number(), length: obj.list.length }; while((ret.length = ret.list.length) < tmp.length) { tmp.index = Math.floor(Math.random() * (tmp.length - ret.length)); ret.list[ret.length] = obj.list[tmp.index]; obj.list.splice(tmp.index, 1); } return ret.list; } var list = new Array(1,2,3,4,5,6,7,8,9); alert(list.rs()); alert(list.rs()); Output: アラートウィンドウで 2,4,6,9,3,5,1,8,7 (一例) アラートウィンドウで 出力無し 要するに、thisをobj.listに入れて、thisには触っていないのに、obj.listと同期してthisが消えているのです。 Code中で先頭に#を付けた部分を次のように変更すると期待通りに動作しましたが、腑に落ちません。 # var obj = { # num: this.join('') # }; # obj.list = obj.num.split(''); もっと簡潔な書き方や、迅速に動く書き方があればご教授頂きたいです。

noname#144089
noname#144089

質問者が選んだベストアンサー

  • ベストアンサー
  • Werner
  • ベストアンサー率53% (395/735)
回答No.2

> obj.list.splice(tmp.index, 1); ここで 配列 this を破壊しています。 この時点で、obj.list == this (指してる先は同じ配列)なのですから、 obj.list.splice と this.splice は同じことです。 thisを破壊させない為には、参照だけコピーする シャローコピーではなく、 各要素をコピーするディープコピーをすれば良いでしょう。 > Code中で先頭に#を付けた部分を次のように変更すると期待通りに動作しましたが、腑に落ちません。 > #var obj = { > #num: this.join('') > #}; > #obj.list = obj.num.split(''); 2桁の要素が出てきたとたん破綻しそうな気がしますが…。 sliceメソッドは新しい配列を返してくれるので、これをつかえばいいでしょう。 var obj = {  list: this.slice() }; あと、ソートアルゴリズムに(シャッフル後の確率分布が均等になるという意味で)問題はないですが、 spliceは配列が長くなると結構重いので、要素の入れ替えだけでシャッフルできる Fisher-Yates法がお勧めです。

noname#144089
質問者

お礼

解答ありがとうございます。 sliceってそんなことができたんですね。 勉強になります。 spliceが重くなるということも知りませんでした。 Fisher-Yates法…。初耳です。調べておきます。 何も知らないな、僕は。 ありがとうございました。

その他の回答 (2)

  • fujillin
  • ベストアンサー率61% (1594/2576)
回答No.3

すでにきちんと回答が出ていますので、いわずもがなですが・・・ 元の配列を保存しておきたいのか直接シャッフルしたいのかが不明でしたので、両方可能な例。 flagをtureにすると元の配列は保存されます。(元はシャッフルされず新しい配列が返される) var randomize = function(flag){  var i, j, tmp, n = this.length;  var a = flag?this.slice():this;  for(i=0; i<n; i++){   j = Math.random()*n | 0;   tmp = a[i], a[i] = a[j], a[j] = tmp;  }  return a; } Array.prototype.rs = randomize; var list = new Array(1,2,3,4,5,6,7,8,9); alert("result=" + list.rs(true).join(", ") + "\nlist =" + list.join(", ")); //保存 alert("result=" + list.rs().join(", ") + "\nlist =" + list.join(", ")); //シャッフル (インデントは全角空白になっています)

noname#144089
質問者

お礼

回答ありがとうございます。 これは…、Wernerさんの仰るFisher-Yates法というやつですね。 代入を,で区切れること、三項演算子の条件式の()がいらないことは初見です。 精進します。

回答No.1

this のかいとうではありませんが・・・ var rs = function (a,b) {return Math.random () - .5}; alert ([0,1,2,3,4,5,6,7,8,9].sort (rs). sort (rs)); 1かいだとかたよるらしいので、2かいする?!(笑)

関連するQ&A

  • JavaScriptで全くの初心者なのですが、

    JavaScriptで全くの初心者なのですが、 フィッシャーイェーツのシャッフルをaのリスト内でしてから、 そこから一つ値をとり、 別の関数内で、その関数内での新たな変数に代入するようにしたいです。(function(){var b = this.result;}のような) プログラミングはやらないのに急に使うことになり困っているのでだれか助けてください。 a = [1,1,1,5,5,5,9,9,9]; for (var a=[],i=0;i<9;++i) a[i]=i; this.result = a[shuffle(a.length)] ; function shuffle(array) { var tmp, current, top = array.length; if(top) while(--top) { current = Math.floor(Math.random() * (top + 1)); tmp = array[current]; array[current] = array[top]; array[top] = tmp; } return array; }

  • JavaScript

    下記のプログラム(ボタン早押しゲーム)に以下のようなプログラムを入れたいのですが、どうすれば良いでしょうか? ・正しい順番にボタンが押されていれば毎回シャッフル(押されたボタンが色が変わり押せなくする。) 間違いならアラートで違うとメッセージを出す。 <script type="text/javascript"> // 初期化処理 var suji = "(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)"; var pushed = ""; // 押されたボタン管理 var startTime; // ゲーム開始時間を管理 var labelArray = suji.split(""); // 数字を一文字ずつ分割 $("startButton").onclick = start; nextGame(); // スタートボタンを表示する // 開始準備 function nextGame() { $("buttons").innerHTML = ""; $("startButton").style.visibility = "visible"; } // ゲーム開始 function start() { $("startButton").style.visibility = "hidden"; // 配列に数字を代入して順番をシャッフル arrayShuffle(labelArray); // ボタンを作る for (var i = 0; i < labelArray.length; i++) { var b = document.createElement("button"); b.innerHTML = labelArray[i]; b.onclick = button_clickHandler; $("buttons").appendChild(b); } startTime = (new Date()).getTime(); } // ボタンが押された時の処理 function button_clickHandler(e) { var ch = e.target.innerHTML; // 押されたボタンの文字 var tmp = pushed + ch; // 順番が正しいかどうか判定する if (suji.substr(0, tmp.length) != tmp) { alert("違います。次は、" + suji.substr(pushed.length,1)); return; } e.target.disabled = true; e.target.style.backgroundColor = "#202020"; pushed = tmp; if (pushed == suji) { var now = new Date().getTime(); var tm = Math.floor((now - startTime) / 1000); alert("おめでとうございます。\n" + tm + "秒でクリア"); nextGame(); } } // 配列をシャッフルする function arrayShuffle(bs) { for (var i = 0; i < bs.length; i++) { var r = Math.floor(Math.random()*bs.length); var tmp = bs[i]; bs[i] = bs[r]; bs[r] = tmp; } } function $(id) { return document.getElementById(id); } </script>

  • this.nの記述について

    ムービクリップのa1,a2,a3,a4をロールオーバするとそれぞれ対応する数字のh1,h2,h3,h4のムービクリップのalphaが0から100にtweenを使って動かすことを考えたいと思います。 //初期設定 var obj_array:Array = new Array(); for (var i = 1; i<=4; i++) { obj_array[i] = eval("a"+i); obj_array[i]._alpha = 0; } //ここからロールオーバの操作 import mx.transitions.*; import mx.transitions.easing.*; for (var i = 1; i<=4; i++) { // 各ボタンの固有値 n を設定 this["h"+i].n = i; // 各ボタンの onRollOver 動作定義 this["h"+i].onRollOver = function() { var tween_handler:Object = new Tween("a"+this.n, "_alpha", Strong.easeOut, 0, 100, 1, true); };  しかし、まったく動作しません。理由としては何が考えられるでしょうか。

    • ベストアンサー
    • Flash
  • ActionScriptで配列をクリアする方法を教えてください。

    ActionScriptで、オブジェクトを格納した配列をクリアする正しい方法を教えてください。 具体的には、 ・new Array() で配列を生成する。 ・new Object() で生成したオブジェクトを配列に格納する。 ・配列の中身を処理したあと、配列を一度クリアする。 ・配列に別のオブジェクトを格納する。 ような処理をしたいのですが、メモリリークを起こさずに配列をクリアするにはどうしたらいいでしょうか? 例えば、イメージはこんな感じです。(意味のないコードですが) // 配列の生成 var arr = new Array(); // 配列にオブジェクトを格納 for (var i = 0; i < 5; i++) { var obj = new Object(); obj.num = i; } // 配列のクリア arr.length = 0; // クリアした配列に別のオブジェクトを格納 for (var i = 0; i < 5; i++) { var obj = new Object(); obj.num = i * 2; } 上記のコードで、arr.length = 0; で配列をクリアしていますが、これで配列に格納されたオブジェクトが確保したメモリがすべて解放されるでしょうか? あるいは、配列の内容がなくなるまで pop() 等で取り出す等の処理をする必要があるでしょうか? 以上よろしくお願いします。

    • ベストアンサー
    • Flash
  • 複数枚画像を表示して一定時間で切り替える方法

    画像を表示して一定時間で切り替える方法は別ページに記載されているものをみて出来たのですが、最初に複数枚表示してて、その画像を定時間で切り替えたいのですがご教授願います。 以下別質問で見つけたもの <!-- これを画像を張りたい位置に挿入。<body>~</body>内 --> <script type="text/javascript"> /* 設定部分 */ var interval = 3000; //切り替わる時間(1000=1秒) var imgs = new Array(); var hrefs = new Array(); imgs[0] = new Image(); imgs[0].src = "http://www.xxxxx.net/images/img-main/pake/013.jpg"; hrefs[0] = "http://www.xxxxx.net/moviepages/130923_707_01/index.html"; imgs[1] = new Image(); imgs[1].src = "http://www.xxxxx.net/images/img-main/pake/014.jpg"; hrefs[1] = "http://www.xxxxx.net/moviepages/130906_701_01/index.html"; imgs[2] = new Image(); imgs[2].src = "http://www.xxxxx.net/images/img-main/pake/015.jpg"; hrefs[2] = "http://www.xxxxx.net/moviepages/130830_698_01/index.html"; /* プログラム部分 */ var num = Math.floor(Math.random()*imgs.length); document.write('<a href="'+hrefs[num]+'" id="cth"><img src="'+imgs[num].src+'" id="cti" style="border:0" /></a>'); var cti = document.getElementById("cti"); var cth = document.getElementById("cth"); setInterval(function(){ num += Math.floor(Math.random()*(imgs.length-1))+1; if(num>=imgs.length) num -= imgs.length; cti.src = imgs[num].src; cth.href = hrefs[num]; },interval); </script> <!-- ここまで --> script初心者でよくわかりません><

  • evalの挙動について

    evalで特定のコントロールにフォーカスを当てれないでしょうか? asp と JavaScript を使って、ユーザが数値以外の値を入力した場合、エラーとしてそのコントロールにフォーカスを当てたいと思います。 Form の OnSubmit で以下の関数を呼び出します。 function CheckForm(){ nRoomCnt = <%=nRCnt%>; var ArrayRoomId = new Array(); <% for nCnt = 0 to nRoomCnt - 1 %> ArrayRoomId.push( <%=arrRoomId(nCnt)%> ); <% next %> for(var nCnt = 0; nCnt < nRoomCnt; nCnt++){ var s = "sCheck = document.form06.SelRoom" + ArrayRoomId[nCnt] + ".value;" ret = eval(s); //こちらは正しく評価されます。 alert(ret); if(isNaN(ret)){ s = "document.form06.SelRoom" + ArrayRoomId[nCnt] + ".SetFocus(); return false;"; ret = eval(s); // ここでエラーが発生し、次の画面に遷移してしまいます。 alert(s); alert(ret); return false; } } alert(ret); return true; どなたか詳しい方、ご教示いただけないでしょうか? よろしくお願いします。

  • javascript new演算子について質問があります。

    javascript new演算子について質問があります。 只今javascript勉強中なのですが、どこかのサイトでnew演算子は悪いパーツとの 記事を読みました。 そこでnew演算子を使用せずにオブジェクト生成の方法を試行錯誤考えてみたのですが (IE用に__proto__もなしで)例えば以下コードはアリなコードでしょうか?? あるいはありえない感じでしょうか?? またnewが悪なのはインスタンスを作る時にnewを付け忘れると、 グローバルな汚染をしてしまうということだけなのでしょうか?? オブジェクトとプロトタイプの理解に苦しんでいるところなので 色々と曖昧なのですが、どなたかどうぞご教授お願い致します。 var obj = {}; obj.method = function(){ this.prop1 = "hoge" return this; }; obj.method.prototype = obj.method(); obj.method.prototype.prop2 = "fuga"; var obj2 = obj.method(); console.log(obj2)

  • JavaScriptのグローバル変数

    JavaScriptのグローバル変数 次のプログラムで、(1)は p.obj1 = 32 と表示されるのに、 (2)はエラーになります。pはグローバル変数的にアクセスできる のに、なぜobj1がアクセスできないのかが分かりません。 詳しい方の解説をお願いします。 // test.js main(); function main() { F2(new F1(32, 100)); alert("p.obj1 = "+ p.obj1); // (1) alert("obj1 = "+ obj1); // (2) } function F1(obj1, obj2) { this.obj1 = obj1; this.obj2 = obj2; } function F2(p) { this.p =p; return this.p; } function alert(message) { WScript.Echo(message) }

  • JavaScriptについて

    JavaScriptでボタンゲームを作っているのですが、ボタンを押して正解だった場合、色が変わり押せなくなるようにしたいのですが、うまくいきません。どうしたら良いか困っています。 原因として「順番が正しいかどうか判定する」ところじゃないかと思います。 <script type="text/javascript"> //初期処理 var suji = "(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)"; var pushed = ""; // 押されたボタン管理 var startTime; // ゲーム開始時間を管理 var labelArray = suji.split(""); // 数字を一文字ずつ分割 $("startButton").onclick = start; nextGame(); // スタートボタンを表示する // 開始準備 function nextGame() { $("buttons").innerHTML = ""; $("startButton").style.visibility = "visible"; } // ゲーム開始 function start() { $("startButton").style.visibility = "hidden"; // 配列に数字を代入して順番をシャッフル arrayShuffle(labelArray); // ボタンを作る for (var i = 0; i < labelArray.length; i++) { var b = document.createElement("button"); b.innerHTML = labelArray[i]; b.onclick = button_clickHandler; $("buttons").appendChild(b); } pushed = 0; startTime = (new Date()).getTime(); } // ボタンが押された時の処理 function button_clickHandler(e) { var ch = e.target.innerHTML; // 押されたボタンの文字 // 順番が正しいかどうか判定する if (suji.substr(pushed, 1) != ch) { alert("違います。次は、" + suji.substr(pushed,1)); } else { $("buttons").innerHTML=""; arrayShuffle(labelArray) for(var i = 0; i < labelArray.length; i++) { var b = document.createElement("button"); b.innerHTML = labelArray[i]; b.onclick = button_clickHandler; $("buttons").appendChild(b); } e.target.disabled = true; e.target.style.backgroundColor = "#909090"; pushed++; } if (pushed == labelArray.length) { var now = new Date().getTime(); var tm = Math.floor((now - startTime) / 1000); var min = Math.floor(tm % 3600 / 60); var sec = tm % 60; alert("おめでとうございます。\n"+ min + "分" + sec +"秒でクリアです!"); nextGame(); } } // 配列をシャッフルする function arrayShuffle(bs) { for (var i = 0; i < bs.length; i++) { var r = Math.floor(Math.random()*bs.length); var tmp = bs[i]; bs[i] = bs[r]; bs[r] = tmp; } } function $(id) { return document.getElementById(id); } </script>

  • JAVASCRIPTについて教えてほしいのですが

    <script language="JavaScript"> var imglist = new Array( "top_pic.jpg", "top_pic2.jpg", "top_pic3.jpg", "top_pic4.jpg" ); var selectnum = Math.floor((Math.random() * 100)) % imglist.length; var output = "<img src=" + imglist[selectnum] + ">"; document.write(output); </script> ・・・・という画像をランダムに表示するスクリプトなんですが、 この画像を表示する際のオプション?みたいなのは何処に記入すればよいのでしょうか? align="right"や、width="50%" みたいなやつです。 よろしくお願いします