ちなみに ECMAScript 5 だと typeof arguments === ”arguments” となるらしい. ややこしい.
%22Javascript quiz %22 解答と解説 - リタマス
↑と僕の発言にリンクされてて、あやふやなことを言うべきじゃなかったと反省。反省の意味を込めて調べてみました。
ECMAScript5 の仕様の60ページ目を見ると、arguments オブジェクトを作るアルゴリズムのところに
と書いてあります。
このことから僕は typeof arguments === ”arguments” になるのだと思っていました。
次に71ページの typeof 演算子のところを見ます。(注、[ ← を二つ重ねるとはてな記法でおかしなことになっちゃうので間を空けて書いてます)
Type of val Result Undefined "undefined" Null "object" Boolean "boolean" Number "number" String "string" Object (native and does not implement [ [Call] ]) "object" Object (native or host and does implement [ [Call] ]) "function" Object (host and does not implement [ [Call] ]) Implementation-defined except may not be "undefined", "boolean", "number", or "string"
これを見ると、arguments は [ [Call] ] 内部メソッドを持たないので、"Object (native and does not implement [ [Call] ])" か "Object (host and does not implement [ [Call] ])" のどちらかということになります。
後者の場合は Implementation-defined なので、arguments もありかもしれません。
次に、仕様の5ページを見ると、native object とか host object について書いてあります。そのあたりを引用します。
4.3.6
native object
object in an ECMAScript implementation whose semantics are fully defined by this specification rather than by the host environment.
NOTE Standard native objects are defined in this specification. Some native objects are built-in; others may be constructed during the course of execution of an ECMAScript program.
4.3.7
built-in object
object supplied by an ECMAScript implementation, independent of the host environment, that is present at the start of the execution of an ECMAScript program.
NOTE Standard built-in objects are defined in this specification, and an ECMAScript implementation may specify and define others. Every built-in object is a native object. A built-in constructor is a built-in object that is also a constructor.
4.3.8
host object
object supplied by the host environment to complete the execution environment of ECMAScript.
どうやら arguments は native object のようです。(仕様の中に章があって挙動が定義されているから)
というわけで、ECMAScript5 でも typeof arguments === 'object' になるっぽいです。
適当なこと言ってごめんなさい。
(もちろん上に書いた推測が間違ってる可能性もありますが)
知ってる人にとってはどうってことない基本的なこと。
DOM 3 Events で deprecated 扱いになった DOM Mutations Events だけど、まだ代替品は無いわけで、これを使わざるをえない。
DOM Mutation Events は同期的なイベントなので、うまく使わないと全体の動作がモッサリしてしまうのが難点。
つまり、ループで DOM ツリーを弄るようなスクリプトだと、ループ中に毎回呼ばれて大変なことになる。
特に同期的動作が必要でない場合は setTimeout を入れてしまったほうがいい。
// イベントリスナーを付ける document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false); function nodeInsertedHandler() { // ハンドラー内でイベントリスナーを削除する document.removeEventListener('DOMNodeInserted', nodeInsertedHandler, false); setTimeout(function() { // 内容は別イベントに分ける /* 処理 */ // イベントリスナーを復活させる document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false); }, 10); }
これだと何回ループが回っても DOMNodeInserted は最初に1回出るだけ。しかも同期処理中でやってることは setTimeout を置くだけなのであまり重くならない。
もし挿入された要素が使いたいときは適当なスタックを用意。
document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false); var timer = null; var nodes = []; function nodeInsertedHandler(e) { nodes.push(e.target); if (!timer) { timer = setTimeout(function() { /* 処理 */ nodes = []; // スタック初期化 timer = null; // タイマーを初期化 }, 50); } }
または
document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false); var timer; var nodes = []; function nodeInsertedHandler(e) { nodes.push(e.target); clearTimeout(timer); timer = setTimeout(function() { /* 処理 */ nodes = []; }, 50); }
後者の場合は、例えば連続してイベントが発生 (キーを押しっぱなしにしたりとか、マウスを動かすなど) してノードを追加するときに、最後のイベントが終わってから 50ms 時間が空けば処理を実行する。debouncing と呼ぶらしい。前者だと、イベントが連続して発生すると、途中に割り込んで処理を実行する。どちらが良いかはその時の状況で使い分けたらいい。
DOM Mutation 系のイベントは DOM 3 Events では deprecated となった。(だいぶ前の話だけど)
Warning! The MutationEvent interface was introduced in DOM Level 2 Events, but has not yet been completely and interoperably implemented across user agents. In addition, there have been critiques that the interface, as designed, introduces a performance and implementation challenge. A new specification is under development with the aim of addressing the use cases that mutation events solves, but in more performant manner. Thus, this specification describes mutation events for reference and completeness of legacy behavior, but deprecates the use of both the MutationEvent interface and the MutationNameEvent interface.
Document Object Model (DOM) Level 3 Events Specification
DOMMutation の代わりとして watchSelector (CSS セレクターを使うもの) と watchCharacterData (テキストノード用) というのが考えられているらしい。
IE の behavior: expression みたいな感じ。
こちらは非同期なので、Mutation Events の使用ケースを完全に置き換えることはできない。(まあ元々 Mutation Events が同期だったのがパフォーマンス的によくないということだったので)
上のページの下のほうにある、Replacing Traditional Mutation Events というところを見ると感じがつかめると思う。
DOM 4 Events を目指してるとか前に読んだので、しばらくは DOMMutation が使われると思う。
今まで listen だったところに watch というのがなんともアレだけど。
その他、最近になって DOMActivate と DOMFocusIn と DOMFocusOut も deprecated となった。(理由はこのへん)
I previously mentioned to you that the DOMActivate, DOMFocusIn, and DOMFocusOut were being considered for deprecation in DOM3 Events. After looking into the matter closely and talking with various invested parties, I've now deprecated them in current drafts of the spec [1]. I've explained some of the rationale for this in the HCG [2].XForms and Deprecating DOM* Events in DOM3 Events from Doug Schepers on 2010-02-01 (public-forms@w3.org from February 2010)
それだけ。
てきとうなベンチ。
var N = 20; var workers = []; setTimeout(function() { for (var i = 0; i < N; i++) (function(i) { var w = new Worker('answer.js'); w.start = Date.now(); w.onmessage = function() { w.duration = Date.now() - w.start }; workers.push(w); })(i); }, 1000); setTimeout(function() { console.log(workers.map(function(w) {return w.duration})); workers.forEach(function(w) { w.start = Date.now(); w.postMessage(''); w.onmessage = function() { w.duration = Date.now() - w.start }; }); }, 3000); setTimeout(function() { console.log(workers.map(function(w) {return w.duration})); }, 4000);
postMessage(Date.now()+''); // 最初にロードしたときに返事 onmessage = function() { postMessage(Date.now()+''); // メッセージが遅られたときに返事 }
以下、特に言及のないときは Worker Script がブラウザのキャッシュにあるときの数字。
Firefox と Safari では最初のレスポンスが3ms、次 (Worker がロードしてから十分時間をおいた後) のレスポンスが3msぐらい。(ただし answer.js がキャッシュされてないときは最初のレスポンスが30msぐらい)
Chromium (昨日の時点での最新版) では 最初のレスポンスが100msぐらいで次は3msぐらい。
[346, 347, 346, 351, 351] [0, 1, 1, 46, 1]
[18, 18, 18, 20, 20] [6, 6, 5, 5, 5]
[325, 499, 250, 212, 212] [325, 499, 250, 212, 212]
↑1回目と2回目が同じなのはおかしい。測り方が悪かった。
[7, 5, 3, 5, 4] [9, 9, 9, 9, 9]
[4098, 4097, 4097, 4097, 4097] [4098, 4097, 4097, 4097, 4097]
これも測り方が悪かった。
[1171, 1164, 1121, 1164, 1124] [8, 8, 8, 8, 8]
今回はローカルから Worker を呼んでるけど、実際はネットワーク通信があるので1秒ぐらいはかかると思ってもいい。
[3, 6, 8, 8, 9, 12, 10, 13, 13, 13] // 最初のレスポンス時間 × Worker 10個 [5, 4, 4, 4, 4, 3, 3, 3, 3, 3] // 2回目のレスポンス時間 × Worker 10 個
[10, 11, 10, 6, 11, 9, 9, 9, 9, 10] [11, 11, 11, 11, 10, 10, 10, 10, 10, 10]
[562, 502, 481, 501, 588, 541, 578, 537, 601, 540] [14, 9, 14, 14, 14, 14, 14, 14, 14, 14]
[4, 8, 6, 7, 10, 10, 11, 14, 12, 14, 15, 17, 17, 18, 19, 21, 21, 23, 24, 24] [8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4]
[12, 16, 17, 11, 12, 11, 15, 16, 16, 12, 10, 16, 10, 15, 11, 14, 15, 5, 14, 14] [13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]
[740, 762, 907, 761, 909, 735, 852, 906, 906, 638, 929, 865, 853, 926, 925, 821, undefined, undefined, undefined, undefined] [14, 16, 16, 14, 13, 15, 16, 13, 13, 15, 13, 15, 16, 15, 15, 16, undefined, undefined, undefined, undefined]
Chromium は 16 個を超える Workers とは通信できない。
[9, 10, 10, 11, 11, 12, 13, 15, 15, 15, 16, 18, 17, 20, 19, 20, 21, 28, 23, 23, 27, 25, 27, 28, 30, 30, 32, 36, 33, 35, 37, 39, 38, 39, 41, 42, 43, 43, 44, 45, 46, 46, 47, 48, 49, 49, 50, 54, 52, 54, 55, 57, 56, 58, 58, 60, 60, 62, 63, 64, 65, 65, 95, 97, 106, 104, 105, 107, 127, 128, 156, 129, 154, 156, 181, 182, 185, 183, 184, 186, 214, 217, 217, 217, 266, 244, 267, 265, 296, 274, 302, 303, 323, 305, 322, 341, 342, 342, 396, 396] [17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 14, 14, 14, 14, 15, 14, 15, 14, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11]
[88, 98, 92, 97, 98, 95, 90, 96, 50, 89, 97, 97, 96, 94, 96, 25, 78, 49, 93, 86, 84, 48, 96, 24, 84, 47, 95, 47, 91, 47, 47, 83, 94, 85, 82, 88, 75, 88, 92, 45, 93, 92, 45, 90, 75, 81, 87, 80, 75, 20, 91, 81, 44, 72, 51, 64, 79, 90, 43, 79, 43, 81, 78, 80, 55, 83, 89, 80, 79, 80, 79, 41, 17, 72, 78, 88, 89, 41, 85, 76, 77, 67, 71, 87, 84, 16, 65, 86, 79, 87, 78, 83, 86, 87, 78, 14, 83, 86, 62, 78] [18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 17, 18, 17, 17, 17, 18, 17, 18, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18]
数字はどれも何度かやって安定した速さのときを取った。たまにこれの3倍ぐらいかかるようなときもある。
Chromium では Workers はまだ使えるレベルではない。
Workers を使うときは、並列化したいその瞬間に呼ぶのではなく、並列化することが分かっているなら予め予定した数をロードしておくのがいい。
3つもあるが、どれも動いてない。
SITEINFO を作るときに検索したりしないんだろうか?
これなら動く。
{ "url": '^http://arstechnica\.com/', "nextLink": '//div[@class="next-page"]/a', "pageElement": 'id("story")/div[@class="body"]', "exampleUrl": 'http://arstechnica.com/business/news/2010/01/chrome-os-interview-1.ars/' }
あとで整理しとこっと。
消す前のやつを残しておく。
url ^http://arstechnica\.com/
nextLink id("pager-wrapper")/ul/li/a[contains(text(),"Next")]
pageElement id("news-item")
exampleUrl http://arstechnica.com/
url ^http://arstechnica\.com/ nextLink //a[text()="Next Page >" and not (@class="Inactive")] pageElement //div[@class="ContentBody"] exampleUrl http://arstechnica.com/guides/buyer/guide-200809.ars
url ^http://arstechnica\.com/articles/
nextLink //p[contains(concat(" ",@class," ")," Paging ")]/a[last()]
pageElement //div[@class="Body"]
exampleUrl http://arstechnica.com/articles/paedia/ie8-super-standards-mode.ars
querySelector と queryScopedSelector の違いがよくわからなかったんだけど、こんな感じだそうな。
element.queryScopedSelectorAll("div") generally becomes
element.parentNode.querySelectorAll(":scope div", element) which is the same as
element.querySelectorAll(":scope div", element) or even
element.querySelectorAll(":scope div")
element.queryScopedSelectorAll(":scope > div") generally becomes
element.parentNode.querySelectorAll(":scope > div", element) which is the same as
element.querySelectorAll(":scope > div", element) or even
element.querySelectorAll(":scope > div")
element.queryScopedSelectorAll(":scope + div") generally becomes
element.parentNode.querySelectorAll(":scope + div", element)
element.queryScopedSelectorAll("div, div:scope") generally becomes
element.parentNode.querySelectorAll(":scope div, div:scope", element)
Re: Publishing Selectors API Level 2 as an FPWD?
引用元では queryScopedSelector を無くそうとか名前を変えようという議論の真っ最中だけど。
それ以上に、仕様は jQuery を元に作られてるんだなあと実感するスレッドだな。