のくす牧場
コンテンツ
牧場内検索
カウンタ
総計:127,645,010人
昨日:no data人
今日:
最近の注目
人気の最安値情報

元スレ+ JavaScript の質問用スレッド vol.122 +

JavaScript覧 / PC版 /
スレッド評価: スレッド評価について
みんなの評価 :
タグ : 追加: タグについて ※前スレ・次スレは、スレ番号だけ登録。駄スレにはタグつけず、スレ評価を。荒らしタグにはタグで対抗せず、タグ減点を。
←前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 次へ→ / 要望・削除依頼は掲示板へ / 管理情報はtwitter

651 = :

じゃあなんで>>640は循環参照するのに>>646は循環参照しないんですか

652 = :

>>651
>>640は循環参照しません

653 = :

>>639
>>553を読めばわかる

> としとけばいいんでない
普通はやる必要がないこと。

removeEventListenerをするためには、名前を付けないといけない。
せっかくクロージャーを使って無名関数で
イベントハンドラを記述できるというのに、それは
newしたものを必ずdeleteしなければならない言語の
ようなもの。面倒くさい

> 一度しか実行されないハンドラは少数に限られるだろうから
それが昔のIEはページを移動してもメモリは解放されなかった。
だからどんどんブラウザが重くなっていった。

jQueryを使った場合は、クロージャーを使って本来あるべき姿のように
簡単に記述できていながら、メモリリークもしないようになっていた。

654 = :

>>641
> クロージャを少なくする設計には賛成だが、循環参照は関係ない

クロージャを少なくする設計なんて考えていたら、
全然問題がないEcmaScriptのmapやforEach、
underscoreやlodashの関数まで
クロージャー使わないほうがいいって勘違いするぞ。

問題は生存期間が長いイベントハンドラでクロージャーを使うと、
それが呼び出されたのと同じスコープの変数も使わないものであっても
生き残ってしまうっていうのが問題だろ。
だから逆にそのスコープ内の変数の方をなくしてもいい。

655 = :

>>654
要するに循環参照しない

656 = :

function foo() {
 var a = [巨大な配列];
 window.addEventHandler('load', function() { });
}

↑これを ↓こう書けばいいことは分かった

function onload() {
}
function foo() {
 var a = [巨大な配列];
 window.addEventHandler('load', onload);
}

巨大な配列は、onloadの中で参照しないからいいけど、
だけどonloadの中で参照したい変数があった時はどうするの?

つまり

function foo(value) {
 var a = [巨大な配列]; // これは要らないけど
 var b = value; // これは必要
 window.addEventHandler('load', onload);
}

function onload() {
 // ここでbを参照したい
}

クロージャーはこういうことが簡単にできるから
便利というめんもあるんだが

657 = :

>>656
jQueryを使えば簡単に実現できるよ。
jQueryなら追加のデータをイベントハンドラに渡せる。

その場合ならこのように書くことが出来る。

function foo(value) {
 var a = [巨大な配列]; // これは要らないけど
 var b = value; // これは必要
 $(window).on('load', {b: b}, onload);
}

function onload(event) {
 // ここでbを参照したい
 console.log(event.data.b);
}

658 = :

>>657
バッチリです。やっぱりjQuery便利やね

659 = :

>>655
循環参照はあまり関係ない

660 = :

この話を聞くと、Javaのクロージャーもどき(ラムダ)の
仕様は正解だったかもしれんな。

JavaScriptやいろんな言語のクロージャーはクロージャーの中から
aが見えてしまう。つまりイベントハンドラが生きてる間中、
イベントハンドラからaを参照しなくても、aが生き残ってしまう。

function foo() {
 var a = [巨大な配列];
 window.addEventHandler('load', function() {
  // ここからaが見える
 });
}

でも、javaのラムダは、aが見えない。
それがラムダはクロージャーではないと言われる理由なんだが、
ラムダであればクロージャーを使った場合のこの問題が無くなる。

クロージャーはあってもいいと思うんだけど
クロージャーとラムダの両方がほしい。そして必要ないならば
ラムダの方を使うべきだろう。

661 = :

>>659
循環参照ではなくて、ハンドラに使ったクロージャーは
生存期間が長いっていうのが問題だよね。

クロージャーの生存期間が短い場合は
クロージャーを使っても何の問題もない。

662 = :

>>661
そうだね。

(DOMとJavaScriptの)循環参照はメモリリークを招くという
古いIEのバグに関連する話で、
それを除けば循環参照してもよい。

ただ循環参照とハンドラの生存期間が長い問題は別の話で
ハンドラにクロージャーを使用すると、クロージャーから見える
そのスコープにある変数すべてがメモリ内に残ってしまう。
これはIEの循環参照のバグとは別の話。

663 = :

>>649
script要素が通信(コードを実行する)のは、
新しくscript要素を作成してdocumentに挿入したときと、srcも中身もないscript要素にsrcをセットしたとき。
http://html.spec.whatwg.org/multipage/scripting.html#the-script-element:prepare-a-script

