2

我正在开发一个使用 javascript 更改 iframe 中的页面的向导。我想为向导的每一页创建一个对象,并引用下一页和上一页。

编辑:下面发布的代码不起作用。actionOne.nextAction 执行后等于 {}。

var actionOne = {};
var actionTwo = {};

actionOne = {
    url: 'actionOneUrl.htm',
    prevAction: null,
    nextAction: actionTwo,
    doDisplay: function(){
        $('.label').html('Action One');
    }
}

actionTwo = {
    url: 'actionTwoUrl.htm',
    prevAction: actionOne,
    nextAction: null,
    doDisplay: function(){
        $('.label').html('Action Two');
    }
}

问题是我无法弄清楚如何正确设置下一个和上一个引用。可能有一个相对简单的解决方案,但我不确定要搜索什么。我可以在创建所有页面后设置引用,但这样做感觉很笨拙。有没有办法在创建对象时做到这一点?

4

7 回答 7

2

对于您想要做的事情,您将需要在 JavaScript 中使用面向对象的方法。这将允许您分配对对象的新实例的引用。例如这有效:

http://jsfiddle.net/Gq7vQ/

function Action(url, name){
    this.url = url;
    this.prevAction = null;
    this.nextAction = null;
    this.name = name;
}

Action.prototype.doDisplay = function(){
    $(".label").html(this.name);
}

var actionOne = new Action('actionOneUrl.html', 'Action One');
var actionTwo = new Action('actionTwoUrl.html', 'Action Two');

actionOne.nextAction = actionTwo;
actionTwo.prevAction = actionOne;

console.log(actionOne.nextAction);

编辑:所以 OP 要求一个实现,在新添加的操作之间自动设置这些链接。所以这是一个双向链表实现:

http://jsfiddle.net/wXC9B/1/

function ActionList(){
    this.head = null;
    this.tail = null;
}

ActionList.prototype.doDisplay = function(index){
    var node = this.getNode(index);

    console.log(node.name);
}

ActionList.prototype.getNode = function(index){
    var current = this.head,
        c = 0;

    while(c < index && current !== null){
        current = current.nextAction;
        c++;
    }

    return current;
}

ActionList.prototype.add = function(url, name){
    var node = {
        url: url,
        name: name,
        nextAction: null,
        prevAction: null
    };

    if(this.head === null){
        this.head = node;
        this.tail = node;
    }
    else{
        this.tail.nextAction = node;
        node.prevAction = this.tail;

        //move tail to new node
        this.tail = node;
    }
}

var actionList = new ActionList();

//Each add automatically sets up links between the two
actionList.add('actionOneUrl.html', 'Action One');
actionList.add('actionTwoUrl.html', 'Action Two');

console.log(actionList.getNode(1));

actionList.doDisplay(1);
于 2013-02-07T19:12:22.730 回答
1

这是一个非常简化的示例,但类似以下结构的内容将避免手动引用您的下一个/上一个操作的需要……让应用程序逻辑根据用户的输入查找要执行的操作。

UnderscoreJS 的where函数http://underscorejs.org/#where在这里很有用

var dataFromServer = [
  {id:"1", name: "First Page", nextId:"2"},
  {id:"2", name: "Second Page", nextId:"3", prevId: "1"},
  .....];

var actions = [];

var Action = function(data) {
    this.doNextURL = function() {
        //find action with id= data.nextId;
        var actionToDo = _.where(actions, {id: data.nextId})[0];
        window.location.href = actionToDo.url; //or something... a callback parameter, or returning the action rather than doing the 'ui logic' here would be better real world
    }
}

for(var i = 0; i < dataFromServer.length; i+=1){
  actions.push(new Action(dataFromServer[i]));
}
于 2013-02-07T19:04:16.917 回答
1

当你这样做

actionTwo = {
    // ...
}

you are assigning a new value to actionTwo. It does not refer to the object anymore you assigned in var actionTwo = {}; and hence does not refer to the object you used in

actionOne = {
    // ...
    nextAction: actionTwo,
    // ...
}

The easiest way would be to just initialise both objects and then assign them to the correct properties later on:

var actionOne = {
    url: 'actionOneUrl.htm',
    prevAction: null,
    nextAction: null,
    doDisplay: function(){
        $('.label').html('Action One');
    }
};

var actionTwo = {
    url: 'actionTwoUrl.htm',
    prevAction: null,
    nextAction: null,
    doDisplay: function(){
        $('.label').html('Action Two');
    }
};

actionOne.nextAction = actionTwo;
actionTwo.prevAction = actionOne;

If you want to do this for multiple actions, you should consider using constructor functions, so as joeltine shows in his answer.


To learn more about objects, have a look at MDN - Working with Objects.

于 2013-02-07T19:14:29.087 回答
1

