A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

DOMStringMap.prototype extension

Edwin_Reynoso
2015-05-05

I apologize if this is the wrong place to discuss this.

Can we make this happen (better reading), I tried fixing indentation on here):

DOMStringMap.prototype.__proto__ = {
keys() {
	let keys = Object.keys(this),
	count = 0;
	return {
		next() {
			if(count == keys.length) return { value: undefined, done: true };
			return { value: keys[count++], done: false };
		}
	}
},
values() {
	let values = Object.keys(this).map(key => this[key], this),
	count = 0;
	return {
		next() {
			if(count == values.length) return { value: undefined, done: true };
			return { value: values[count++], done: false };
		}
	}
},
entries() {
	let keys = Object.keys(this),
	values = keys.map(key => this[key], this),
	count = 0;
	return {
		next() {
			if(count == keys.length) return { value: undefined, done: true };
			return { value: { key: keys[count], value: values[count++] }, done: false };
		}
	}
},
[Symbol.iterator]: DOMStringMap.prototype.values //Or perhaps .entries
}

Now the [Symbol.iterator]: DOMStringMap.prototype.values is to make it iterable in for of loops.

Can someone please explain how the following works:

for(var c of document.body.classList) console.log(c);

Meaning how is the above possible if it’s a DOMStringList and DOMStringList.prototype[Symbol.iterator] is undefined.

Yet the above doesn’t work with DOMStringMap example would be document.body.dataset

tabatkins
2015-05-05

Most likely, DOMStringList isn’t supposed to work in an iterator context, per spec, but whatever browser you’re testing in implemented it anyway, since it’s an array-like and iteration behavior is obvious.

DOMStringMap could probably be given default behavior as you suggest. Adding things to the prototype is dangerous, though - object-as-map means that anything you expect to see on the prototype might be shadowed by a value on the instance, which is why they’re a bad idea. But you can certainly safely add a symbol-valued property like Symbol.iterator, and give it one of the behaviors by default (whatever Map uses as its default behavior, if it has one).

Edwin_Reynoso
2015-05-05

though - object-as-map means that anything you expect to see on the prototype might be shadowed by a value on the instance, which is why they’re a bad idea.

Why would that be bad if I want every instance to be like that. That’s why I set it on the prototype (Ik how prototypes work in js)

Every dataset of every element should have the above.

As far as the DOMStringList it’s iterable through for of. Shouldn’t that only be for anything with [Symbol.iterator] on it’s prototype. Like Array.prototype[Symbol.iterator] and String.prototype[Symbol.iterator] is not undefined. Which means for of should work. Now DOMStringList[Symbol.iterator] is undefined, which should mean for of should NOT work. (or am I mssing something)

I want to know why and how is that working on chrome.

rwaldron
2015-05-13

What happens when you have an element with attributes called data-entries, data-keys or data-values? DOMStringMap is a garbage design that creates its “storage” by growing expando properties. Check out what happens:

DOMStringMap.prototype.entries = function * () {
  let keys = Object.getOwnPropertyNames(this);

  while (keys.length) {
    let key = keys.shift();
    yield [key, this[key]];
  }
};


document.body.setAttribute("data-foo", 1);


var entries = document.body.dataset.entries();

for (var [k, v] of entries) {
  console.log(k, v); // "foo" "1"
}


document.body.setAttribute("data-entries", "ohhhhhh shit!");


var entries = document.body.dataset.entries();

// TypeError: document.body.dataset.entries is not a function

Whoops!

That will happen for this as well:

<body data-entries="whatever">

However, as Tab said, using Symbol.iterator is safe:


Object.defineProperty(DOMStringMap.prototype, Symbol.iterator, {
  value: function * () {
    let keys = Object.getOwnPropertyNames(this);

    while (keys.length) {
      let key = keys.shift();
      yield [key, this[key]];
    }
  }
});

for (var [k, v] of document.body.dataset) { 
  console.log(k, v); 
}

// "foo" "1"
// "entries" "ohhhhhh shit!"
Edwin_Reynoso
2015-05-14

Oh wow I didn’t even think of that, that’s so true, um just a thought what if those methods exist, but when you have data-entries they get overridden. That’s probably a really really bad DOM API which were trying to get away from.

Also I just figured out why DOMStringList is iterable each DOMStringList has [Symbol.iterator] as a own property, why not on the DOMStringList.prototype?

Thanks guys

rwaldron
2015-05-14

Looks like Firefox has it implemented on the prototype:

(That’s in Firefox Developer Edition)

Edwin_Reynoso
2015-05-14

Looks like chrome doesn’t should I file a issue? It should be on the prototype.

domenic
2015-05-14

Be sure to test Chrome Canary before filing. Moving things to the prototype has been a recent thing in Chrome.

Edwin_Reynoso
2015-05-14

Yes I’m aware, thanks @domenic and @rwaldron yup same thing in canary. Needs to be moved to the prototype. Should I post the issue here: https://code.google.com/p/chromium/issues/list