通过JavaScript构造函数引入循环依赖

时间:2012-07-10 04:00:36

标签: javascript scope circular-dependency

我有一个javascript应用程序,我正在开发我正在动态构建一个动作树,我发现自己处于想要有目的地引入循环依赖的奇怪情况。在最初非常hacky尝试之后,我发现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。有没有办法解决这个限制,让我的变量以任何顺序声明?是否会使用空白声明并稍后设置它们? e.g。

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,最新版本)。但是,在新窗口或新浏览器中打开它会停止工作。

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

感谢您的帮助!

2 个答案:

答案 0 :(得分: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 with:undefined ...这不起作用”

这是因为在'c'启动时,'a'不会被启动并正确引用。

试试jsfiddle:

http://jsfiddle.net/sNbm5/

答案 1 :(得分: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(...);

但是如果你开始这样做,你就无法总是在不重写代码的情况下确定在设置动作时会发生什么。

为什么这样?你必须了解操作的顺序以及这对事物的影响。

in

a = b(c(a))

操作顺序是

  1. c(a) - > result1
  2. b(result1) - > result2
  3. a获取result2
  4. 的值

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