【问题标题】:Javascript, how can I pass properties from method up to class constructor?Javascript,如何将属性从方法传递到类构造函数?
【发布时间】:2022-01-05 05:23:00
【问题描述】:

我正在开发一个普通的 JS / jQuery 应用程序,该应用程序从会话存储中获取数据并将其显示在浏览器中。我创建了一个“Box”类,它代表浏览器页面上的一个框(即 html 卡)。构造函数具有各种属性,如name: this.name 等。

有两种不同类型的框——组和主题——所以我创建了两个子类class GroupBox extends Boxclass SubjectBox extends Box

这是设计的工作方式:在初始页面加载时,我们调用函数displayGroupBoxes 来填充新的 GroupBox。该函数遍历会话存储中的一堆数据,并在循环的每次迭代中创建一个new GroupBox()。在类构造函数中备份,每次实例化一个新的 GroupBox 时,我们都会创建 HTML 标记来显示数据并将它们附加到页面上。该 DOM 操作代码位于在类构造函数中调用的 GroupBox 类方法中。

还有一点,每个 GroupBox 都有关联的“主题”,这就是我们有 SubjectBox 类的原因。当用户点击 GroupBox 时,我们会找到相关的 SubjectBox 对象并将它们显示在页面上。

好的,所以这里一切正常,除了我遇到了一个问题。 displayGroupBoxes() 函数获取关联的主题并将它们传递回构造函数,以便为数据创建 HTML 标记并将其附加到页面。当所有 DOM 操作代码都在构造函数中时,我得到了这个工作。然而,我收到了一位同事的代码审查,他们说最好将 DOM 操作代码分离到一个类方法中以提高可读性,即它不全部在构造函数中。完成此操作后,我将无法再访问从 displayGroupBoxes 函数传递的数据。

我知道这里有很多非常特定于项目的代码,但我希望通过这种解释,有人可能能够查看代码并理解为什么我无法访问 displayGroupBoxes 中的属性GroupBox 类方法createNewElements 中的函数。

//////////////////////////////////////////////////////////////////////////////
//  CLASSES \\
//////////////////////////////////////////////////////////////////////////////

/////////////////////////
// BOX Parent Class
/////////////////////////

/**
 * Represents a box.
 * @constructor
 * @param {object} boxOptions - An object to hold our list of constructor args
 * @param {string} name - Name associated with account
 * @param {string} type - Type associated with account
 */
class Box {
  constructor(boxOptions) {
    this.name = boxOptions.name;
    this.type = boxOptions.type;
    this.logo = boxOptions.logo;
  }
}

/////////////////////////////
// SubjectBox SubClass
////////////////////////////

/**
 * Represents a SubjectBox. These are subjects, like individual people, associated with a given group. We don't see these on initial page load. They get created (or if already created, toggled to hidden/visible) when a user clicks on a group.
 */

class SubjectBox extends Box {
  constructor(subjectOptions) {
    super({
      name: subjectOptions.name,
      type: subjectOptions.type,
      logo: subjectOptions.logo,
    });
    this.name = subjectOptions.name;
    this.type = subjectOptions.type;
    this.logo = subjectOptions.logo;
    this.subjectId = subjectOptions.subjectId;
    this.container = document.createElement("div");

// ---> a bunch of code to create HTML tags and append to page
}

/////////////////////////
// GroupBox SubClass
/////////////////////////

/**
 * Represents a GroupBox. New group boxes are instantiated when the "displayGroupBoxes()" function is called. This is what we see on initial page load.
 * @constructor
 * @param {object} groupOptions - An object to store our list of constructor args
 * @param {array} subjectBoxes - An array to store subject boxes so that once they've been instantiated, we can store them here and then toggle to hidden/visible.
 */

class GroupBox extends Box {
  constructor(groupOptions) {
    super({
      name: groupOptions.name,
      type: groupOptions.type,
      logo: groupOptions.logo,
    });

// Use this array to store the subject boxes that have already been created, so if the user clicks on the same subject, we can grab those boxes and show them rather than rebuilding.

    this.subjectBoxes = [];
    this.name = groupOptions.name;
    this.type = groupOptions.type;
    this.logo = groupOptions.logo;

    // Create HTML Elements and Append To Page

    this.createGroupBox();
  }

  // Class method to create HTML tags and append to page

