JS023. 不用JSON将Object与Array类型转换为字符串

阅前先知

* 能够在保留 <Boolean> (false) 的前提下过滤 <undefined> 与 <null> 类型。

* 无法识别 <Symbol> 与 <Function> 类型,这些类型将会与 <undefined> 、<null> 一样会被忽略。

效果预览

JS023. 不用JSON将Object与Array类型转换为字符串

代码部分

/**
* [对象 -> 字符串]
* @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})">` 并点击触发时:

JS023. 不用JSON将Object与Array类型转换为字符串

查看 DOM 树:

JS023. 不用JSON将Object与Array类型转换为字符串

可预见的, 该参数仅仅被 innerHTML 转译成成了一段字符串,如下示例:

JS023. 不用JSON将Object与Array类型转换为字符串

那么当我们想在 shadowDOM 上绑定自定义属性时,便难以将其传递给该函数的形参。

我们知道 onclick 事件的 this 会指向该 DOM 的结点,而不是继承了 HTMLElement 的 Class,无法获取类内部绑定的变量。

这时我尝试将 onclick 函数中传递的参数改为  `<div onclick="myFn({ msg: 'success!' })">`,浏览器反而能成功返回:

JS023. 不用JSON将Object与Array类型转换为字符串

可以看出在 innerHTML API 中引用 Function 并传入实参本身就不被建议与支持,也许现在我们更加了解浏览器与 DOM 了。

但当我想要完整的传入一个对象 / 数组变量时,可能就要将其转换为字符串后传入,才可被浏览器解析与识别:

JS023. 不用JSON将Object与Array类型转换为字符串

JS023. 不用JSON将Object与Array类型转换为字符串

但如果无法改变 Class 内部的 props 变量,这样仅仅取值而不通知且无法改变宿主的拿来主义,反而让ShadowDOM变得更局限。

反而需要另一个普通 Class 作为中继器来完成更多的处理,这并不能发挥 Class 的全部作用,这是一个非常浪费的行为,也是框架开发者们拥抱 Functional Component 而非 Class Component 的主要理由,当然大多框架的实现与 shadowDOM 无关,如 Vue 的 virtualDOM 与 React 的 JSX 语法,shadowDOM 只是更便于原生 JS 实现组件化的手段之一。

由此可得在编写 ShadowDOM 组件时,应多利用 createElement 这样的 API 而非 innerHTML...

- END -

上一篇:生成唯一编号(序列号)--sql存储过程


下一篇:Photoshop 一只漂亮的枯叶蝶