ハヌモニヌコレクションNOW



Map、WeakMap、Setなどのすばらしいこずに関する蚘事はすでにハブに掲茉されおいたしたが、実際にはこれらのAPIの実際の機胜は明らかになりたせんでしたそれでも怜玢をうたく利甚した堎合。

これらのAPIは、FirefoxChrome Canaryに含めるこずができたす以倖には実際には実装されおいたせんが、最近たで、キヌずしおのHTMLElementのようなオブゞェクトの䜿甚はサポヌトされおいたせんでした。 たずえば、ポリマヌはわずか3週間前に削陀されたした



if (navigator.userAgent.indexOf('Firefox/') > -1)
      
      







なぜ圌らはずおも良いのですか 本質的に、Map / WeakMapは通垞のハッシュオブゞェクトずしお認識でき、キヌずしお䜿甚できるのは耇雑なオブゞェクトオブゞェクト、関数、配列のみです。これは、内容に埓っおではなくメモリ内のアドレスによっおバむンディングが行われるためです。

したがっお、フロント゚ンドに接続するこずが可胜になりたす





これにより、芁玠のid芁玠なしで䜜業したり、デヌタバむンディングを䜕倍も速くしたり、promiseのクレむゞヌな代替実装を䜜成したりするこずができたす。

WeakMapに぀いお説明したす。 それではなく、WeakMapの既存のポリフィルに぀いお説明したす。







残りの芁玠に぀いおは

マップは、キヌがプリミティブたたはオブゞェクトのいずれかであるハッシュです。

セットは、䞀意の芁玠のコレクションです。 Mapの䞊に構築するのは非垞に簡単です。 その最も重芁な利点は、配列の凊理です。これにより、uniq問題の耇雑さをOn ^ 2からOnに枛らすこずができたす。



node.jsを䜿甚しお、DBMSを操䜜する機胜がバック゚ンドにどのような機胜を備えおいるかに぀いおは、おそらくお勧めしたせん。



構文は、叀兞的なオブゞェクトの構文よりも少し耇雑ですが、非垞に理解可胜で読みやすいものです。



 var map = new WeakMap; map.set(someObject, 'someKey'); alert(map.get(someObject)); map.delete(someObject);
      
      







cornerJSの以前のバヌゞョンの1぀を含むいく぀かのプロゞェクトでは、そのような゜リュヌションはコヌドの簡朔さず読みやすさの芳点から正圓化されたしたが、ポリフィルの1぀が䜿甚されたずいう事実により、この゜リュヌションは顕著にメモリを消費するず考えたした。



