JavaScript 中的数组有自己的怪癖,如果您来自其他语言,您可能不会想到这些怪癖。对于您的用例,两个重要的是:
- 不能在 JavaScript 中直接声明多维数组。
- 在创建时设置数组的大小几乎没有提高效率(并且没有增加安全性)。
与其他语言不同,JavaScript 不会为整个数组分配一块内存。
(它不知道你将在每个单元格中放入什么样的对象,
因此它需要多少总内存。)
相反,Array() 的所有 size 参数为您所做的是设置数组的 length 属性。
对于一般的二维数组情况,我建议:
-
创建“top”数组,例如:
var i // the first-order index in a
, j // the second order index in a
, a = []
-
根据需要初始化数组元素。
这叫lazy initialization,
而且,在这种情况下,它只涉及测试a[i] 是否存在
在我们尝试将某些内容分配给a[i][j] 之前,例如:
if (!a[i]) a[i] = []
上面的声明用英文写成:
"如果a 的第 i 个元素是 'falsy',则为第 i 个元素分配一个空数组。"
-
最后,将实际值赋给多维度数组:
a[i][j] = 'whatever'
对于您的情况,您提前知道这些值,
所以你可以提前初始化每个元素。
(但是,如果您没有覆盖大多数元素,
懒惰的实现可能会更好;见下文。)
var x, x_length = 100
, y, y_length = 100
, map = []
// Don't be lazy
for (x = 0; x < x_length; x++) {
map[x] = []
for (y = 0; y < y_length; y++) {
map[x][y] = 'grass.gif|ongrass.gif|collision.gif|above.gif'
}
}
正如其他人所说,
具有 100 个元素的数组的索引编号从 0 到 99,
所以这里用小于比较是最合适的。
作为参考,这里有一个使用延迟初始化的实现。
我已经使用函数接口而不是直接访问数组;
它更长、更复杂,但也更完整。
我在这里使用的初始化模式称为
immediately invoked function expression。
如果你以前没有看过,
它是更有用的 JavaScript 模式之一
值得花一些时间去理解。
var map = (function (x_length, y_length, v_default, undefined) {
// Unless v_default is overwritten, use ...
v_default = v_default || 'grass.gif|ongrass.gif|collision.gif|above.gif'
// Private backing array; will contain only values for a[x][y]
// that were explicitly set.
var a = []
// Private helper function.
// - Returns `true` if `x` is between `0` and `x_length - 1`
// and `y` is between `0` and `y_length - 1`.
// - Returns `false` otherwise.
function valid (x, y) {
return (x >= 0
&& x < x_length
&& y >= 0
&& y < y_length)
}
// Private helper function.
// - Returns `true` if a[x][y] has been set().
// - Returns `false` otherwise.
function exists (x, y) {
return !!a[x] && !!a[x][y]
}
// Private getter
// - Returns the value of a[x][y] if it has been set().
// - Returns `undefined` if the point (x,y) is invalid.
// - Returns `v_default` otherwise.
function get (x, y) {
if (!valid(x, y)) return undefined
else if (exists(x, y)) return a[x][y]
else return v_default
}
// Private setter
// - Returns the value set on success.
// - Returns `undefined` on failure
function set (x, y, v) {
if (valid(x, y)) {
// We're being lazy
if (!a[x]) a[x] = []
a[x][y] = v
return a[x][y]
}
return undefined
}
// Return an interface function.
// - Pass the function three arguments, (x, y, v), to set a[x][y] = v
// - Pass the function two arguments, (x, y), to get a[x][y]
return function (x, y, v) {
if (arguments.length > 2) {
return set(x, y, v)
} else {
return get(x, y)
}
}
})(100, 100)
当我在节点中运行上述内容时,以下测试会打印出合理的值:
// Invalid invocations
console.log('map() : %s', map())
console.log('map( 0) : %s', map(0))
console.log('map( -1, 0) : %s', map(-1,0))
console.log('map( 0, -1) : %s', map(0, -1))
console.log('map( -1, -1) : %s', map(-1, -1))
// Valid invocations
console.log('map( 0, 0) : %s', map(0, 0))
console.log('map( 99, 99) : %s', map(99, 99))
console.log('map( 1, 1) : %s', map(1,1))
console.log('map( 1, 1, "foo") : %s', map(1,1, 'foo'))
console.log('map( 1, 1) : %s', map(1,1))