クロージャを使って、インターフェースを合わせこむ
公開済みの関数
以下の、Ajaxなラッパー関数があったとしましょう。
function ajax(url, callback) { var xhr = uuw.XMLHttpRequest ? new XMLHttpRequest() : uuw.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : null; xhr.onreadystatechange = function() { ... callback(); ... } xhr.open("GET", url, true); // async xhr.send(null); return xhr; }
XMLHttpRequestオブジェクトが戻されるので、リクエストの中断はこう書けます。
var xhr = ajax("http://example.com/", function() { alert("done"); }); xhr.abort();
公開済みの関数にI/Fを合わせる
仮にあなたがライブラリ作者だとして、
1. AjaxとJSONPって、出来ることが似てるなぁ
2. AjaxがダメならJSONPで代行すれば使いやすくなるよね?
3. よし、JSONPをラップする関数を作ってみよう。I/Fは、既存のajax()のソレに合わせてみよう
4. あれ? ajax()は“XMLHttpRequestオブジェクトを返す”ってI/Fだったね。
5. I/Fレベルで違いすぎるのはダメだし、ajax()のI/Fを変更するのは本末転倒だ。どうしよう?
function jsonp(url, callback) { var head = document.documentElement.firstChild; var e = document.createElement("script"); jsonp.fn = function(text) { callback(text); }; e.type = "text/javascript"; e.charset = "utf-8"; e.src = url + "&callback=jsonp.fn"; head.appendChild(e); }
このようなコードを実装した時点でライブラリ作者はハタと困ります。
どうすれば適切なオブジェクトを返し、リクエストの中断機能をJSONPでも提供できるのか?
ここ大事
jsonp()に数行追加すればabort()が可能になります。
// ajax()とI/Fを合わせるためにクロージャなオブジェクトを返しabort()を可能にする function F() {}; F.prototype.abort = function() { e.src = ""; head.removeChild(e); }; return new F();
こんな感じに。
var jp = jsonp("http://example.com/.../...?hoge=huga", function() { alert("done"); }); jp.abort();
テストコード
<html> <head> <title>uupaa's diary</title> </head> <body> <script> function jsonp(url, callback) { var head = document.documentElement.firstChild; var e = document.createElement("script"); jsonp.fn = function(text) { callback(text); }; e.type = "text/javascript"; e.charset = "utf-8"; e.src = url + "&callback=jsonp.fn"; head.appendChild(e); function F() {}; F.prototype.abort = function() { e.src = ""; head.removeChild(e); }; return new F(); } </script> <script> var targetURL = "http://d.hatena.ne.jp/uupaa/20080420/1208636929"; var url = "http://b.hatena.ne.jp/entry/json/?url=" + encodeURIComponent(targetURL); var jp = jsonp(url, function(obj) { alert("title:" + obj.title); }); jp.abort(); </script> </body> </html>