@@ -438,4 +438,106 @@ getdata.php可以是任何类型的页面或者脚本。callback参数指定用
438
438
439
439
(译注:原文这里说得不是太明白。JSONP的返回内容如上面的代码片段,它的工作原理是在页面中动态插入一个脚本,这个脚本的内容是函数调用+JSON数据,其中要调用的函数是在页面中已经定义好的,数据以参数的形式存在。一般情况下数据由服务端动态生成,而函数由页面生成,为了使返回的脚本能调用到正确的函数,在请求的时候一般会带上callback参数以便后台动态返回处理函数的名字。)
440
440
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
+
441
543
0 commit comments