实现 JSONP
跨域Ajax有两种方式,<img src="out_url"/> 和 <sccript src="outside_url"></script>
JSONP使用的就是 script方式。
jsonp.js
代码
var jsonp = (function(window){
var document = window.document;
var extend = function(obj, attrs){
for(var name in attrs){
obj[name] = attrs[name];
}
};
var Class={
create: function(class_define){
var class_ = function(){};
extend(class_, class_define);
class_.create = function(attrs){
var obj = new class_();
extend(obj, attrs);
if(obj.init){obj.init();}
return obj;
};
return class_;
}
};
var is_ie = !!window.ActiveXObject;
var jsonp_imp=null;
var jsonp = function () {
jsonp_imp.apply(null, arguments);
};
extend(jsonp, {
guid:0,
requests:{},
avail_tag:[],
request_timeout:5 //seconds
});
var removeChildSafe = function(parent, node) {
for(var i=0, len=node.childNodes.length; i<len; ++i) {
removeChildSafe(node, node.childNodes[i]);
}
parent.removeChild(node);
};
var Request = Class.create({
prototype:{
remove:function(){
var js = this.js;
if(is_ie) {
this.finished = 1;
this.on_ready_state_change();
js.detachEvent("onreadystatechange", this.on_ready_state_change);
this.js = null;
}
removeChildSafe(js.parentNode, js);
js = null;
delete jsonp.requests[this.id];
},
on_complete:function(){
if(this.success) {
this.success();
this.success = null;
}
},
on_timeout:function(){
var state = this.js.readyState;
if( state != 'complete' && state != 'loaded' && this.failed) {
this.failed();
this.failed = null;
}
state = null;
this.remove();
},
init:function(){
var request = this;
if(is_ie) {
request.on_ready_state_change = function(){
if(request.finished) {
request = null;
return;
}
if(request.js.readyState=='complete' || request.js.readyState=='loaded') {
request.on_complete();
}
};
this.js.attachEvent("onreadystatechange", request.on_ready_state_change);
} else {
this.js.onload = function(){
request.on_complete();
};
}
setTimeout(function(){request.on_timeout();}, request.timeout);
}
}
});
var head = document.getElementsByTagName('head')[0];
jsonp_imp = function(url, charset, timeout, failed, success) {
var js = document.createElement('script');
head.appendChild(js);
if(!charset) {charset = 'utf-8';}
var id = jsonp.guid++;
if(!timeout) {timeout = jsonp.request_timeout*1000;}
var now = new Date();
var request = Request.create({
id: id,
url: url,
charset: charset,
failed: failed,
success: success,
js: js,
stat_time: now,
timeout: timeout
});
jsonp.requests[id] = request;
js.charset = charset;
js.src = url;
return id;
};
return jsonp;
})(window);
var document = window.document;
var extend = function(obj, attrs){
for(var name in attrs){
obj[name] = attrs[name];
}
};
var Class={
create: function(class_define){
var class_ = function(){};
extend(class_, class_define);
class_.create = function(attrs){
var obj = new class_();
extend(obj, attrs);
if(obj.init){obj.init();}
return obj;
};
return class_;
}
};
var is_ie = !!window.ActiveXObject;
var jsonp_imp=null;
var jsonp = function () {
jsonp_imp.apply(null, arguments);
};
extend(jsonp, {
guid:0,
requests:{},
avail_tag:[],
request_timeout:5 //seconds
});
var removeChildSafe = function(parent, node) {
for(var i=0, len=node.childNodes.length; i<len; ++i) {
removeChildSafe(node, node.childNodes[i]);
}
parent.removeChild(node);
};
var Request = Class.create({
prototype:{
remove:function(){
var js = this.js;
if(is_ie) {
this.finished = 1;
this.on_ready_state_change();
js.detachEvent("onreadystatechange", this.on_ready_state_change);
this.js = null;
}
removeChildSafe(js.parentNode, js);
js = null;
delete jsonp.requests[this.id];
},
on_complete:function(){
if(this.success) {
this.success();
this.success = null;
}
},
on_timeout:function(){
var state = this.js.readyState;
if( state != 'complete' && state != 'loaded' && this.failed) {
this.failed();
this.failed = null;
}
state = null;
this.remove();
},
init:function(){
var request = this;
if(is_ie) {
request.on_ready_state_change = function(){
if(request.finished) {
request = null;
return;
}
if(request.js.readyState=='complete' || request.js.readyState=='loaded') {
request.on_complete();
}
};
this.js.attachEvent("onreadystatechange", request.on_ready_state_change);
} else {
this.js.onload = function(){
request.on_complete();
};
}
setTimeout(function(){request.on_timeout();}, request.timeout);
}
}
});
var head = document.getElementsByTagName('head')[0];
jsonp_imp = function(url, charset, timeout, failed, success) {
var js = document.createElement('script');
head.appendChild(js);
if(!charset) {charset = 'utf-8';}
var id = jsonp.guid++;
if(!timeout) {timeout = jsonp.request_timeout*1000;}
var now = new Date();
var request = Request.create({
id: id,
url: url,
charset: charset,
failed: failed,
success: success,
js: js,
stat_time: now,
timeout: timeout
});
jsonp.requests[id] = request;
js.charset = charset;
js.src = url;
return id;
};
return jsonp;
})(window);
测试代码:
jsonp("http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.js",
null,
function(){alert("timeout");},
function(){alert("load ok");}
);
null,
function(){alert("timeout");},
function(){alert("load ok");}
);
IE6内存泄漏测试:
incr.js
incr();
test_ie6.html
<html>
<head>
<script src='jsonp.js'></script>
</head>
<body>
<div id="t">ok</div>
<script defer="defer">
var i = 0;
var incr = function(){
++i;
document.getElementById('t').innerHTML=i;
};
setInterval(function(){jsonp("incr.js");},1000);
</script>
</body>
</html>
<head>
<script src='jsonp.js'></script>
</head>
<body>
<div id="t">ok</div>
<script defer="defer">
var i = 0;
var incr = function(){
++i;
document.getElementById('t').innerHTML=i;
};
setInterval(function(){jsonp("incr.js");},1000);
</script>
</body>
</html>
IE6下内存泄露,还没有找到比较好方法,不过泄露的比较少1K次请求1M左右。
Jquery等很多库在IE6下都有泄漏。IE6真的应该淘汰了。