我想知道我是否可以创建HTMLDivElement的子类。像这样。
MyDivElement.prototype.pickColor = function()
{
return this.picked;
}
function MyDivElement()
{
this = new HTMLDivElement();
this.picked = 'unknowd';
}
alert(this.picked); // print: 'unkowd'
这是(类似的)这可能吗? 如果没有,实现这一目标的最佳方法是什么?
答案 0 :(得分:6)
在__proto__
公开且可变的浏览器中,您可以使用子类DOM元素。它看起来像这样:
function CustomEl () {
var el = document.createElement('div')
el.__proto__ = CustomEl.prototype
return el
}
CustomEl.prototype.__proto__ = HTMLDivElement.prototype
我还在jsFiddle上详细介绍了它。不幸的是,虽然IE和Opera不允许__proto__
访问,并且据我所知,将来也没有计划。
答案 1 :(得分:2)
new HTMLDivElement();
在Chrome中引发了TypError "Illegal constructor"
- 所以这是不可能的。
更新:我已在其他当前浏览器中测试过,它们会抛出各种类型的错误 - 但它们都会抛出。
实际上,这可行:
function MyDivElement() {
this.picked = 'unknowd';
}
MyDivElement.prototype = document.createElement('div');
var mydiv = new MyDivElement();
但我不确定你如何使用这种模式...
答案 2 :(得分:1)
在某些浏览器中,您可以扩展原型,在其他浏览器中,可以扩展原型。我会让你猜你不能的。 :-)这与扩展DOM元素并不完全相同,但它确实允许你做一些你可能想要那个设施的东西的子集。问题是,DOM元素不是真正的JavaScript实体;它们只是运行时系统提供的模拟。 (也许有一天,所有的jsdom工作都会实现。)
好吧我会告诉你有问题的浏览器:IE根本不喜欢它。然而其他人呢。如果你曾经看过Prototype库,那么当你忘记Prototype-ize一个DOM元素引用的时候,你会偶然发现这个事实的表现,通过令人烦恼的IE-only bug。
(IE9可能会有所不同,但我对此表示怀疑。)
这是在jsfiddle测试过的简单易事的事情。
答案 3 :(得分:1)
我正在试验这一点。一个很大的困难是您需要文档的上下文来创建元素。您可以使用window.document
作为默认设置,但这很无聊。
这是我正在进行的POC:
function CustomNode(type, parent) {
if (type instanceof Node) {
// Decorate a preexisting node appropriately if called that way.
if (arguments.length === 2 && type.ownerDocument !== parent) {
// Import the node if it's not owned by the requested document.
type = parent.importNode(type, true);
}
return Object.assign(type, this.__proto__);
}
//Normal flow, e.g., new CustomNode("div");
var d = document;
if (parent) {
// Alt document flow, e.g., new CustomNode("div", xmlDoc);
if (parent.nodeType === 9) {
d = parent;
} else {
// Support for new CustomNode("div", parentElement);
// This doesn't append the element, just makes sure
// the documents match
d = parent.ownerDocument;
}
}
var inst;
// Creation flags
if (type[0] === '#') { //text
inst = d.createTextNode(type.substr(1));
} else if (type[0] === '?') { //Processing instruction
type = type.substr(1).split(' ');
inst = d.createProcessingInstruction(type.shift(), type.join(' '));
} else if (type[0] === '[') { // CDATA
inst = d.createCDATASection(type.substr(1));
} else if (type[0] === '/') { // Comment
inst = d.createComment(type.substr(1));
} else { //Everything else gets an element.
inst = d.createElement(type);
}
// DE-COR-ATE
Object.assign(inst, this.__proto__);
return inst;
}
// Decorator for customized NodeLists; probably inefficient. Decorates
// contents with CustomNode
function CustomNodeList(list) {
var Self = this.constructor,
CNode = this.Node;
return Object.assign([].map.call(list, function (node) {
return new CNode(node);
}), this.__proto__);
}
CustomNodeList.prototype = {
// so we know how to wrap...
Node: CustomNode,
text: function () {
return this[0].textContent;
}
};
CustomNode.prototype = {
// So we know how to decorate NodeLists
NodeList: CustomNodeList,
// So we know how to decorate Nodes
Node: CustomNode,
// Easy make-and-attach
new: function (type, attach) {
var CNode = this.Node;
var ret = new CNode(type, this.ownerDocument);
if (attach) {
this.appendChild(ret);
}
return ret;
},
// NodeLists with ES5 iterators!
find: function () {
var CNodeList = this.NodeList;
return new CNodeList(this.querySelectorAll.apply(this, arguments));
},
kids: function () {
var CNodeList = this.NodeList;
return new CNodeList(this.childNodes);
}
};
介意,这可能是一个坏主意:这些函数的相同范围内的所有内容(包括元素本身)将永远不会收集垃圾,因为大多数浏览器中的GC都是愚蠢的它涉及引用对象的元素。因为这个原因,我永远不会将它用于制作:它是一种直接的内存泄漏。