  createGroupBox() {
    // Create container div for box
    const newGroupBox = document.createElement("div");
    newGroupBox.className = "group-box-container";

    // ----> A bunch of code here to create name and title, append to page. Now we need to display image icons of the subjects that are associated with the group:

    // Container for SubjectBox Icons
    const mainContainer = document.createElement("div");
    mainContainer.className = "box-main-container";

    // Icons
    const iconsContainer = document.createElement("div");
    iconsContainer.className = "box-icons-container";

    // Loop through Icons object, which contains arrays of subjects, to create an img tag for each one and append to iconsContainer.

   //// ------ >> this.subjectIcons is now returning undefined << \\


    let icons = this.subjectIcons;
    for (let i in icons) {
        let iconDiv = document.createElement("div");
        iconDiv.className = "box-icon-div";
        let icon = document.createElement("img");
        icon.src = icons[i];
        icon.className = "box-icon";
        iconDiv.append(icon);
        iconsContainer.append(iconDiv);
        mainContainer.append(iconsContainer);
        i++;
    }

    newGroupBox.append(mainContainer);

/////////////////////////////////////////////////////////////////
    // EVENT LISTENER. -- when the user clicks on a GroupBox, it runs a function to show us the subjects associated with that group
////////////////////////////////////////////////////////////////

    newGroupBox.addEventListener("click", () => {
      /// --> some code to hide the GroupBoxes that are already on the page so we can display SubjectBoxes instead

      // build subject boxes
      const boxesContainer = document.getElementById("boxes-container");

      // Check to see if subject boxes have already been created. If not, instantiate the new subjectBox objects. Otherwise, if they have already been built, loop through and set them to visible

      if (!this.subjectBoxes.length) {

// If subject boxes haven't been created yet, meaning if the user hasn't clicked on this group yet, map over the array of subjects passed up from the `displayGroupBoxes()` function and create a `new SubjectBox` object for each item

     //! -----> groupOptions.subjects is returning undefined << \\

        this.subjectBoxes = groupOptions.subjects.map((subject, i) => {
          return new SubjectBox({
            name: groupOptions.subjects[i].entry_text,
            type: groupOptions.subjects[i].entry_type,
            logo: groupOptions.subjects[i].icon_link,

            subjectId: groupOptions.subjects[i].subject_id,

            // Set the container for HTML
            container: boxesContainer,
          });
        });

// Otherwise if subject boxes have already been created previously, toggle them to visible
      } else {
        for (const box of this.subjectBoxes) {
          box.show();
        }
      }

    // Append group box to document inside the same container we created for GroupBoxes:

// -------> groupOptions.container is returning undefined ----- \\

    groupOptions.container.append(newGroupBox);
  }
}


这是上面代码块中引用的displayGroupBoxes()函数:

// --------- Populate Group Boxes --------- \\

function displayGroupBoxes() {
  // Initialize array to hold collection of group boxes
  let groupBoxes = [];

  // Grab the "boxes-container" div from HTML and set it to variable "boxesContainer"
  const boxesContainer = document.getElementById("boxes-container");

  // Loop through the data to grab all the "B" type group boxes
  // Create a new GroupBox object for each item in the array

  
  for (let arr in data) {
    if (data[arr].entry_type === "B") {
      // Find all "Subject" ("P") boxes associated with item and store in variable "subjectGroup". Then grab all the icon image URL's
      
      let subjectGroup = data.filter(
        (group) =>
          group.group_number === data[arr].goto_group &&
          group.entry_type === "P"
      );

      let subjectGroupIcons = [];
      for (let i in subjectGroup) {
        if (
          subjectGroup[i].group_number === data[arr].goto_group &&
          subjectGroup[i].entry_type === "P"
        ) {
          subjectGroupIcons.push(subjectGroup[i].icon_link);
        }
      }


      // Create new GroupBox objects
      let groupBox = new GroupBox({
        name: data[arr].entry_text,
        type: data[arr].entry_type,
        logo: data[arr].icon_link,
       

        //! I'm not able to access these from the new "createGroupBoxes" function...

        // Set the container for HTML
        container: boxesContainer,
        // Pass subjects to GroupBox
        subjects: subjectGroup,
        // Pass subject icons up to GroupBox
        subjectIcons: subjectGroupIcons,
        // Pass headers (e.g. "Tennis Group") up to GroupBox
        headers: headerGroup,
      });

      // Push each groupBox object into the groupBoxes array.
      // This is just for convenience so we can see all the group boxes easily if we need to.
      groupBoxes.push(groupBox);
    }
  }
}

这里有很多内容,但希望很清楚我想要做什么。我想将containersubjectssubjectIcons 属性从这个displayGroupBoxes 函数传递给为数据创建HTML 标记并将其附加到页面的类方法。当我在类构造函数中拥有所有的 DOM 操作代码时,这很有效,但是当我将它移到构造函数之外的它自己的类方法中以提高可读性时,除了这些属性被传递之外,一切都正常工作。我一定是把他们叫错了,但我不明白这个问题。

【问题讨论】:

  • "我收到了一位同事的代码审查,他们说最好将 DOM 操作代码分离到一个类方法中以提高可读性,也就是说,它不会全部在构造函数。” - 不仅如此,也不应该从构造函数内部调用。构造函数应该只关心设置实例属性。这样,您的子类可以在调用方法之前完成初始化。
  • "我的代码编辑器在此处添加了括号,我不明白为什么" - 可能是因为您在此处使用了逗号而不是分号。
  • 非常有帮助的cmets,理解,我会努力解决这个问题,谢谢!
  • @MickeyVershbow 要跟进 Bergi 的评论,您应该改用 for..of。如果您有机会粘贴这些课程的完整代码,我可以在今天晚些时候或明天提供全面审查
  • @Mulan 好的,我明白你的意思,谢谢,我会这样做!

标签: javascript html jquery oop


【解决方案1】:

问题出在GroupBox 构造函数中,它仅定义名称、类型和徽标。您需要在类定义中添加其他属性才能使它们可用。

  constructor(groupOptions) {
    super({
      name: groupOptions.name,
      type: groupOptions.type,
      logo: groupOptions.logo,
      container: groupOptions.container,
      subjects: groupOptions.subjects,
      subjectIcons: groupOptions.subjectIcons || [],
      headers: groupOptions.headers
    });

【讨论】:

  • 这成功了!我还必须将它作为this.container = groupOptions.container 等添加到构造函数中。非常感谢!
  • 当然可以。我刚刚进行了编辑以澄清groupOptions 方面。我提供的那些值应该被视为默认值。
  • 您可以通过以下几种方式进行操作。一种方法是在类构造函数本身subjectIcons: groupOptions.subjectIcons || [] 中设置默认值。但是,这也可以在您的实现中定义,您将在其中实例化 GroupBox 实例并将值传递给构造函数。在这种情况下,它看起来像 displayGroupBoxes 函数。我忘记了 JavaScript 缺少接口,所以你可能只想在构造函数中硬编码默认值。
  • 更新了答案以反映这些 cmets。
  • @MickeyVershbow 请put that in an answer 而不是编辑您的问题以不再显示您询问的代码
【解决方案2】:

根据我得到的回复在这里分享答案。我标记为正确的答案几乎是正确的,除了属性不应在super() 调用中传递。我需要在类构造函数中定义属性,以便稍后能够从displayGroupFunctions 向它们传递数据。

现在正如@Bergi 所提到的,我仍然需要从构造函数之外调用类方法createGroupBox,但以下代码解决了我原来的问题:

class GroupBox extends Box {
  // PARAM: groupOptions = {}
  constructor(groupOptions) {
    super({
      name: groupOptions.name,
      type: groupOptions.type,
      logo: groupOptions.logo,
    });
    // Use this array to store the subject boxes that have already been created, so if the user clicks on the same subject, we can grab those boxes and show them rather than rebuilding.
    this.subjectBoxes = [];
    this.name = groupOptions.name;
    this.type = groupOptions.type;
    this.logo = groupOptions.logo;

 -----> new code:
    this.container = groupOptions.container;
    this.subjects = groupOptions.subjects;
    this.subjectIcons = groupOptions.subjectIcons;
    this.headers = groupOptions.headers;

----> this should be called from outside the constructor 

    // Create HTML Elements and Append To Page
    this.createGroupBox();
  }

【讨论】:

  • 用类和子类进行抽象可能是一件非常困难的事情。很多时候,它最终感觉比有效更麻烦。随意包含 complete 类,我会尝试给你一些指示
  • 那太不可思议了。接下来的几个小时我都离开办公桌,所以今晚我会这样做。谢谢你的帮助,我学习了很多。
  • @Mulan 我正在做一些改进,我知道在发布其余代码之前我需要做一些改进。如果不在构造函数中,你能帮我理解我应该在哪里调用this.createGroupBox()吗?我希望每次实例化新的 GroupBox 对象时都运行该函数。
  • 如果没有看到整个画面,我很难给出建议。每当您发布时,请随时再次发表评论,我会看看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多