ecmascript.orgの実装は、オプションずしお同様の実装を提䟛したす疑䌌jsから、やや瞮小された実行可胜ファむル、 githubでの完党な実装に転送されたす。



 window.WeakMap = function () { var keyList = []; var valueList = []; return Object.freeze({ 'get': function (key, defaultValue) { var index = keyList.indexOf(key); var value = valueList[index]; return index === -1 ? defaultValue : value; }, 'set': function (key, value) { var index = keyList.indexOf(key); if (index === -1) { keyList.push(key); valueList.push(value); } else { valueList[index] = value; } } //...( has, delete  clear) }); };
      
      







この実装には倚くの問題がありたす。

たず、メモリは著しく流れたす。芁玠が削陀されおも、keyListに残りたす。このようなWeakMapが実際のプロゞェクトで動䜜する堎合、倧量のメモリが基本的に既に削陀されたオブゞェクトのストレヌゞにリヌクする可胜性がありたす。 GarbageCollectorは芁玠では機胜せず、そのため、ただ存圚しおいるず芋なされたす。

゚ンゞンレベルではこれは解決できたすが、それでもGarbageCollectorが正しく機胜しない堎合がありたす。

次に、keyListに倚くの芁玠がある堎合、MacBook Air 2013のchromeでは埌者の遞択に時間がかかりたす。1e9th芁玠の怜玢には1秒以䞊かかりたした。 タスクの耇雑さはOnであり、パフォヌマンスに倧きく圱響する堎合がありたす。



サンプリングレヌトに関する問題を解決し、問題の耇雑さをO1に枛らす代替の実装がありたす。



 window.WeakMap = (function(){ var counter = Date.now() % 1e9; return function(){ var name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); return { set: function(key, value) { var entry = key[this.name]; if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {value: [key, value], writable: true}); }, get: function(key) { var entry; return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined; } } } })();
      
      







ただし、これはメモリの問題を解決するものではなく、さらに悪化させたす。 埪環リンクが各芁玠のメモリに衚瀺されたすkey [name] [0] === key。 オヌプンドキュメントを信じおいる堎合、ガベヌゞコレクタヌはそのようなシナリオをキャッチするこずができないため、時間が経぀に぀れおメモリが詰たっおしたいたすが、倚くの時間がかかりたす。

そのため、Polymer / X-tagsを慎重に䜿甚する䟡倀があり、その倚くはWeakMapに䟝存しおいたす䞊蚘の実装を少し枛らしたす。 そのため、たずえば、MutationObserverのポリフィルはそれに基づいおいたす。これは、MatationObserverだけでなく、倚数のサヌドパヌティプロゞェクトでも䜿甚されおいたす。その倚くは、メモリの問題を認識しおいたせん。

正盎な実装ず比范しお、さらにマむナスが远加されたす。 そのうちの1぀は、ほずんどの堎合重芁ではありたせん。凍結されたオブゞェクトにアタッチする機胜が倱われたす。 䞀郚の人にずっおは2番目は非垞に深刻です。 この実装はIE9 +になりたす。 最初のオプションは非垞に単玔なので、 MozillaからArray.prototype.indexOfポリフィルを接続するずIE6 +で動䜜したす。 Array.prototype.indexOfもIE9で登堎したしたが、少なくずも実装可胜です。 Object.definePropertyずは異なり厳密に蚀えば、IE8ではDOM芁玠のdefinePropertyがサポヌトされおいたすが、これに䟝存するこずはできたせん。



その結果、倧量のレコヌドの凊理が遅くなるか、キヌ芁玠に远加のプロパティ倖郚からアクセスするこずを意味する堎合がありたす、むテレヌタヌの䞀郚に問題が発生し、いずれの堎合も远加の属性でいっぱいになりたす。



jQueryにはWeakMapsの実装もありたす。 jQuery.data芁玠、キヌを芁求するずき、WeakMap実装の1぀を䜿甚しおいたす。 少し簡単に機胜したす。

おそらくあなたはあなたのコヌドで次のようなものを芋たこずがありたす



 document.body.jQuery19104357186993584037 20
      
      







これで、これがjQuery独自のWeakMapの芁玠のIDであるこずがわかりたした。



芁玠が削陀されるず、それは削陀されたすが、それでも





ここで䞻芁な郚分に行きたす。



WeakMapの名前は、GCで考慮されおいない匱い参照を匱参照を実装したかったためです。それぞれ、このキヌの他の゚ントリがない堎合、キヌず倀の䞡方が削陀されたす。

正しい実装では、キヌず倀の䞡方を削陀する必芁がありたす。

しかし、芋出しのgithubにあるリポゞトリの1぀で、驚くこずはできたせんが、フレヌズに出くわすたで、これが可胜だずは思いもしたせんでした。

挏れのないO1ルックアップ時間のWeakMapのShim


これはObject.definePropertyに䟝存したす-これはIE9 +およびフリヌズされたオブゞェクトを参照できないこずを意味したすが、芁玠には新しい属性がありたせんでした。

リポゞトリ自䜓はこちらです https : //github.com/Benvie/WeakMap/

私はこれがどれほど真実であるかをチェックするこずに抵抗できたせんでした。



このために、メモリスナップショットを䜿甚した簡単で迅速なテストが行​​われたした。







コヌドを完党にテストする

 // ""  1 //  // ""  2 var someStorage = []; var map = new WeakMap; var length = 1e5; // ie    1e4,     1e5      10   . for (var i = 0; i < length; i++) { someStorage[i] = {randomValue: Math.random() * 1e9 >>> 0}; //     ,        -  garbage collector       map.set(someStorage[i], {otherRandomValue: Math.random() * 1e9 >>> 0}) } // ""  3 for (var i = 0; i < length; i++) { someStorage[i] = void 0; } // ""  4
      
      







結果は次のずおりです。



Chrome 30Mac 10.9





Firefox 24Mac 10.9





IE 11Win 8.1





私は正盎に認めたす-アプリケヌションが䜿甚したすべおのメモリキヌず倀の䞡方のメモリが攟棄されたこずを芋おショックを受けたした。



その埌、念のため、怜玢時間が本圓にO1になるようにしたした。 それは真実であるこずが刀明したした。



Chrome 30Mac 10.9





Firefox 24Mac 10.9





最埌に、それはすべお真実です。 WeakMapは実際に倚数のプロゞェクトで䜿甚でき、デヌタバむンディング、ミサゎでの䜜業、倚数のプラグむンの䜜成に倧きな可胜性を䞎えたす。これは、5぀ごずにIE8 +プロゞェクトずIE9 +の残りの5぀だけがある堎合に、今日行うこずができたす。 同時に、WeakMapは特定の皮類のデヌタの凊理を簡玠化するだけでなく、パフォヌマンスの向䞊ず最適化を可胜にし、堎合によっおはメモリの凊理も倧幅に改善したす。

䞻なこずは、適切なポリフィルを遞択するこずです。



ずころで、次のようなダブルポリフィルを䜜成できたす。



 if (Object.defineProperty) { window.weakMap = //O(1)         } else { window.weakMap = //O(n)  ,     }
      
      







もちろん、この゜リュヌションで最も興味深いのは、その仕組みです。 解決策は非垞に玛らわしいので、それを理解するために、著者 Brandon Benvie に連絡しお、最も玛らわしい詳现を理解するために圌にいく぀か質問をしなければなりたせんでした。



私は他の誰かのコヌドに培底的に觊れるのは奜きではありたせんが、これは実装するのが信じられないほど興味深いものでしたが、ブランドン著者はES3でES6コンパむラを曞き、app.jsノヌド䞊のデスクトップアプリケヌション甚のプラットフォヌムを䜜成したしたJSでさらに耇雑な゜リュヌションを実装したした。

免責事項ほずんどの䟋では、読みやすくするためにコヌドがわずかに削枛されおいたす。䞀郚のチェックは削陀され、䞀郚はむンラむン化されおいたす。 ここで匕甚しおいるコヌドは、ガベヌゞコレクタヌがメモリを解攟できるようにするための䞀皮のシャヌマニズムであるか、コヌドをより高速に動䜜させるが、普通の人を簡単に混乱させお怖がらせるこずができるため、実際の䜿甚にはお勧めしたせん平均的なフロント゚ンド開発者。


最初は、すべおのメ゜ッドが戻るこずを混乱させる可胜性がありたす



 function toString() { [native code] }
      
      







この堎合、バむンドはどこでも行われず、通垞の関数をそのような関数に倉えるこずができたす。

ただし、次のように蚭定されたprepメ゜ッドは、すべおがはるかに単玔であるこずが刀明したした。



 var prep = { __proto__: [] } instanceof Array ? function(f){ f.__proto__ = stringifier } : function(f){ define(f, stringifier) };
      
      







特定の芁玠をプロトタむプ化する__proto__オブゞェクトを䜜成したす。

stringifierは別の関数です。



 var stringifier = function toString(){ return src[0] + nameOf(this) + src[1]; };
      
      







それはtoStringメ゜ッドずしおそれ自身に蚭定されたす私が理解しおいるように-これは簡朔にするためです。 その結果、実際には次のようなものが埗られたす。



 a.__proto__ = { toString: function(){ return "function "+this.name+"() { [native code] }" } }
      
      







ちなみに、この特定のコヌドは、関数のname属性がすべおのJS実装で利甚できるわけではないため、特にうたく機胜したせん。それを䜿甚するのは悪い習慣ず芋なされたす。 関数は環境名前を含むに䟝存せず、どのような条件䞋でも同じように機胜する必芁がありたす。

Function.prototype.nameはすべおの実装で䜿甚できるわけではないため、Brandonコヌドでは、関数名は次のように定矩されおいたす。



 function nameOf(func){ return 'name' in func ? func.name : toSource.call(func).match(/^\n?function\s?(\w*)?_?\(/)[1]; }
      
      







definestringifier、stringifierを削陀するだけで十分であり、コヌドが混乱を防ぐこずができたす。



そのため、キヌの保存堎所を理解する必芁がありたす。 これを行うには、倀を蚭定し、set関数に分類したす。



 function set(key, value){ unwrap(this).set(key, value); } function get(key){ return unwrap(this).get(key); }
      
      







そしお、ここから楜しみが始たりたす。



 var unwrap = function(weakMap){ return data.unlock(weakMap).value; }
      
      







dataは内郚クラスDataのむンスタンスであり、それ自䜓はWeakMapに非垞に䌌おいたすが、getずsetの2぀のメ゜ッドしかありたせんが、weakMap自䜓はすべお1぀の倧きなDataオブゞェクトに栌玍されたす。 1぀のデヌタオブゞェクト。

すべおのWeakMapを栌玍するメタWeakMapスペヌドをスペヌドず呌ぶ堎合がありたす。各WeakMapには、オブゞェクトのキヌず倀のペアがすでに含たれおいたす。



そしお最埌に、Dataオブゞェクトで最も興味深いこずです。



最初の秘:キヌの䞀般的なセットから倀を非衚瀺にしお、どのむテレヌタにも該圓しないようにしたす。 これを行うには、getOwnPropertyNamesを再割り圓おしたす



 Object.defineProperty(Object, 'getOwnPropertyNames', { value: function getOwnPropertyNames(obj){ var props = getProps(obj); if (Object.prototype.hasOwnProperty.call(obj, globalID)) //   hasOwnProperty,       - . props.splice(props.indexOf(globalID), 1); return props; } });
      
      







結果ずしお、chromiumデバッガヌでさえ、プロパティの存圚を疑っおいたせん。







2番目のトリック



 function storage(obj){ if (hasOwn.call(obj, globalID)) return obj[globalID]; //   Object.isExtensible var store = create(null); defProp(obj, globalID, { value: store }); return store; };
      
      







出力では、キヌ倀を持぀䞀意のオブゞェクトを取埗したす。



 function Data(){ var puid = createUID(), //   16-  secret = {}; // -      WeakMap   ,             .      ,      ,     .     –   private    Data this.unlock = function(obj){ var store = storage(obj); if (hasOwn.call(store, puid)) return store[puid](secret); var data = Object.create(null, { value: { writable: true, value: undefined } }); Object.defineProperty(store, puid, { value: (function(secret,data){ return function(key){ if(key===secret) return data } })(secret, data) }); return data; } }
      
      







最初の非垞に興味深い行は次のずおりです。



 Object.create(null, { value: { writable: true, value: undefined } });
      
      







nullから継承するオブゞェクトを䜜成したす。これにより、拡匵のないダミヌオブゞェクトが提䟛されたす。次に、2番目の匕数ずしおpropertiesObjectを枡したす。これにより、definePropertyを介しお必芁な倀を蚭定できたす。 明瀺的に倀を䜜成したすが、それが既に存圚し、その埌チェックデヌタの「倀」を枡すためだけに、未定矩で枡したす。



2番目の非垞に興味深い行



 Object.defineProperty(store, puid, { value: (function(secret,data){ return function(key){ if(key===secret) return data } })(secret, data) });
      
      







本質的に、各WeakMapに固有の䞀意の特定のダミヌオブゞェクトを取埗し、秘密キヌず䞀臎する堎合に栌玍された倀を返す関数を取埗したす。



実際、元のコヌドではさらに混乱しおいたす。



 defProp(store, puid, { value: new Function('s', 'l', 'return function(k){if(k===s)return l}')(secret, data) });
      
      







ブランドンは次のようにコメントしたした。

単にパフォヌマンス。 リテラル関数を定矩するこずにより、スコヌプ内のすべおのロヌカル倉数も取り蟌むこずができたす。 「関数」を䜿甚するこずで、スコヌプ内の唯䞀のものはグロヌバルオブゞェクトです。 目暙は、䞍泚意によるメモリリヌクを防ぐこずでした。 お䜿いのバヌゞョンもおそらく正垞に動䜜したすが、それ以倖の堎合は保持されない参照をリヌクする可胜性があり、WeakMapを䜿甚する目的の倚くは、この皮のリヌクを防ぐこずです。



ロシア語ぞの無料の拡匵翻蚳では

これはパフォヌマンスのために行われたす。 function{}を䜿甚しお関数を䜜成するこずにより、ロヌカルのミサゎにバむンドしたす-いかなる方法でもそれを䜿甚しないずいう事実にもかかわらず。 デバッガヌを䜿甚する堎合、この関数の内郚から、䜜成された配列党䜓が利甚可胜であるこずがわかりたす。

new Functionは、グロヌバルスコヌプで機胜する関数を䜜成し、ロヌカルスコヌプぞのバむンディングを削陀したす。 実装の目的はメモリリヌクがないこずであり、そこで行われたすべおの䜜業は、䞻にリヌクがないこずに察しお行われたした。


その結果、実際には、メモリ内の䜕かのように芋えたす。



 var map = new WeakMap; var a = {}; map.set(a, ['test']) console.log(a['2ql3g5ae6pcwstt9']['o6tnzx1xskf39pb9']) >function (k){if(k===s)return l}
      
      







['2ql3g5ae6pcwstt9']は、任意のweakMapに該圓するすべおの芁玠に共通のランダムに生成されたglobalIDからの名前を持぀ダミヌオブゞェクトであるキヌストア、および['2ql3g5ae6pcwstt9'] ['o6tnzx1xskf39pb9クロヌゞャを栌玍するオブゞェクト



 { value: '' }
      
      







2番目の匕数ずしお。 倀が倉曎された堎合は、倀を眮き換えたすが、オブゞェクトは以前ず同じようにクロヌゞャヌ内に残りたす。



すべおのキヌは、Object.getOwnPropertyNamesから削陀するたで、あらゆる意味で非衚瀺になりたす。これにより、コヌド内に存圚する疑いのない、ほずんど存圚しないキヌが埗られたす。それらは、むテレヌタにリストされず、ク゚リ。

結果ずしお、簡単に倉曎でき、垞にキヌで削陀されるコンテンツを含むコンテナを開くためのキヌを備えた完党に安党なコヌド内の倉曎なしのアクセスは䞍可胜ですIE9 + WeakMapsの実装を取埗したす。 O1の耇雑さ、および属性が蚭定可胜なObject.definePropertyによる曞き蟌み䞍可および削陀䞍可。アクセスキヌで構成可胜false、列挙可胜false、曞き蟌み可胜falseデフォルトで蚭定。



もちろん、キヌの属性が䜜成されるずいう事実は奜きではありたせんが、どの゜リュヌションがこれより理想的かはわかりたせん。 ブランドンが蚘憶を扱う玠晎らしい仕事をし、本番で䜿甚すべき決定を䞋したこずを認めるだけです。



コメントで、特にHarmonyコレクションず特にWeakMapを匕き続き䜿甚できる理由、および同様のAPI党般に察する態床に぀いお意芋を聞くこずができたす。



UPD。 ブランドンは、0、eval 'this'が最埌に行われた理由を説明したした。 0、eval "this"は、var e = evalずほが同等です。 e「これ」。 実際、evalは垞にロヌカル配列で機胜したすが、evalを別の倉数に割り圓おお実行するず、グロヌバル環境で実行されたす。



All Articles