阅前先知
* 能够在保留 <Boolean> (false) 的前提下过滤 <undefined> 与 <null> 类型。
* 无法识别 <Symbol> 与 <Function> 类型,这些类型将会与 <undefined> 、<null> 一样会被忽略。
效果预览
代码部分
/**
* [对象 -> 字符串]
* @param o [对象数据]
* @param o ({ key: value[] }: abled)
*/
function obj2str(o){
const result = []
for (const prop in o) {
if(hasValue(o[prop])) {
if(o[prop].length && o[prop] instanceof Array) {
result.push(`${prop}: ${arr2str(o[prop])}`)
} else {
switch (typeof o[prop]) {
case 'string':
result.push(`${prop}: '${o[prop]}'`)
break
case 'number':
result.push(`${prop}: ${o[prop] + ''}`)
break
case 'boolean':
result.push(`${prop}: ${o[prop].toString()}`)
break
case 'object':
result.push(`${prop}: ${obj2str(o[prop])}`)
break
}
}
}
}
return `{ ${result.join(', ')} }`
}
/**
* [数组 -> 字符串]
* @param a [数组数据]
* @param a ([{ key: value }]: abled)
*/
function arr2str(a) {
const result = []
a.forEach(item => {
if(hasValue(item)) {
if(item.length && item instanceof Array) {
arr2str(item)
} else {
switch (typeof item) {
case 'string':
result.push(`'${item}'`)
break
case 'number':
result.push(item + '')
break
case 'boolean':
result.push(item.toString())
break
case 'object':
result.push(obj2str(item))
break
}
}
}
})
return `[ ${result.join(', ')} ]`
}
/**
* [检验值是否存在]
* @param v [被检验值]
* @param v ([<boolean> (false): abled)
*/
function hasValue(v) {
let flag = !v && typeof v !== 'boolean'
return !flag
}
应用场景
我在使用 ES6: class something extends HTMLElement 编写 shadowDOM 组件时踩的坑。
当我使用 innerHTML 创建一个 HTML 树结点: `<div onclick="myFn(${arg})">` 并点击触发时:
查看 DOM 树:
可预见的, 该参数仅仅被 innerHTML 转译成成了一段字符串,如下示例:
那么当我们想在 shadowDOM 上绑定自定义属性时,便难以将其传递给该函数的形参。
我们知道 onclick 事件的 this 会指向该 DOM 的结点,而不是继承了 HTMLElement 的 Class,无法获取类内部绑定的变量。
这时我尝试将 onclick 函数中传递的参数改为 `<div onclick="myFn({ msg: 'success!' })">`,浏览器反而能成功返回:
可以看出在 innerHTML API 中引用 Function 并传入实参本身就不被建议与支持,也许现在我们更加了解浏览器与 DOM 了。
但当我想要完整的传入一个对象 / 数组变量时,可能就要将其转换为字符串后传入,才可被浏览器解析与识别:
但如果无法改变 Class 内部的 props 变量,这样仅仅取值而不通知且无法改变宿主的拿来主义,反而让ShadowDOM变得更局限。
反而需要另一个普通 Class 作为中继器来完成更多的处理,这并不能发挥 Class 的全部作用,这是一个非常浪费的行为,也是框架开发者们拥抱 Functional Component 而非 Class Component 的主要理由,当然大多框架的实现与 shadowDOM 无关,如 Vue 的 virtualDOM 与 React 的 JSX 语法,shadowDOM 只是更便于原生 JS 实现组件化的手段之一。
由此可得在编写 ShadowDOM 组件时,应多利用 createElement 这样的 API 而非 innerHTML...