《像素大战》

电影中出现的游戏:

===========================================================================================================

-Game Start-

约2:12处出现在一台街机上的巨大怪物图案来自于著名街机游戏蜈蚣 Centipede http://www.douban.com/subject/25727952/ 其后在这一场景中再次出现,展现了用子弹射击蜈蚣怪物的游戏机制。人类与外星怪物在伦敦的第一次遭遇战内容也即是这款游戏。

约2:15处右侧带有“Jou……”字样的街机来自骑士大战 Joust
http://www.douban.com/subject/25730536/ ,而在最后大决战中骑着鸟的战士(毁坏华盛顿纪念碑的)形象也来自于这款游戏,另外,这款游戏也是史上第一款获得IGN满分的游戏。(尽管以前IGN给满分相当慷慨)

约2:18处出现的就是享誉全球的著名游戏吃豆人 Pac-Man
http://www.douban.com/subject/19975730/ 了,其后镜头里玩家排起的长队也证明了吃豆人在当时的火爆。而后在纽约人类与外星怪物的第二次遭遇战的内容也是吃豆人。

约2:36处美国未来总统玩的游戏是大蜜蜂 Galaga http://www.douban.com/subject/24530609/ 最开始关岛美军基地遭受的袭击也来自于这款游戏的怪物。并且在最后大决战的飞行怪物也应该有出自本游戏的。

约3:13处男主玩的,是著名的街机游戏大金刚 http://www.douban.com/subject/24530604/,它是任天堂早期热门作品之一,同时也是超级巨星马里奥的第一次登场,那时候他还没有名字,而只是一个来自意大利的水管工。后面1982年电子游戏比赛的决赛玩的也是这款游戏,而最后在外星人母船的决战内容也是这款游戏,可谓是整个影片中最为重要的游戏之一。

约3:20秒出现的操纵一个奇异生物跳上跳下的游戏是来自跳方块 Q*bert http://www.douban.com/subject/20395764/ 其中游戏主角Q伯特在后面作为第二次遭遇战的奖励品送给了人类,并成为了影片中的卖萌担当,并在最后圆了奇迹小子的梦想。值得一提的是,在2012年迪斯尼动画片《无敌破坏王》中,Q伯特也以完整的形象出现在影片中,逗留车站成了流浪汉。

约3:27处的飞行游戏是防卫者 Defender
http://www.douban.com/subject/25726828/ 雅达利的飞行射击游戏,也在其后的电子游戏比赛中露面。

插播题外话:之后未来总统在游戏厅夹起的面具来自于星球大战系列电影的著名人物楚巴卡Chewbacca,这个大家应该也都知道。

插播题外话2:电子游戏大赛主舞台放了五个游戏公司的条幅,分别是Nintendo、Namco、Atari、Taito、Konami,这五家为街机时代最成功的游戏公司,出品了无数经典的街机游戏,上面所提到的很多游戏都是由其出品的。而且这五家公司中有四家是日本公司,也说明了日本游戏界在整个世界电子游戏产业中所占的重要位置。

约6:09处的大妈玩的游戏是青蛙过河 Frogger
http://www.douban.com/subject/25728646/ 在今天依然是大家耳熟能详的经典游戏,在最后的大决战中,由一个汽车跳向另一个汽车最后被总统用吊车夹住的青蛙形象即来自于这款游戏。

约6:14处主角玩的射击游戏是著名的太空侵略者 Space Invaders
http://www.douban.com/subject/10755030/ 与吃豆人一样,这款游戏也可算得上是风靡全球,并改变了全球电子游戏产业的面貌。在两位玩家对美军特战队训练时,这款游戏也出现过,最后决战其中的一些飞行怪物也应该出现与此。

插播题外话3:约8:48处两位少年讨论的梦中情人,第一位是Olivia Newton-John,生于1948年,澳大利亚影视歌三栖明星,四次格莱美奖获得者,80年代正是其改走性感路线的时期,同时她也是联合国亲善大使,长期活跃在环保与动物权益保护的运动当中。她同时也是一名乳腺癌患者,但在于癌症的斗争中坚强地活了下来,如今依然活跃在公众视野当中。第二位是Samantha Fox,生于1966年,英国影视歌三栖明星,同时也是一位享誉全球的模特,故事发生的80年代正是年纪轻轻的她以模特身份出道,成为太阳报的整版女郎,也算是不可多得的性感尤物。第三位是Sheena Easton,苏格兰影视歌三栖明星(三位都是全能艺人啊),80年代正是其歌唱生涯渐入佳境的时期,这一时期她也为多部热门电影献唱,其中包括007系列电影《For Your Eyes Only》等等。第四位就是Madonna了,这个大家都熟悉,就不介绍了。之后镜头一转,两位中年大叔所谈论的三位艺人也都是现在当红艺人,也不多做赘述。

