Skip to content

Instantly share code, notes, and snippets.

@domenic

domenic/1MyDOMPoint.js

Last active Aug 29, 2015
Embed
What would you like to do?
Subclassing DOM objects via @@create
// Using ES5 syntax to avoid any confusion.
function makeSlots(obj, slotNames) { /* native code */ }
function setSlot(obj, name, value) { /* native code */ }
function getSlot(obj, name) { /* native code */ }
function ensureBranded(obj) {
if (getSlot(obj, 'domPointer') !== 'DOM_POINTER_FOR_MY_DOM_POINT') {
throw new TypeError('Incompatible method or accessor call: ' +
'the this value is not a MyDOMPoint');
}
}
function MyDOMPoint(x, y) {
setSlot(this, 'x', x);
setSlot(this, 'y', y);
}
MyDOMPoint[Symbol.create] = function () {
var obj = allocateNativeObjectWithSlots(['domPointer', 'x', 'y'])
setSlot('domPointer', 'DOM_POINTER_FOR_MY_DOM_POINT');
return obj;
}
MyDOMPoint.prototype = {
constructor: MyDOMPoint,
get x() {
// Non-generic, like most DOM things today
ensureBranded(this);
return getSlot(this, 'x');
},
get y() {
// Non-generic, like most DOM things today
ensureBranded(this);
return getSlot(this, 'y');
},
norm: function () {
// This is generic, unlike most DOM things today.
// We could make it non-generic by adding a brand check, but that's not necessary.
// See the [Generic] annotation in the WebIDL that says to omit the brand check.
return Math.hypot(this.x, this.y);
}
}
[Constructor(unrestricted double x, unrestricted double y)]
interface MyDOMPoint {
readonly attribute unrestricted double x;
readonly attribute unrestricted double y;
[Generic] unrestricted double norm();
}
// [Generic] would be an addition to WebIDL that says to remove any brand checks,
// as the spec author has ensured that the prose of the algorithm only accesses publicly-accessible
// properties and methods.
// Using ES5 syntax to avoid any confusion.
// This is a user subclass of MyDOMPoint.
function MyAwesomeDOMPoint(x, y, awesomeness) {
MyAwesomeDOMPoint.call(x, y);
this.awesomeness = awesomeness;
}
// This is important; it causes MyAwesomeDOMPoint[Symbol.create] === MyDOMPoint[Symbol.create]
MyAwesomeDOMPoint.__proto__ = MyDOMPoint;
MyAwesomeDOMPoint.prototype = {
__proto__: MyDOMPoint.prototype,
constructor: MyAwesomeDOMPoint,
alertAwesomeness: function () {
alert('I\'m this awesome: ' + this.awesomeness);
}
};
// Everything still works:
var adp = new MyAwesomeDOMPoint(1, 2, 'super awesome');
console.log(adp.x, adp.y, adp.awesomeness); // 1, 2, 'super awesome'
// In more detail:
assert(getSlot(adp, 'x') === 1);
// Because MyAwesomeDOMPoint inherited MyDOMPoint[Symbol.create], so all the slots are still there.
// And the slot values were set correctly by `MyAwesomeDOMPoint.call(this, x, y)`, as was the branding:
var getX = Object.getOwnPropertyDescriptor(MyDOMPoint.prototype, 'x').get;
assert(getX(adp) === 1);
assert(getX(new MyDOMPoint(-1, -2)) === -2);
assert.throws(function () { getX({}); }, TypeError);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment