I wrote a simple Sudoku solver using backtracking in JS. In effort to be "purely functional" all my 9x9 puzzle arrays are immutable thus a new array is created whenever a new number is inserted.
new SudokuPuzzle
In the first version I use the new Puzzle(puzzle)
approach to clone the object:
function SudokuPuzzle(obj) {
if (obj instanceof SudokuPuzzle) {
this.grid = obj.grid.slice(0); // copy array
} // ...
}
Then whenever I update the array I do the following:
SudokuPuzzle.prototype.update = function(row, col, num) {
var puzzle = new SudokuPuzzle(this); // clone puzzle
puzzle.grid[row*9 + col] = num; // mutate clone
return puzzle; // return clone
}
Object.create()
I wrote another version where I use Object.create()
instead and have a base object sudokuPuzzle
that I inherit from to create new puzzles. Here is the clone()
method:
sudokuPuzzle.clone = function() {
var puzzle = Object.create(this); // create puzzle from sudokuPuzzle
puzzle.grid = this.grid.slice(0); // copy array
return puzzle; // return newly minted puzzle
}
My update method in this case is
sudokuPuzzle.update = function(row, col, num) {
var puzzle = this.clone(); // clone puzzle
puzzle.grid[row*9 + col] = num; // mutate clone
return puzzle; // return clone
}
The first version using new
is very fast using Node:
$ time node Sudoku.js
real 0m0.720s
user 0m0.699s
sys 0m0.016s
The second version using Object.create()
is consistently over 10x slower:
$ time node Sudoku2.js
real 0m7.746s
user 0m7.647s
sys 0m0.091s
A similar question here noted that Object.create()
is a lot slower in browsers, while I am also seeing a large disparity with node.js as well. I can certainly see timing discrepancies between JS engines, but a > 10x difference?!? Does anyone know why the difference is over an order of magnitude?!
You can find the source code here.
Thanks to Bergi's answer below I changed the line in the clone
method from
var puzzle = Object.create(this);
to this:
var puzzle = Object.create(sudokuPuzzle);
which avoids long and complex inheritance chains (i.e., I always inherit from the same base object) and I now get speed results comparable to using new
. Thanks Bergi.
To create an object, use the new keyword with Object() constructor, like this: const person = new Object(); Now, to add properties to this object, we have to do something like this: person.
You can create an object in three different ways: Using object literal. By creating instance of Object directly. By using constructor function.
You've already established that V8 fails to optimise Object.create
as good as new
(in contrast to SpiderMonkey). Or at least did historically. See also this blog post for details.
However, there is a second reason for this being a lot slower: your two codes have different results. You would have to use
SudokuPuzzle.prototype.clone = function() {
var puzzle = Object.create(SudokuPuzzle.prototype); // a new instance, without `new`
puzzle.grid = this.grid.slice(0); // copy array (usually initialised in constructor)
return puzzle; // return newly minted puzzle
};
to create clones that are equivalent to those created using new SudokuPuzzle()
.
The problem is that when you use Object.create(this)
, you are creating a new object that has a different prototype - namely, the this
instance. Cloning very often from each other, you are creating a very complex inheritance hierarchy. And all these objects, with different prototypes, will have different hidden classes - which prevents optimisations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With