微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

阅前先知

* 能够在保留 <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...

- END -

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