<script src="hoge.js"></script> ← srcを変えても二度と通信しない
<script>alert(123);</script> ← 中身が書かれているので、srcを変えても通信しない
<script></script> ← srcも中身もないので、srcや中身を変えると一度だけ実行する

まあ簡単に言うと、一つのscript要素につきスクリプトは一回しか実行されないってことです。

664 = :

>>659
>>555で「↓なぜこれが循環参照になるのかというと」と明言している意図は?
初めから「循環参照は関係ない」と指摘しているのにあなたは枝葉にばかり拘っている

665 = :

>>664
循環参照にこだわるから、循環参照することは悪いのかって話になって
本当の問題が見えてこなくなる。

例えば、forEachでクロージャーを使っても循環参照する。だけどこれは問題ならない。
なぜならforEachのクロージャーの生存期間は凄く短いから。

本当の問題は、ハンドラであれば一般的にクロージャーの生存期間が長いことにある。
両方とも循環参照しているのに、一方は問題なくても一方は問題がある。
その理由は循環参照ではなくて、生存期間であるということに気づかないといけない。

それさえわかっていれば、クロージャーを使ってもいいことが理解できるはず。
本当の問題を理解せずに、わかりやすいからといってクロージャーは使ったらだめという
決まりを作ると、ルールだけが生き残ることになる。

666 = :

>>553
これremoveEventせずにDOMオブジェクトが消滅したらどうなるの?
ハンドラは開放されず永久に残リ続ける?

667 = :

>>665
そんなことは理解している
繰り返すが、>>555で「↓なぜこれが循環参照になるのかというと」と明言している意図は?

668 = :

>>666
> これremoveEventせずにDOMオブジェクトが消滅したらどうなるの?

DOMを消す時に、jQuery内部のcleanDataが呼ばれる。
cleanDataではDOMに関連づいたデータを全て消去する。

669 = :

>>647
> 繰り返すが、>>555で「↓なぜこれが循環参照になるのかというと」と明言している意図は?

古いIEでメモリリークする原因が、DOMとJavaScriptの間で
循環参照しているからだろ。「"DOMとJavaScriptの間で"循環参照してる」という
説明なしに、古いIEでメモリリークする理由を説明することは出来ない。

ここまでで一区切り。以下次の話として、

今の時代は古いIEは消えてしまったと考えていいものとする。
古いIEは消えてしまったのだから、循環参照しても問題ないよね?

そうは問屋が卸さない。
メモリリークの問題はなくなったが、
生存期間による不要なメモリ使用の問題は残っている。
これが今も起きてる注意すべき問題の話。

670 = :

そーゆーのってどうやってチェックするの?
タスクマネージャーを随時みるの?

671 = :

>>670
今はブラウザの開発者ツールに内蔵されてるよ。
昔は昔でそういうのを検出するプラグインがあった。

672 = :

>>668
MutationEventのようなもので監視してるわけではないのかな?
jQuery以外のAPIを叩いたり
ブラウザが勝手にDOMオブジェクトを消滅させた時はリークする?

673 = :

ダミーのデータを使ってわざと数十MB単位でメモリを使うように
すればタスクマネージャーでもわかると思う

674 = :

>>672
使われていないし、俺なら使わない

Mutation Event から Mutation Observers へ
http://standards.mitsue.co.jp/archives/001538.html
> “Detect DOM changes with Mutation Observers” で紹介されているように、
> DOM4 では Mutation Observers という新しいインタフェイス群が定義されました。
> これらを用いると、DOM2 Events で定義された Mutation Event を置きかえることができます。
>
> Mutation Event は数々の問題点を抱えていることが明らかになっています
> (策定中の DOM3 Events では該当箇所に警告文が記述されているほどです)。
> 例えばパフォーマンスに関わる問題があります。

jQueryでハンドラやデータを追加したのなら、jQueryで消さないとだめ。

ブラウザが勝手にDOMオブジェクトを消滅させることはないだろう。
(ページ移動は別。ページ移動時=unload時に解放していたはず)

jQueryで操作したならjQueryが使えるわけで、
それをjQuery以外で削除しようと思うことはあまりないと思うけどね。

675 = :

>>669
> 古いIEでメモリリークする原因が、DOMとJavaScriptの間で
> 循環参照しているからだろ。「
>>641で「巨大な配列がDOMでなければ循環参照しない」と指摘したはずだが、具体的に循環参照する「DOMオブジェクト」と「JavaScriptオブジェクト」は何だ?

function foo() {
 var a = [巨大な配列];
 window.addEventHandler('load', function() {
   alert(a.length);
   alert(foo.name);
 });
}

# 以下、戯言。
# 同じ内容を4回説明してようやく本題「循環参照しない」に入れたわけだが、何を答えてよいかわからない程の難問だったのかね…。

676 = :

>>675
それでお前が言いたいことは何なんだよ?w

678 = :

>>675
addEventHandlerって何だ、と思ったら>>555が出典だからか
普段から素のJavaScriptコードを書いているなら間違えるわけがないんだが

679 = :

>>678
すまんすまん。普段は書いてないよ。
jQueryの方を使うほうが楽なのでね。

681 = :

