2

我有一个我正在开发的 javascript 应用程序,我正在动态构建一个动作树,我发现自己处于想要有目的地引入循环依赖的奇怪情况。经过最初的非常骇人听闻的尝试后,我发现 JavaScript 变量作用域实际上引入了一种非常合理的方法来解决这个问题。我仍然不是 JavaScript 方面的专家,所以我想获得一些关于最佳实践的意见。这是一段工作代码:

var Step = function(title, instructions, action) {
    this.title = ko.observable(title);
    this.instructions = instructions;
    this.action = action;
};

var Action = function(cancelText, cancelDescription, cancelStep, nextDescription, addText, addStep, continueText, continueStep) {
    this.cancelText = cancelText;
    this.cancelDescription = cancelDescription;
    this.cancelStep = cancelStep;
    this.nextDescription = nextDescription;
    this.addText = addText;
    this.addStep = addStep;
    this.continueText = continueText;
    this.continueStep = continueStep;
};

var PersonalStep = new Step(
    "Contact Information",
    "How can we contact you about your awesome assortment of vehicles? Fill out the form below",
    new Action(null, null, null, null, null, null, null, null)
);

var AddVehicleStep = new Step(
    "Additional Vehicle",
    "You have another car? Great, tell us about it too!",
    new Action("Cancel",
               "No, nevermind about this vehicle.",
               PersonalStep,
               "Add another vehicle?",
               "+ Add",
               AddVehicleStep, // This is the weird bit to me
               "No, continue on",
               PersonalStep)
    );

var VehicleStep = new Step(
    "Vehicle Details", 
    "Tell us about your primary vehicle by filling out the form below.",
    new Action(null, null, null,
               "Add another vehicle?",
               "+ Add",
               AddVehicleStep,
               "No, continue on",
               PersonalStep)
);

因此,实际上,当用户在表单上选择“添加”操作时,AddVehicleStep 可以连续添加其他车辆。在大多数语言中(无论如何我都熟悉),AddVehicleStep 变量将无法从其自己的构造函数中解析。这对我来说很奇怪,我想更多地了解这个 JS 习语。有没有更好的方法来动态地做这样的对象树?

这也让我开始思考,我一直有目的地以相反的顺序声明我的步进变量,以便它们可以解析。我在自己的构造函数中引用变量的发现使我相信这不是必需的。但我只是测试了它,如果我在 VehicleStep 之后移动 AddVehicleStep var,VehicleStep 会得到null它的action.addStep. 有没有办法绕过这个限制,让我的变量以任何顺序声明?使用空白声明并稍后设置它们会起作用吗?例如

var a;
var b;

var a = new Step(b);
var b = new Step(b);
// a should now have the full instantiated object of b within it, right?
// (provided the step constructor assigned it, of course)

这可能已经在其他地方得到了回答,我只是还没有找到关键字来提出它......

此外,我将这些步骤用作 Knockout.js 应用程序的一部分,该应用程序本质上是实现对话框/表单向导 - 我希望示例代码能够独立地提出难题,但以防你好奇。

更新 我昨晚在 JS 小提琴中工作。事实证明,在 jsfiddle 上的后续运行之间如何处理 js 内存,导致它在我一直在工作的特定窗口(Chrome,最新版本)中工作。但是,在新窗口或新浏览器中打开它会停止工作。

真正奇怪的部分是我无法在任何浏览器中复制该行为。也许我的一个编辑让它以不同的方式声明并以某种方式将它留在内存中。我真希望我能复制它,只是为了证明我没有疯...

谢谢您的帮助!

4

2 回答 2

2

看看下面的代码:

function b(data, ref) {
    alert("instantiated B with : " + data + " and "  + ref);
}

function c(ref){
    alert("instantiated C with : " + ref + " ... this doesnt work");
}


var a = new b('blah', new c(a));

'b' 已正确初始化。但是你在初始化'c'时得到的警告如下:

“实例化C:未定义......这不起作用”

这是因为在启动“c”时,“a”并未正确启动和引用。

试试 jsfiddle:

http://jsfiddle.net/sNbm5/

于 2012-07-10T05:15:31.887 回答
2

由于步骤可以具有引用同一步骤的动作,我认为最简单的方法是让自己在构建步骤之后也添加动作。所以,像这样的东西。

var Step = function(title, instructions, action) {
    this.title = ko.observable(title);
    this.instructions = instructions;
    if (action === undefined) 
       this.action = null;  //action was not passed
    else
       this.action = action; 
};

//set the action after constructor invocation
Step.prototype.SetAction = function(action) {
    this.action = action;
};

var AddVehicleStep = new Step(
    "Additional Vehicle",
    "You have another car? Great, tell us about it too!"
    );
//AddVehicleStep is now instantiated with a new Step,
// so we can now set its action refering to that step 
AddVehicleStep.SetAction(new Action("Cancel",
               "No, nevermind about this vehicle.",
               PersonalStep,
               "Add another vehicle?",
               "+ Add",
               AddVehicleStep, // This is the weird bit to me
               "No, continue on",
               PersonalStep));

或者地狱,忘记方法,直接做

AddVehicleStep.action = new Action(...);

但是,如果您开始这样做,您就失去了在不重写代码的情况下始终确定设置操作时会发生什么的能力。

为什么要这样做?您必须了解操作顺序以及它如何影响这里的事情。

a = b(c(a))

操作顺序是

  1. c(a)->result1
  2. b(result1)-> 结果2
  3. a获得价值result2

假设a(在本地范围内)之前未分配给,则c(a)等价于c(undefined)

于 2012-07-10T06:20:51.210 回答