Try this: don't create NEW objects for actionOne, actionTwo, instead leave your code as is - but assign to object properties of the already existing objects (which the first two lines create).

var actionOne, actionTwo;

actionOne = {
    url: 'actionOneUrl.htm',
    doDisplay: function(){
        $('.label').html('Action One');
    }
};

actionTwo = {
    url: 'actionTwoUrl.htm',
    doDisplay: function(){
        $('.label').html('Action Two');
    }
};

actionOne.prevAction = null;  //could also be set above
actionOne.nextAction = actionTwo;

actionTwo.prevAction = actionOne;
actionTwo.nextAction = null;  //could also be set above

Your question was a very good one - don't let anyone tell otherwise :) It is NOT obvious, even with quite a bit of JS background, that the object properties point to the objects the variables pointed to at the time the (literal) object creation statement was executed, rather than to the variable itself (in which case your example would have worked).

And please ignore the MVC pattern thing, even if it was even upvoted. Nothing wrong with MVC (sometimes), but this is a much, much MUCH more basic Javascript question, those pattern things come into play on a whole different (higher) level than your little interesting issue.

Some background: Deep inside the bowels of the Javascript execution engine variables that have an object as value are pointers (C/C++ background knowledge is good for understanding Javascript, because JS engines are written in it). So, when you assign the value of such a variable to an object property it will not point to the variable, but instead it will receive the pointer the variable has at value at the time. This means if the variable gets a new object assigned, pointing to another place in memory, the object property keeps pointing to the old object. If it pointed to the variable instead it would there find a pointer to the new object. As you can see, answering your question leads us deep inside how Javascript engines actually work on a very low level :)

All the other answers sure also solve your immediate issue, but I believe knowing this bit of background is much more fertile, in the end. Instead of trying to just give an answer that works it's sometimes worth investigating what's really going on... :)

Primitive types are stored in the variable directly, variables for objects are actually pointers. new String("foo") is an object (String), "foo" is a primitive type (string).

The exact same issue is important to keep in mind when calling functions in Javascript! It is call by value always, technically - but when the variable is a pointer to an object the value IS the pointer, which one must consider when assigning to variables the function gets as parameter.

于 2013-02-07T19:15:22.387 回答
1

When you use the {} object literal to define an object you are creating a new object instance with the Object constructor.

The following creates two new object instances from the Object constructor:

var actionOne = {}; // object instance 1
var actionTwo = {}; // object instance 2

This next part creates another new object instance (the third object instance) from the Object constructor and adds several properties. actionOne.nextAction points to the object instance of actionTwo (which doesn't have any of its own properties).

actionOne = {
    url: 'actionOneUrl.htm',
    prevAction: null,
    nextAction: actionTwo,
    doDisplay: function(){
        $('.label').html('Action One');
   }
} // object instance 3

So now when you declare actionTwo = {....} it creates a fourth object instance with a bunch of new properties. actionOne.prevAction still points to the second object instance you created (but are are no longer referencing with the the global variable actionTwo).

The key to remember is that the object literal {} creates new object instances with the Object constructor and the properties you create reference the object instance at the time they are declared.

于 2013-02-07T19:31:15.817 回答
0

Everyone seems to really be overcomplicating this.

function Wizard(o) {
    return { url:o.url, doDisplay:function() { $('.label').html(o.html); } };
}
var wizards = [{url: 'actionOneUrl.html', html:'Action One'},
               {url: 'actionTwoUrl.html', html:'Action Two'}].map(Wizard);
    // wizards now contains an array of all your wizard objects
wizards.reduce(function(p,c) { c.prevAction = p; return p.nextAction = c;  });
    // prevAction and nextAction now point to the right places
于 2013-02-07T20:39:35.553 回答
-1

No you can't, but you can keep the empty objects and just fill them:

var actionOne = {};
var actionTwo = {};

actionOne.url = 'actionOneUrl.htm';
actionOne.prevAction = null;
actionOne.nextAction = actionTwo;
...

But that's rather ugly. I would recommend filling in the links between them by using a function like this:

function chain() {
    var i, prev, curr;
    for(i = 0; i < arguments.length; i++) {
        curr = arguments[i];
        if(prev) {
            prev.nextAction = curr;
            curr.prevAction = prev;
        }
        else {
            curr.prevAction = null;
        }
        prev = curr;
   }
   if(curr) curr.nextAction = null;
}


var actionOne, actionTwo;

actionOne = {
    url: 'actionOneUrl.htm',
    doDisplay: function(){
        $('.label').html('Action One');
    }
}

actionTwo = {
    url: 'actionTwoUrl.htm',
    doDisplay: function(){
        $('.label').html('Action Two');
    }
}

chain(actionOne, actionTwo);
于 2013-02-07T19:15:32.133 回答