约14:16处,小男孩举出的两个“经典游戏”(其实应该是游戏系列)光晕 Halo系列与使命召唤Call of Duty系列,为当代最为火爆的FPS游戏系列之中的代表,其中Halo系列迄今为止正统作品四部,除此之外还有几部衍生作品,除了一代二代有PC版本外其他均为XBOX系列主机独占。Call of Duty系列迄今为止正统作品共发行了11部,除一代二代没有同世代主机版本,三代没有PC版本以外,其他均全平台通吃。

约14:23处,男主举出的三个“经典游戏”,前两个在上面均有介绍,第三个游戏是行星 Asteroids
http://www.douban.com/subject/25726434/ 也是经典的街机游戏,这款游戏在后面两位游戏玩家对美军特战队的训练中也有出现。

约33:33处在印度泰姬陵,外星人开始了第二次破坏,所采用的游戏形式是我们耳熟能详的打砖块游戏,就影片所呈现的画面来看,应该是超级打砖块 Arkanoid
http://www.douban.com/subject/25771945/ 的版本,不过最早的打砖块游戏来自于雅达利公司的打砖块 Breakout http://www.douban.com/subject/10799202/

约51:38,作为第一次遭遇战的奖品,一只抓着鸭子的狗被送到了跳健身操的老太太身边,这一形象出自小时候红白机的著名光枪射击游戏 打鸭子 Duck Hunt
http://www.douban.com/subject/26319367/ 想必很多玩家会对此记忆犹新吧,并且在80年代就能在家用机实现光枪控制,也是让我对任天堂感到非常之佩服。

约69:30处小男孩所玩的游戏是 最后生还者 The Last of Us
http://www.douban.com/subject/10794117/ 也算是本世纪最佳电子游戏作品的代表了,IGN满分评价,Q伯特对于这款游戏过于血腥的评价大概因为首先由于图像技术的进步使得游戏能做出更为逼真的效果,其次在Q伯特的那个年代无论是任天堂的FC还是街机都有相对严格的审查制度,大部分游戏的尺度都还是很小的。

约81:31处,从母船放出的一堆怪物中有一个骑着自行车的少年,这一形象出自送报童 Paperboy
http://www.douban.com/subject/10787908/ ,另外还有一个带有较长蓝色翅膀的怪物疑似出自不死鸟 Phoenix
http://www.douban.com/subject/25727617/

约81:54处,在跳跃的青蛙后面,有一个绿色的双足行走的大眼睛的类似于恐龙形象的外星人,出自充气狗 Dig Dug
http://www.douban.com/subject/25722706/ 中的怪物形象

约81:58处出现的机器人形象来自于跨平台(当时所说的跨平台包括跨数个品牌的电脑)游戏机器人2084 Robotron: 2084
http://www.douban.com/subject/25731829/

约82:15处出现的是无人不知无人不晓的游戏俄罗斯方块 Tetris
http://www.douban.com/subject/10736681/

约84:03处在背后有超级马里奥闪过http://www.douban.com/subject/10735077/

约93分钟处,地面战场最终围困的敌人里面有一个红绿黄配色的大机器人形象,位于整个敌人队伍的最后,我个人猜想这一形象应该出自我,机器人 I, Robot
http://www.douban.com/subject/25753858/ 这款游戏也是世界上第一款商用的带有完全多边形填充(主要是相对于之前只有矢量线条的伪3D)的3D图像游戏。

题外话4:电影中神奇小子所迷恋的Lady Lisa以及最后决战中攻击校车的忍者形象出自电影虚构的一款游戏Dojo Quest。虽然游戏是虚构的,但这个名字很难不让人联想起8位时代的著名RPG游戏系列 勇者斗恶龙 Dragon Quest http://www.douban.com/subject/10785774/

题外话5:影片结束的前半部分字幕,背景的图像是较为原汁原味的街机及第三世代家用机(以FC为代表)的8-bit图像,也即像素风格图像,大家可以在此领略一下当年电子游戏的风采。

JS中的for循环

for语法主要有如下四种用法:

1. for-of,是比较新for语法,但是兼容性有问题
for (var object of arrays) {}
(ES2015+ only)

