lodash 的 isElement 等价于纯 JS

lodash's isElement equivalent in pure JS

现在我正在使用 isElement by lodash 检查一个元素是否 DOM 可能。我想摆脱这个库,所以我正在寻找这个功能的纯 JS 实现。

我正在使用它,但我不知道它是否是正确的实现以及我是否忽略了任何边缘情况:

const isDOMElement = el => el instanceof HTMLElement

更新

我已经测试过(codepen link) the four proposed implementation, in all cases is working as expected, maybe the one proposed by @cesare covers most of the cases, but also the one proposed by @peter-seliger is clever and work with an "outside of the BOX" thinking. Finally, both @David Thomas 并且我第一次使用的那个适用于大多数情况。

总而言之,我认为,正如 David Thomas 指出的那样 It's just a matter of preference

P.S:测试用到的标签摘自MDN的HTML elements reference

P.S2:不确定如何进行以及接受什么答案。 cesare 和 peter 都有一个很好的观点

从库源代码看实现:

https://github.com/lodash/lodash/blob/master/isElement.js

这是 lodash 实用程序:

function isElement(value) {
  return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value)
}

基本上 isObjectLike 它检查传递的值是一个非 null 对象(因为 null 是 js 中的一个对象)并且 !isPlainObject 它不是一个普通对象(因为它可能是一个带有 "nodeType": 1 条目的对象,并且因为 HTMLElement 实例具有嵌套原型。

isPlainObject 实用程序是这样的:

function isPlainObject(value) {
  if (!isObjectLike(value) || getTag(value) != '[object Object]') {
    return false
  }
  if (Object.getPrototypeOf(value) === null) {
    return true
  }
  let proto = value
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  return Object.getPrototypeOf(value) === proto
}

如您所见,第一次检查是多余的,因为它再次检查 isObjectLike,但我认为主要的警告是它不涵盖其他对象情况,因为例如“数组”是对象也因此他们通过了检查:

const arr = [1, 2]; // array
arr.nodeType = 1; // add nodeType property to the array object
console.log(isElement(arr)); // true Lodash

自己试一下。

我认为检查对象是否具有继承的 nodeType 属性 更安全:

const _isElement = (node) =>
    typeof node === 'object' &&
    node !== null &&
    !node.hasOwnProperty('nodeType') &&
    node.nodeType &&
    node.nodeType === 1
  

const el = document.createElement('h1'); // node
const arr = [1, 2]; // array
arr.nodeType = 1; // add nodeType property to the array object

console.log(isElement(arr)); // true Lodash
console.log(isElement(el)); // true Lodash

console.log(_isElement(arr)) // false
console.log(_isElement(el)) // true

无论如何,我更喜欢使用你的检查,它已经涵盖了大部分检查,因为任何原始非对象都不是 HTMLElement 的实例,null 不是 HTMLElement 的实例,并且 HTMLElement 实例具有“nodeType”属性,但它继承自 proto 而不是自己的 属性, :

const isDOMElement = el => el instanceof HTMLElement
//or
const isDOMElement = el => el instanceof Node

大家可以试试...

function exposeImplementation(value) {
  return Object
    .prototype
    .toString
    .call(value);
}
function getInternalClassName(value) {
  return (/^\[object\s+(?<className>[^\]]+)\]$/)
    .exec(
      exposeImplementation(value)
    )
    ?.groups
    ?.className;
}
function isHTMLElement(value) {
  return !!value && (/^HTML(?:[A-Z][A-Za-z]+)?Element$/)
    .test(
      String(
        getInternalClassName(value)
      )
    );
}

console.log(
  getInternalClassName(document.createElement('h1')),
  isHTMLElement(document.createElement('h1'))
);
console.log(
  getInternalClassName(document.body),
  isHTMLElement(document.body)
);
console.log(
  getInternalClassName(document),
  isHTMLElement(document)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }