Как перебрать слабую карту?
Слабая карта JavaScript ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) не позволяет получить ключ, длину или размер в зависимости от конструкции.
Возможно ли, тем не менее, зацикливать записи каким-то образом?
Если нет.. как консоль Chrome делает это?
4 ответа
Возможно ли, тем не менее, зацикливать записи каким-то образом?
Нет, как вы говорите, содержимое WeakMap
не доступны по дизайну, и нет повторяемости.
Если нет... как консоль Chrome делает это?
Консоль использует API отладки движка JS, который обеспечивает доступ к внутренним объектам (также для обещания состояний, обернутых примитивов и т. Д.) И многим другим.
Дело движется, и скоро станет возможным создавать повторяющиеся недельные карты благодаря слабым ссылкам. См. Повторяющийся пример WeakMap в предложении tc39 weakrefs.
(обратите внимание, что это уже возможно с nodejs v12.?.? используя --harmony-weak-refs
флаг)
Вы можете использовать этот маленький фрагмент для универсального forEach. Однако он использует lodash и может быть значительно улучшен.
import lodashForEach from 'lodash/forEach';
import entries from 'lodash/entries';
/**
* @since lodash@4.17.3
* @param {Array|Map|Object|Set|WeakMap|WeakSet} iterable
* @param {Function} iteratee
* @example
* const iterable = new Map();
* iterable.set('somekey1', 1);
* iterable.set('somekey2', 2);
* // or
* const iterable = new Set();
* iterable.add("entry1");
* iterable.add("entry2");
* // run function
* forEach(iterable, (value, key) => {
* console.group('-');
* console.info('KEY');
* console.log(key);
* console.info('VALUE');
* console.log(value);
* console.groupEnd();
* });
*/
function forEach(iterable, iteratee) {
lodashForEach((iterable instanceof Set || iterable instanceof WeakSet) ? Array.from(iterable) : entries(iterable), (entry, index, collection) => {
iteratee(entry[1], entry[0], collection, index);
});
return iterable;
}
export default forEach;
ОБНОВИТЬ
После долгого времени я нашел этот ответ неверным. Дело в том, что в старой версии библиотеки polyfill core-js исправлена ошибка, из-за которой она заменяла собственную реализацию WeakMap своей собственной. Поэтому мое решение не подходит для современных браузеров.
function forEach(iterable, iteratee) {
switch (true) {
case Array.isArray(iterable) || iterable instanceof Map:
iterable.forEach(iteratee);
break;
case iterable instanceof Set:
Array.from(iterable).forEach(iteratee);
break;
case iterable instanceof WeakMap || iterable instanceof WeakSet:
// some kind of warning
break;
default:
Object.keys(iterable).forEach((key) => iteratee.call(null, iterable[key], key));
break;
}
return iterable;
}
Забегая далеко с квалификатором в вашем (2015) вопросе, а именно:
Можно ли каким-то образом перебрать записи?
Да.
В одной нелепой ситуации можно сымитировать, а затем перебрать ключи и значения WeakMap, а также сделать правильную независимую копию WeakMap.
Если WeakMap, который вы хотите клонировать, был построен очень специфическим образом с помощью функции-конструктора, вы можете сделать это:
// Define a Constructor-Function
// that makes objects
// containing WeakMaps:
function makeWeakMapObject(){
this.wm1 = new WeakMap();
this.o1 = {};
this.o2 = {"orange":"orange"};
this.wm1.set(this.o1, 37);
this.wm1.set(this.o2, 'azerty');
}
// Construct a new object:
let constructedWeakMapObject = new makeWeakMapObject();
// Then set a new key-value pair
// on the WeakMap in your object;
// because, ya know, otherwise you'd
// just reuse the WeakMap constructor
// and wouldn't need to clone :D
constructedWeakMapObject.added = {"ya":"glad"};
constructedWeakMapObject.wm1.set(constructedWeakMapObject.added, 42);
// In preparation to clone your newly constructed object,
// get that newly constructed object's property descriptors:
let props = Object.getOwnPropertyDescriptors(constructedWeakMapObject);
// Have a gander at those props; just for fun:
console.log({"props":props});
// Attempt to clone the constructedWeakMapObject
// using its ownPropertyDescriptors
let weakClone = new cloneWeak(props);
// and then check out what you made:
console.log({"weakClone":weakClone});
// Verify that you've made an independent clone
// (even though this example is shallow)
// by altering the WeakMap in your weakClone:
weakClone.wm.delete(weakClone.o1);
// Make sure your clone was altered:
console.log(weakClone.wm.get(weakClone.o1));
// And then check to see that the
// changes to your clone
// don't appear on your constructed object:
console.log(constructedWeakMapObject);
console.log(constructedWeakMapObject.wm1.get(constructedWeakMapObject.o1));
// A support function to help you use fresh keys in your cloned WeakMap to keep it independent from your original WeakMap
function cloneObject(obj) { // use something more robust, like underscore: _.cloneDeep(obj); actually, you'll likely have to roll your own so you can make clones of functions... anywho
var clone = {};
for(var i in obj) {
if(typeof(obj[i])==="object" && obj[i] !== null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
// Called as a constructor function w/arguments
function cloneWeak(inco){ // a bit wonky, at least in the middle
this.wm = new WeakMap();
let tempMap;
for(key in inco){
// Build keys on 'this' that match the incoming keys
if(Object.prototype.toString.call(inco[key].value) !== "[object WeakMap]"){
this[key] = cloneObject(inco[key].value);
}
// Reference the incoming map from your temp map
// (this makes the following loop possible)
else{tempMap = inco[key].value;}
}
this.fakeForHack = {}; // no idea why this works
this.wm.set(this.fakeForHack, "ok"); // no idea why this works... without it, the WeakMap entry for made.wm1.get(made.added) won't transfer -> ???
for(key in inco){
if(Object.prototype.toString.call(inco[key].value) !== "[object WeakMap]"){
// Set values for 'this' WeakMap:
this.wm.set(this[key], tempMap.get(inco[key].value));
}
}
}
Это уродливо, хрупко и решает только нелепый крайний случай; пожалуйста!