2. Array#forEach,同样存在兼容性问题
Array.prototype.forEach.call(node.childNodes, function(child) {}
(ES5+ only)

3. for-in,其实是和for-i一样的用法,初学者会误以为是for-of
for (var key in arrays) {
var object = arrays[key];
}

4. for-i, 这个是最常用的用法,也是最推荐的用法
for (var i=0; i<arrays.length; i++) {
var object = arrays[i];
}

======================================================================

 

JavaScript has powerful semantics for looping through arrays and array-like objects. I've split the answer into two parts: Options for genuine arrays, and options for things that are just array-like, such as the arguments object, other iterable objects (ES2015+), DOM collections, and so on.

I'll quickly note that you can use the ES2015 options now, even on ES5 engines, by transpilingES2015 to ES5. Search for "ES2015 transpiling" / "ES6 transpiling" for more...

Okay, let's look at our options:

For Actual Arrays

You have three options in ECMAScript 5 ("ES5"), the version most broadly supported at the moment, and will soon have two more in ECMAScript 2015 ("ES2015", "ES6"), the latest version of JavaScript that vendors are working on supporting:

  1. Use forEach and related (ES5+)
  2. Use a simple for loop
  3. Use for-in correctly
  4. Use for-of (use an iterator implicitly) (ES2015+)
  5. Use an iterator explicitly (ES2015+)

Details:

1. Use forEach and related

If you're using an environment that supports the Array features of ES5 (directly or using a shim), you can use the new forEach (spec | MDN):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEach accepts an iterator function and, optionally, a value to use as this when calling that iterator function (not used above). The iterator function is called for each entry in the array, in order, skipping non-existent entries in sparse arrays. Although I only used one argument above, the iterator function is called with three: The value of each entry, the index of that entry, and a reference to the array you're iterating over (in case your function doesn't already have it handy).

Using forEach on a general-purpose web page still (as of March 2014) requires that you include a "shim" for it for browsers that don't support it natively, because IE8 and earlier don't have it (and they're used by somewhere between 7% and 21% of the global browser users depending on who you believe; that figure is skewed a bit by markedly higher use in China vs. elsewhere, always check your own stats to see what you need to support). But shimming/polyfilling it is easily done (search for "es5 shim" for several options).

forEach has the benefit that you don't have to declare indexing and value variables in the containing scope, as they're supplied as arguments to the iteration function, and so nicely scoped to just that iteration.

If you're worried about the runtime cost of making a function call for each array entry, don't be; details.

Additionally, forEach is the "loop through them all" function, but ES5 defined several other useful "work your way through the array and do things" functions, including:

  • every (stops looping the first time the iterator returns false or something falsey)
  • some (stops looping the first time the iterator returns true or something truthy)
  • filter (creates a new array including elements where the filter function returns true and omitting the ones where it returns false)
  • map (creates a new array from the values returned by the iterator function)
  • reduce (builds up a value by repeated calling the iterator, passing in previous values; see the spec for the details; useful for summing the contents of an array and many other things)
  • reduceRight (like reduce, but works in descending rather than ascending order)

2. Use a simple for loop

Sometimes the old ways are the best:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

If the length of the array won't change during the loop, and it's in performance-sensitive code (unlikely), a slightly more complicated version grabbing the length up front might be a tiny bit faster:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

And/or counting backward:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

But with modern JavaScript engines, it's rare you need to eke out that last bit of juice.

3. Use for-in correctly

You'll get people telling you to use for-in, but that's not what for-in is forfor-in loops through the enumerable properties of an object, not the indexes of an array. Up through ES5, the order is not guaranteed; as of ES2015, the order is guaranteed (by [[OwnPropertyKeys]][[Enumerate]], and the definition of for-in/for-of). (Details in this other answer.)

Still, it can be useful, particularly for sparse arrays, if you use appropriate safeguards:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

Note the two checks:

  1. That the object has its own property by that name (not one it inherits from its prototype), and
  2. That the key is a base-10 numeric string in its normal string form and its value is <= 2^32 - 2 (which is 4,294,967,294). Where does that number come from? It's part of the definition of an array index in the specification. Other numbers (non-integers, negative numbers, numbers greater than 2^32 - 2) are not array indexes. The reason it's 2^32 - 2 is that that makes the greatest index value one lower than 2^32 - 1, which is the maximum value an array's lengthcan have. (E.g., an array's length fits in a 32-bit unsigned integer.) (Props to RobG for pointing out in a comment on my blog post that my previous test wasn't quite right.)

That's a tiny bit of added overhead per loop iteration on most arrays, but if you have a sparse array, it can be a more efficient way to loop because it only loops for entries that actually exist. E.g., for the array above, we loop a total of three times (for keys "0""10", and "10000" — remember, they're strings), not 10,001 times.

Now, you won't want to write that every time, so you might put this in your toolkit:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

And then we'd use it like this:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

Or if you're interested in just a "good enough for most cases" test, you could use this, but while it's close, it's not quite correct:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

4. Use for-of (use an iterator implicitly) (ES2015+)

ES2015 adds iterators to JavaScript. The easiest way to use iterators is the new for-ofstatement. It looks like this:

var val;
var a = ["a", "b", "c"];
for (val of a) {
    console.log(val);
}

Output:

a
b
c

Under the covers, that gets an iterator from the array and loops through it, getting the values from it. This doesn't have the issue that using for-in has, because it uses an iterator defined by the object (the array), and arrays define that their iterators iterate through their entries (not their properties). Unlike for-in in ES5, the order in which the entries are visited is the numeric order of their indexes.

5. Use an iterator explicitly (ES2015+)

Sometimes, you might want to use an iterator explicitly. You can do that, too, although it's a lot clunkier than for-of. It looks like this:

var a = ["a", "b", "c"];
var it = a.values();
var entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

The iterator is a function (specifically, a generator) that returns a new object each time you call next. The object returned by the iterator has a property, done, telling us whether it's done, and a property value with the value for that iteration.

The meaning of value varies depending on the iterator; arrays support (at least) three functions that return iterators:

  • values(): This is the one I used above. It returns an iterator where each value is the value for that iteration.
  • keys(): Returns an iterator where each value is the key for that iteration (so for our aabove, that would be "0", then "1", then "2").
  • entries(): Returns an iterator where each value is an array in the form [key, value] for that iteration.

(As of this writing, Firefox 29 supports entries and keys but not values.)

For Array-Like Objects

Aside from true arrays, there are also array-like objects that have a length property and properties with numeric names: NodeList instances, the arguments object, etc. How do we loop through their contents?

Use any of the options above for arrays

At least some, and possibly most or even all, of the array approaches above frequently apply equally well to array-like objects:

  1. Use forEach and related (ES5+)

    The various functions on Array.prototype are "intentionally generic" and can usually be used on array-like objects via Function#call or Function#apply. (See the Caveat for host-provided objects at the end of this answer, but it's a rare issue.)

    Suppose you wanted to use forEach on a Node's childNodes property. You'd do this:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });

    If you're going to do that a lot, you might want to grab a copy of the function reference into a variable for reuse, e.g.:

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
  2. Use a simple for loop

    Obviously, a simple for loop applies to array-like objects.

  3. Use for-in correctly

    for-in with the same safeguards as with an array should work with array-like objects as well; the caveat for host-provided objects on #1 above may apply.

  4. Use for-of (use an iterator implicitly) (ES2015+)

    for-of will use the iterator provided by the object (if any); we'll have to see how this plays with the various array-like objects, particularly host-provided ones.

  5. Use an iterator explicitly (ES2015+)

    See #4, we'll have to see how iterators play out.

Create a true array

Other times, you may want to convert an array-like object into a true array. Doing that is surprisingly easy:

  1. Use the slice method of arrays

    We can use the slice method of arrays, which like the other methods mentioned above is "intentionally generic" and so can be used with array-like objects, like this:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);

    So for instance, if we want to convert a NodeList into a true array, we could do this:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));

    See the Caveat for host-provided objects below. In particular, note that this will fail in IE8 and earlier, which don't let you use host-provided objects as this like that.

  2. Use the spread operator (...)

    It's also possible to use the ES2015 spread operator, with JavaScript engines that support this feature:

    var trueArray = [...iterableObject];

    So for instance, if we want to convert a NodeList into a true array, with spread syntax this becomes quite succinct:

    var divs = [...document.querySelectorAll("div")];

Caveat for host-provided objects

If you use Array.prototype functions with host-provided array-like objects (DOM lists and other things provided by the browser rather than the JavaScript engine), you need to be sure to test in your target environments to make sure the host-provided object behaves properly. Most do behave properly (now), but it's important to test. The reason is that most of the Array.prototypemethods you're likely to want to use rely on the host-provided object giving an honest answer to the abstract [[HasProperty]] operation. As of this writing, browsers do a very good job of this, but the ES5 spec did allow for the possibility a host-provided object may not be honest; it's in §8.6.2(several paragraphs below the big table near the beginning of that section), where it says:

Host objects may implement these internal methods in any manner unless specified otherwise; for example, one possibility is that [[Get]] and [[Put]] for a particular host object indeed fetch and store property values but [[HasProperty]] always generates false.

(I couldn't find the equivalent verbiage in the ES2015 spec, but it's bound to still be the case.) Again, as of this writing the common host-provided array-like objects in modern browsers (NodeList instances, for instance) do handle [[HasProperty]] correctly, but it's important to test.