Cuckoo
Cuckoo

Reputation: 366

Why does apply skip first element of array used for arguments?

I am adding object literals to a prototype. I am doing this by taking the properties of objects and putting the values in an array. I am then creating a new object using the constructor, with the array as the arguments.

The only problem is that the constructor (using apply) is skipping the first element in the array when creating the new object, and so is assigning the wrong value to the wrong property in the new object - the last value ending up empty.

In the debugger, both the array and the constructor function display the properties/elements in the correct order. Yet the output is incorrect.

I know I could simply create the new objects by putting the arguments directly in the new object constructor. But this is difficult to read. Unless there is another way of attaching objects to a prototype? Or a tidier way of arranging my data, ready for the constructor?

Here is the code:

(function(root, undefined) {
  var roomArray = [
    {
      locationIndex: 0,
      name: "North room",
      description: "The room is bare, there is a smashed window on the North wall, beyond which you see a grey mist.",
      exits: {north: false, south: 1, east: false, west: false, down: false, up: false}
    },
  {
      locationIndex: 1,
      name: "Room 1",
      description: "It is hard to see much here.",
      exits: {north: 0, south: 3, east: 2, west: false, down: false, up: false}
    },
    {
      locationIndex: 2,
      name: "Room 2",
      description: "A bedroom.",
      exits: {north: false, south: false, east: false, west: 1, down: false, up: false}
    },
    {
      locationIndex: 3,
      name: "kitchen",
      description: "A kitchen.",
      exits: {north: 1, south: false, east: false, west: false, down: false, up: false}
    }
  ];
  
  // Room constructor
    function Room(location, name, description, exits) {
      this.location = location;
      this.name = name;
      this.description = description;
      this.exits = exits;
  }
  
  // Create Rooms
  roomArray.forEach(function(room, index) {
    var convertArray = [];
    for (var props in room) {
      convertArray.push(room[props]);
    }
    eval("room_" + index + " = new (Room.bind.apply(Room, convertArray))()");
    console.log(convertArray);
    console.log(eval("room_" + index))
  });
  
})(this);

Upvotes: 0

Views: 146

Answers (2)

cptaffe
cptaffe

Reputation: 426

Room.bind.apply(Room, convertArray) is the issue. You are applying the bind function, not the constructor directly. Since bind takes this as the first parameter and so does apply, you've got to provide it twice.

Something like

Function.bind.apply(Room, [null].concat(convertArray))

should work.

Relevant Mozilla docs

Upvotes: 1

Jaromanda X
Jaromanda X

Reputation: 1

try this

root['room_' + index] = new (Function.prototype.bind.apply(Room, convertArray))();

or for modern javascript

root['room_' + index] = new Room(...convertArray)

Upvotes: 1

Related Questions