You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS类构造函数返回字符串显示[object Object]?SPA组件解析问题求助

问题背景

我正在学习SPA的常见工作机制(router、state、route等),已实现基础hash router并能在fragment元素中展示内容,现在尝试开发类React风格的自定义组件,遇到{{}}占位符替换的问题:从类构造函数返回时,使用[...].join("")或直接取数组元素会被解析为[object Object],虽找到临时解决方法,但无法理解原因。

代码场景

为避免干扰嵌套组件,我采用DFS遍历组件子节点,并将解析逻辑委托给_Fo_Component_PlaceholderParser子类,相关处理代码:

if (current && current?.getAttribute) {
    current.innerHTML = new _Fo_Component_PlaceholderParser({ node: current, content: current.innerHTML })
}

_Fo_Component_PlaceholderParser类核心代码:

class _Fo_Component_PlaceholderParser {

    constructor({ node, content }) {
         this.node = node;
         return this.placeholderParser(content);
    }

     placeholderParser(value) {
        if(value.trim === "") {
            return value
        }
        
        const parts = value.split(/(<\/?\w+[^>]*>)/g);
        const stack = [];
        let output = [];
        for (let part of parts) {
            const trimmedPart = part.trim();
            if (/^<\w+[^>]*>$/.test(trimmedPart)) {
                stack.push(trimmedPart);
                const processedTag = stack.length === 1 ? trimmedPart.replace(/\{\{.*?\}\}/g, "testing") : trimmedPart;
                output.push(String(processedTag));
                if (this.isSelfClosing(trimmedPart)) stack.pop();
            } else if (/^<\/\w+>$/.test(trimmedPart)) {
                stack.pop();
                output.push(String(trimmedPart));
            } else if (/\{\{.*?\}\}/.test(trimmedPart) && stack.length === 0) {
                output.push(String(trimmedPart.replace(/\{\{.*?\}\}/g, "testing")));
            } else if (trimmedPart.length > 0) {
                output.push(String(trimmedPart));
            }
        }
        return output.join("");
    }
// rest of the method ...
}
问题现象

该类构造函数直接返回this.placeholderParser()的结果(即output.join("")),但DOM中显示为[object Object],尽管日志显示解析数据正确。若直接返回output数组,DOM中能正常显示逗号分隔的内容;即使将output硬编码为["test", "it works"],返回output.join("")仍显示[object Object],返回output[0]也一样。仅当返回output数组并在外部调用.join("")时才正常:

if (current && current?.getAttribute) {
    current.innerHTML = new _Fo_Component_PlaceholderParser({ node: current, content: current.innerHTML }).join("")
}
求助问题
  1. 为何从类构造函数返回output.join("")时,DOM中显示[object Object]?
  2. 这是否与JavaScript处理类构造函数返回值的机制有关?
  3. 正确的修复方式是什么?

解答

  1. 原因:JavaScript类的构造函数有特殊返回规则——如果构造函数返回的是原始值(字符串、数字、布尔等),这个返回值会被忽略,最终new操作符会返回类的实例对象。你在构造函数里返回字符串output.join(""),但new _Fo_Component_PlaceholderParser(...)实际返回的还是类的实例,当把这个实例赋值给current.innerHTML时,会调用实例默认的toString()方法,返回[object Object],所以DOM显示该结果。

  2. 确实和构造函数返回机制有关:构造函数的返回逻辑是:

    • 如果返回的是对象类型(包括数组、函数、类实例等),new操作符会直接返回这个对象;
    • 如果返回的是原始类型,则会忽略这个返回值,返回类的实例。
      你返回的字符串是原始类型,所以new操作返回类实例而非期望的字符串;返回数组时,数组是对象类型,new操作直接返回数组,因此外部调用.join("")能得到正确字符串。
  3. 正确修复方式:
    推荐两种方案:

    • 方案一:改用静态方法(最符合语义)
      将解析逻辑放到静态方法中,直接调用获取结果,无需实例化类:
      class _Fo_Component_PlaceholderParser {
          static parse({ node, content }) {
              if(content.trim === "") {
                  return content
              }
              // 复制原placeholderParser的逻辑
              const parts = content.split(/(<\/?\w+[^>]*>)/g);
              const stack = [];
              let output = [];
              for (let part of parts) {
                  const trimmedPart = part.trim();
                  if (/^<\w+[^>]*>$/.test(trimmedPart)) {
                      stack.push(trimmedPart);
                      const processedTag = stack.length === 1 ? trimmedPart.replace(/\{\{.*?\}\}/g, "testing") : trimmedPart;
                      output.push(String(processedTag));
                      if (this.isSelfClosing(trimmedPart)) stack.pop();
                  } else if (/^<\/\w+>$/.test(trimmedPart)) {
                      stack.pop();
                      output.push(String(trimmedPart));
                  } else if (/\{\{.*?\}\}/.test(trimmedPart) && stack.length === 0) {
                      output.push(String(trimmedPart.replace(/\{\{.*?\}\}/g, "testing")));
                  } else if (trimmedPart.length > 0) {
                      output.push(String(trimmedPart));
                  }
              }
              return output.join("");
          }
      
          static isSelfClosing(tag) {
              // 原isSelfClosing方法逻辑
          }
      }
      
      // 使用时直接调用静态方法
      if (current && current?.getAttribute) {
          current.innerHTML = _Fo_Component_PlaceholderParser.parse({ node: current, content: current.innerHTML })
      }
      
    • 方案二:返回对象包装的原始值(不推荐)
      比如返回new String(output.join("")),此时返回的是对象类型,new操作会返回这个字符串对象,赋值给innerHTML时会自动拆箱为原始字符串。但这种方式不符合类的设计语义,不推荐使用。

内容的提问来源于stack exchange,提问作者Mister HS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:13:12