@@ -438,4 +438,106 @@ getdata.php可以是任何类型的页面或者脚本。callback参数指定用
438438
439439(译注:原文这里说得不是太明白。JSONP的返回内容如上面的代码片段,它的工作原理是在页面中动态插入一个脚本,这个脚本的内容是函数调用+JSON数据,其中要调用的函数是在页面中已经定义好的,数据以参数的形式存在。一般情况下数据由服务端动态生成,而函数由页面生成,为了使返回的脚本能调用到正确的函数,在请求的时候一般会带上callback参数以便后台动态返回处理函数的名字。)
440440
441+ ####JSONP示例:井字棋
442+
443+ 我们来看一个使用JSONP的井字棋游戏示例,玩家就是客户端(浏览器)和服务器。它们两者都会产生1到9之间的随机数,我们使用JSONP去取服务器产生的数字(图8-2)。
444+
445+ 你可以在< http://jspatterns.com/book/8/ttt.html > 玩这个游戏。
446+
447+ ![ 图8-2 使用JSONP的井字棋游戏] ( ./figure/chapter8/8-2.jpg )
448+
449+ 图8-2 使用JSONP的井字棋游戏
450+
451+ 界面上有两个按钮:一个用于开始新游戏,一个用于取服务器下的棋(客户端下的棋会在一定数量的延时之后自动进行):
452+
453+ <button id="new">New game</button>
454+ <button id="server">Server play</button>
455+
456+ 界面上包含9个单元格,每个都有对应的id,比如:
457+
458+ <td id="cell-1"> </td>
459+ <td id="cell-2"> </td>
460+ <td id="cell-3"> </td>
461+ ...
462+
463+ 整个游戏是在一个全局对象ttt中实现:
464+
465+ var ttt = {
466+ // cells played so far
467+ played:[ ] ,
468+
469+ // shorthand
470+ get: function (id) {
471+ return document.getElementById(id);
472+ },
473+
474+ // handle clicks
475+ setup: function () {
476+ this.get('new').onclick = this.newGame;
477+ this.get('server').onclick = this.remoteRequest;
478+ },
479+
480+ // clean the board
481+ newGame: function () {
482+ var tds = document.getElementsByTagName("td"),
483+ max = tds.length,
484+ i;
485+ for (i = 0; i < max; i += 1) {
486+ tds[i].innerHTML = " ";
487+ }
488+ ttt.played = [];
489+ },
490+
491+ // make a request
492+ remoteRequest: function () {
493+ var script = document.createElement("script");
494+ script.src = "server.php?callback=ttt.serverPlay&played=" + ttt.played.join(',');
495+ document.body.appendChild(script);
496+ },
497+
498+ // callback, server's turn to play
499+ serverPlay: function (data) {
500+ if (data.error) {
501+ alert(data.error);
502+ return;
503+ }
504+
505+ data = parseInt(data, 10);
506+ this.played.push(data);
507+
508+ this.get('cell-' + data).innerHTML = '<span class="server">X<\/span>';
509+
510+ setTimeout(function () {
511+ ttt.clientPlay();
512+ }, 300); // as if thinking hard
513+ },
514+
515+ // client's turn to play
516+ clientPlay: function () {
517+ var data = 5;
518+
519+ if (this.played.length === 9) {
520+ alert("Game over");
521+ return;
522+ }
523+
524+ // keep coming up with random numbers 1-9
525+ // until one not taken cell is found
526+ while (this.get('cell-' + data).innerHTML !== " ") {
527+ data = Math.ceil(Math.random() * 9); }
528+ this.get('cell-' + data).innerHTML = 'O';
529+ this.played.push(data);
530+ }
531+ };
532+
533+ ttt对象维护着一个已经填过的单元格的列表ttt.played,并且将它发送给服务器,这样服务器就可以返回一个没有玩过的数字。如果有错误发生,服务器会像这样响应:
534+
535+ ttt.serverPlay({"error": "Error description here"});
536+
537+ 如你所见,JSONP中的回调函数必须是公开的并且全局可访问的函数,它并不一定要是全局函数,也可以是一个全局对象的方法。如果没有错误发生,服务器将会返回一个函数调用,像这样:
538+
539+ ttt.serverPlay(3);
540+
541+ 这里的3是指3号单元格是服务器要下棋的位置。在这种情况下,数据非常简单,甚至都不需要使用JSON格式,只需要一个简单的值就可以了。
542+
441543