Javascript 为什么将IIFEs与Mithril.js一起使用时,组件的所有实例的最新实例中的属性会覆盖状态?

Javascript 为什么将IIFEs与Mithril.js一起使用时,组件的所有实例的最新实例中的属性会覆盖状态?,javascript,iife,mithril.js,Javascript,Iife,Mithril.js,我正在试用Mithril.js,并制作了三个小组件 let Map = (function() { let Map = {}; let parent; Map.oninit = function(vnode) { parent = vnode.attrs.parent; } Map.view = function(vnode) { return m("p", parent) } Map.onupdate

我正在试用Mithril.js,并制作了三个小组件

let Map = (function() {
    
  let Map = {};
  let parent;
  
  Map.oninit = function(vnode) {
    parent = vnode.attrs.parent;
  }
  
  Map.view = function(vnode) {
    return m("p", parent)
  }
  
  Map.onupdate = function(vnode) {
      console.log(parent);
  }
  
  return Map;
})();

let Popup = (function() {
    let Popup = {};
  
  Popup.view = function(vnode) {
    return m(Map, {parent: "Popup"});
  }
  
  return Popup; 
})();

let Dashboard = (function() {
    
  let Dashboard = {};
  Dashboard.view = function(vnode) {
    return m("div", [
        m(Map, {parent: "Dashboard"}),
      m(Popup),
      m("button",
      {
        onclick: function(e) {
            //Click event that doesn't do anything. Just added to trigger Mithril redraw
        }
      }, "Redraw")
    ]);
  }
  
  return Dashboard;
})();

m.mount(document.body, Dashboard);
主要的外部组件是仪表板,应该显示地图组件和弹出组件。弹出组件显示了Map的另一个实例。当显示映射组件时,我发送一个字符串,将父组件的名称作为属性,并将一个局部变量设置为该属性。Map组件只返回一个包含此变量的简单段落,以显示父组件的名称。仪表板显示两个段落,其中包含父组件的名称和一个按钮,以便单击时Mithril将重新绘制

初始化时,它按预期工作,并显示包含“Dashboard”的段落、包含“Popup”的段落和按钮。问题是,当我点击按钮更新DOM时,两个段落现在都显示“Popup”。因此,出于某种原因,这两个实例都使用最新的值。我可以通过将映射组件重写为非IIFE来修复此问题,如下所示:

function Map() {
  let parent;
  
  return {
    oninit : function(vnode) {
      parent = vnode.attrs.parent;
    },

    view : function(vnode) {
      return m("p", parent);
    },

    onupdate: function(vnode) {
      console.log(parent);
    }
  }
}


为什么作为一个生命的版本不能像我预期的那样工作,我如何在保持它作为一个生命的同时使它工作?

就像@Bergi所说的,可能更容易放弃这个生命。这里我转换了您必须使用的闭包样式组件。您可以使用POJO作为组件,但我不建议使用它,因为状态令人困惑

当您立即调用该函数时,您将返回一个Map对象。该对象在弹出窗口中使用,因此您可以在所有代码中使用相同的对象。我相信有一个大脑巨大的人可以通过所有的调用来确定确切的位置,要么值只设置了一次,要么值发生了冲突,但无论如何,你不能让父对象设置为同一地图对象上的两个不同的对象。这是令人惊讶的第一次工作,但正如你所看到的,它是不可靠的

我删除了IIFE,并用Component后缀重命名了组件,以使代码更加清晰

let MapComponent = function() {
    
  let Map = {};
  let parent;
  
  Map.oninit = function(vnode) {
    parent = vnode.attrs.parent;
  }
  
  Map.view = function(vnode) {
    return m("p", parent)
  }
  
  Map.onupdate = function(vnode) {
      console.log(parent);
  }
  
  return Map;
};

let PopupComponent = function() {
    let Popup = {};
  
  Popup.view = function(vnode) {
    return m(MapComponent, {parent: "Popup"});
  }
  
  return Popup; 
};

let Dashboard = (function() {
    
  let Dashboard = {};
  Dashboard.view = function(vnode) {
    return m("div", [
        m(MapComponent, {parent: "Dashboard"}),
      m(PopupComponent),
      m("button", {
        onclick: function(e) {
            //Click event that doesn't do anything. Just added to trigger Mithril redraw
        }
      }, "Redraw")
    ]);
  }
  
  return Dashboard;
})();

m.mount(document.body, Dashboard);

您没有包含实例的组件。代码中只有一个
Map
模块,它有一个
parent
变量。当你想创建多个独立的对象时,为什么要尝试使用iLife?@Bergi似乎对它的工作原理没有很好的理解。我想使用父变量更改贴图的外观。例如,如果父对象是仪表板,我希望贴图具有给定的大小。因此,一个映射可能具有给定的大小和所有功能,而另一个映射可能更小,功能更有限,因为父组件是其他组件,如弹出窗口。组件应该这样使用,还是我误解了它们应该如何使用?