Last active
April 27, 2022 21:04
-
-
Save dfkaye/7f8eb53f14f04a03507126ee5b3c33d1 to your computer and use it in GitHub Desktop.
Make a custom data structure with non-enumerable iterators.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 21 March 2022 | |
// Custom data structure with non-enumerable iterators. | |
// 22 March 2022: make Data, not Iterable, the constructor: | |
// - callable without the `new` keyword | |
// - pass instance to Iterable | |
// - decorate instance with iterators | |
// - merge data | |
// 27 April 2022 | |
// - pull up Iterable decoration into the Data constructor. | |
// - pull out cruft and comments to custom-data-structure.js | |
// for posterity. | |
/* factory version */ | |
function Data(data) { | |
if (!(this instanceof Data)) { | |
return new Data(data) | |
} | |
data = Object(data) | |
return Object.assign(this, data) | |
} | |
// Iterator methods | |
function entries() { | |
return Object.entries(this); | |
} | |
function values() { | |
return Object.values(this) | |
} | |
function keys() { | |
return Object.keys(this) | |
} | |
function toString() { | |
return `[object ${this.constructor.name}]` | |
} | |
// Descriptors for the iterator methods: | |
// - all readonly, non-deletable, non-enumerable. | |
var descriptors = { | |
[Symbol.iterator]: { | |
value: function () { | |
var entries = Object.entries(this); | |
var i = 0; | |
return { | |
next: function () { | |
return { | |
value: entries[i], | |
done: i++ >= entries.length | |
} | |
} | |
} | |
} | |
}, | |
entries: { value: entries }, | |
values: { value: values }, | |
keys: { value: keys }, | |
toString: { value: toString } | |
}; | |
// Decorate the prototype with iterable method descriptors. | |
Object.defineProperties(Data.prototype, descriptors); | |
/* test it out */ | |
var object = { | |
name: 'base', | |
value: Date.now(), | |
mission: 'test' | |
}; | |
var data = Data(object); | |
console.log( Data(data) === data ); | |
console.log( Data(object) != Data(object) ); | |
console.log( data.entries() ); | |
console.log( data.values() ); | |
console.log( data.keys() ); | |
console.warn( data.toString() ); | |
for (var [k,v] of data) { console.log([k,v]) } | |
for (var [k,v] of data.entries()) { console.log([k,v]) } | |
for (var v of data.values()) { console.log(v) } | |
for (var k of data.keys()) { console.log(k) } | |
/* prototype iteration tests */ | |
// All properties in data's prototype (Iterable) are non-enumerable, | |
// so these should not print anything. | |
var proto = Object.getPrototypeOf(data); | |
var noop = (p) => console.error("should not see this: ", p) | |
for (var p in proto) { noop(p) } | |
proto.entries().forEach(p => noop(p)) | |
proto.values().forEach(p => noop(p)) | |
proto.keys().forEach(p => noop(p)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 20 March 2022 | |
// Custom data structure with non-enumerable iterators. | |
// return [key, value] entries | |
function entries() { | |
return Object.entries(this) | |
} | |
// bind entries() to data prototype's iterator methods | |
var o = Object.defineProperties({}, { | |
[Symbol.iterator]: { value: entries }, | |
entries: { value: entries } | |
}); | |
// data with iterators as prototype | |
var data = Object.create(o); | |
// merge new data changes | |
Object.assign(data, {name: "test"}); | |
// iterate data | |
var r = [] | |
for (var [k, v] of data.entries()) { | |
r.push([k,v]) | |
} | |
console.warn(r); | |
// Array (2) | |
// 0: Array [ "name", "test" ] | |
// length: 1 | |
// show us our iterators in the prototype | |
Object.getPrototypeOf(data); | |
// Object { … } | |
// entries: function entries() | |
// Symbol(Symbol.iterator): function entries() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 21 March 2022 | |
// Custom data structure with non-enumerable iterators. | |
// 22 March 2022: make Data, not Iterable, the constructor: | |
// - callable without the `new` keyword | |
// - pass instance to Iterable | |
// - decorate instance with iterators | |
// - merge data | |
// 27 April 2022 | |
// - pull up Iterable decoration into the Data constructor. | |
/* factory version */ | |
function Data(data) { | |
if (!(this instanceof Data)) { | |
return new Data(data) | |
} | |
data = Object(data) | |
// if (data instanceof Iterable) { | |
// return data | |
// } | |
// var iterable = new Iterable(this) | |
// Object.defineProperty(iterable, "constructor", { | |
// value: Data | |
// }); | |
// bind iterator descriptor properties to this instance | |
// Object.defineProperties(this, descriptors); | |
// return object with iterator interface as prototype | |
// var iterable = Object.create(this); | |
return Object.assign(this, data) | |
} | |
// Interface containing iterator methods | |
// Interface decorates data object with iterator methods | |
/* | |
function Iterable(data) { | |
// if (!(this instanceof Iterable)) { | |
// return new Iterable | |
// } | |
// bind iterator descriptor properties to this instance | |
Object.defineProperties(data, descriptors); | |
// return object with iterator interface as prototype | |
return Object.create(data); | |
} | |
*/ | |
// Iterator methods | |
function entries() { | |
return Object.entries(this); | |
} | |
function values() { | |
return Object.values(this) | |
} | |
function keys() { | |
return Object.keys(this) | |
} | |
// Some metadata | |
function toString() { | |
return `[object ${this.constructor.name}]` | |
} | |
// Descriptors for the iterator methods, all readonly, non-deletable, non-enumerable. | |
var descriptors = { | |
[Symbol.iterator]: { | |
value: function () { | |
var entries = Object.entries(this); | |
var i = 0; | |
return { | |
next: function () { | |
return { | |
value: entries[i], | |
done: i++ >= entries.length | |
} | |
} | |
} | |
} | |
}, | |
entries: { value: entries }, | |
values: { value: values }, | |
keys: { value: keys }, | |
toString: { value: toString } | |
}; | |
// Put descriptors on the prototype | |
Object.defineProperties(Data.prototype, descriptors); | |
/* test it out */ | |
var object = { | |
name: 'base', | |
value: Date.now(), | |
mission: 'test' | |
}; | |
var data = Data(object); | |
console.log( Data(data) === data ); | |
console.log( Data(object) != Data(object) ); | |
console.log( data.entries() ); | |
console.log( data.values() ); | |
console.log( data.keys() ); | |
console.warn( data.toString() ); | |
for (var [k,v] of data) { console.log([k,v]) } | |
for (var [k,v] of data.entries()) { console.log([k,v]) } | |
for (var v of data.values()) { console.log(v) } | |
for (var k of data.keys()) { console.log(k) } | |
/* prototype iteration tests */ | |
// All properties in data's prototype (Iterable) are non-enumerable, | |
// so these should not print anything. | |
var proto = Object.getPrototypeOf(data); | |
var noop = (p) => console.error("should not see this: ", p) | |
for (var p in proto) { noop(p) } | |
proto.entries().forEach(p => noop(p)) | |
proto.values().forEach(p => noop(p)) | |
proto.keys().forEach(p => noop(p)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment