我想要:
document.createElement('div') //=> true
{tagName: 'foobar something'} //=> false
在我自己的脚本中,我曾经只使用这个,因为我从来不需要tagName作为属性:
if (!object.tagName) throw ...;
所以对于第二个目标,我想出了下面的快速解决方案——这基本上是有效的。;)
问题是,它依赖于浏览器强制执行只读属性,而并非所有浏览器都这样做。
function isDOM(obj) {
var tag = obj.tagName;
try {
obj.tagName = ''; // Read-only for DOM, should throw exception
obj.tagName = tag; // Restore for normal objects
return false;
} catch (e) {
return true;
}
}
有好的替代品吗?
大多数答案使用某种鸭子类型,例如检查对象是否具有nodeType属性。但这还不够,因为非节点也可以具有类节点属性。
另一种常见的方法是instanceof,它会产生误报,例如Object.create(Node),尽管继承了节点属性,但它不是节点。
此外,上述两种方法都调用内部基本方法,这可能会有问题,例如,如果测试的值是一个代理。
相反,我建议借用一个节点方法并在我们的对象上调用它。浏览器可能会通过查看代理中不可自定义的内部槽来检查该值是否是一个节点,因此即使它们也无法干扰我们的检查。
function isNode(value) {
try {
Node.prototype.cloneNode.call(value, false);
return true;
} catch(err) {
return false;
}
}
如果您愿意,还可以使用属性getter。
函数isNode(value) {
尝试{
Object.getOwnPropertyDescriptor (Node.prototype nodeType) .get.call(价值);
返回true;
} catch(err) {
返回错误;
}
}
类似地,如果您想测试一个值是否是一个元素,您可以使用
function isElement(value) {
try {
Element.prototype.getAttribute.call(value, '');
return true;
} catch(err) {
return false;
}
}
function isHTMLElement(value) {
try {
HTMLElement.prototype.click.call(value);
return true;
} catch(err) {
return false;
}
}
上面和下面的所有解决方案(包括我的解决方案)都有可能是不正确的,特别是在IE上——(重新)定义一些对象/方法/属性来模拟DOM节点,使测试无效是很可能的。
所以我通常使用duck-typing风格的测试:我专门测试我使用的东西。例如,如果我想克隆一个节点,我这样测试它:
if(typeof node == "object" && "nodeType" in node &&
node.nodeType === 1 && node.cloneNode){
// most probably this is a DOM node, we can clone it safely
clonedNode = node.cloneNode(false);
}
基本上,它是一个小的完整性检查+对我计划使用的方法(或属性)的直接测试。
顺便说一句,上面的测试对于所有浏览器上的DOM节点来说都是一个很好的测试。但是如果您想要安全起见,请始终检查方法和属性的存在并验证它们的类型。
编辑:IE使用ActiveX对象来表示节点,因此它们的属性行为不像真正的JavaScript对象,例如:
console.log(typeof node.cloneNode); // object
console.log(node.cloneNode instanceof Function); // false
而它应该分别返回“function”和true。测试方法的唯一方法是查看是否定义了方法。