>>680
お、気づいたかw

そこは間違ったなって思っていたところなんだよ。
で、お前は気づいているよな?

windowsはグローバル変数だから当てはまるけど、
var elm = document.getElementById('id'); なら
ローカル変数になるから当てはまらないということを

今更ながら訂正すると

function foo() {
 var a = [巨大な配列];
 var elm = document.getElementById('id');
 elm.addEventListener('click', function() {
   alert(a.length);
   alert(foo.name);
 }, false);
}

これは循環参照する。

684 = :

>>681
> お、気づいたかw
あなたが気が付いてないだけで>>641から同じ指摘を何度もしている
対してあなたは何を勘違いしたのか、無関係な返答を繰り返すばかり
あなたは気が付くのが遅すぎる

685 = :

>>682
「対象IEのバージョン」と「MSDNのソースURL」を教えてください
グローバルオブジェクト(DOM)を含む循環参照でもメモリリークするなら回避手段はないと思いますが、当時はどうしていたのでしょう?

686 = :

>>683
関数1つ作ったらグローバルスコープの参照カウンタは回るのでは?
関数スコープの外はすべてクロージャなんだから

687 = :

>>660
今のブラウザなら大丈夫なはず
自分で確かめてないからアレだけど、検証結果がこのスレ(の過去スレ)にも貼られていた

ただしハンドラ内でeval()を使ったりすると、そこを通るまでaが参照されるか不明なので
aは残ってしまうだろうね

688 = :

>>686
その考え方が認められるのなら「a(Script)→foo(Script)」も成立するのでは?

function hoge () {
 var p = document.createElement('p');

 function foo () {
  var div = document.createElement('div');
 }

 foo();
}

変数から上位スコープオブジェクトを参照可能とする場合、「div(DOM)→foo(Script)→p(DOM)」で循環参照が成立するが、勿論そんなことはない
ECMAScript 5 は関数スコープなので参照は関数単位で形成される
変数はスコープチェーンの過程で上位スコープを参照するだけ

689 = :

ローカル変数はスコープチェーン上で始めに一番近い関数スコープから参照するのでグローバルスコープの参照は発生しえないね

690 = :

a[1] == 1
このときaが配列じゃなかったら
Uncaught TypeError: Cannot read property '1' of null
ってエラーになりますが
a != null && a[1] == 1
って書くのが正解ですか?

691 = :

>>610
エラーになるべきなら、エラーになっていいだろ?

それともaがnullになることが有るのですか?
そしてaがnullだったら、
aには何も入れないという"仕様"ですか?

692 = :

>>610じゃなくて>>690

693 = :

たとえばさjsonpで取得した時ですよ
サーバから正常にデータを取得出来た時にaは配列になるけど
ただしいデータが送られて来なかった場合はaはnullになります

694 = :

>>693
正しいデータが送られてこなかった場合は、
エラーなんだから、エラーでいいじゃん?

なんで正しいデータが送られてこない時にまで
そのエラーを握りつぶして、無理やり動かすようなことをするのさ?

695 = :

正しいデータが送られてこなかったら
エラーが発生しましたって表示させるか、
ログなどに表示して、そこに来る前に処理を打ち切るんだよ

696 = :

本気でコード書いたらエラーチェックとエラー処理の方がコード量が多い事も普通

697 = :

>>696
それは作り方が悪いな。
エラーを戻り値で返すパターンをやるとそうなる。
try-catchで捕まえてエラーをreturn -1とかするようなコードね。

例外を正しく使えばエラー処理のコードは少なくなるし、
JavaScriptの場合、window.onerrorで未知のエラーを
一括して捕まえることも出来る。

698 = :

>>697
> エラーを戻り値で返すパターンをやるとそうなる。
全てのAPIでエラーコードが違う場合にはそうせざるを得ないしそれで正しい
try-catchで括るのは単に楽してるだけ

699 = :

>>698
正しくねーよw

そういうのは、エラーコードをラップした
エラーオブジェクトを作ってthrowするもんだ。

そうすれば、エラーメッセージ等は
そのエラーオブジェクトに処理を任せられる。

そして特殊な処理をしたい場合だけcatchすればいい。

> try-catchで括るのは単に楽してるだけ
当たり前だろ。戻り値でエラーを返すやり方では面倒だから
ほぼ全ての言語に例外が搭載されたんだぞ。
楽して対応できるのだから、そっちのほうがいいだろ。

700 = :

>>699
> そういうのは、エラーコードをラップした
> エラーオブジェクトを作ってthrowするもんだ。
それだったら結局同じことをやっているに過ぎない
try-catchを使えば楽になるなんて迷信だ


←前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 次へ→ / 要望・削除依頼は掲示板へ / 管理情報はtwitterで / JavaScript一覧へ
スレッド評価: スレッド評価について
みんなの評価 :
タグ : 追加: タグについて ※前スレ・次スレは、スレ番号だけ登録。駄スレにはタグつけず、スレ評価を。荒らしタグにはタグで対抗せず、タグ減点を。

類似してるかもしれないスレッド


トップメニューへ / →のくす牧場書庫について