Category: RIA Technology

JSONP enable you to send request to other domain beyond the same domain policy. And the corss domain alternative to AJAX.

JSONP is natively supported by JQuery, Zepto (alternative to JQuery for mobile usages). But When you require JSONP and DO NOT want to add the huge JQuery to your code base. This library will be useful.

var JSONP = (function () {
    var counter = 0,
        head, query, key, window = this;

    function load(url) {
        var script = document.createElement('script'),
        var done = false;
        script.src = url;
        script.async = true;

        script.onload = script.onreadystatechange = function () {
            if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
                done = true;
                script.onload = script.onreadystatechange = null;
                if (script && script.parentNode) {
                    script.parentNode.removeChild(script);
                }
            }
        };
        if (!head) {
            head = document.getElementsByTagName('head')[0];
        }
        head.appendChild(script);
    }

    function jsonp(url, params, error, callback) {
        query = "?";
        params = params || {};
        for (key in params) {
            if (params.hasOwnProperty(key)) {
                query += encodeURIComponent(key) + "=" + encodeURIComponent(params[key]) + "&";
            }
        }
        var jsonp = "json" + (++counter);
        window[jsonp] = function (data) {
            callback(data);
            try {
                delete window[jsonp];
            } catch (e) {}
            window[jsonp] = null;
        };

        load(url + query + "callback=" + jsonp);

        error = error ||
        function () {};

        window.setTimeout(function () {
            if (typeof window[jsonp] == "function") {

                // replace success with null callback in case the request is just very latent.
                window[jsonp] = function (data) {
                    try {
                        delete window[jsonp];
                    } catch (e) {}
                    window[jsonp] = null;
                };

                // call the error callback
                error();

                // set a longer timeout to safely clean up the unused callback.
                window.setTimeout(function () {
                    if (typeof window[jsonp] == "function") {
                        try {
                            delete window[jsonp];
                        } catch (e) {}
                        window[jsonp] = null;
                    };
                }, 120000);
            };
        }, 10000);

        return jsonp;
    }
    return {
        get: jsonp
    };
}());

/*
Example:
----------------

var url = 'http://blog.eood.cn/api';
var error = function() {alert('error');};
var success = function(data) {
        // process the data
};
JSONP.get( url, {'parm1': 'parm1_value', 'parm2': 'parm2_value'}, error, success);

*/

客户端计算能力的增强,使 WEB 技术发展的天枰又开始偏向客户端运算。比如 Facebook 将模板渲染的运算放到了浏览器,浏览器先将模板下载到本地,再按需从服务器请求 JSON 格式的数据,然后在浏览器中渲染。对于提供给搜索引擎抓取的内容仍然可以在服务器端拼接完成后再输出。为了提高开发效率,可以考虑在服务器端也用 Javascript 或者其他语言拼接渲染 JSON 格式的数据和与客户端一致的模板。Mustache 就提供了这样的一种机制,并且支持大部分 WEB 语言。但是比较高效的服务器端方案仍然是用 Javascript 来写,用 NodeJS 向浏览器输出 JSON 数据和重用浏览器模板渲染代码向搜索引擎输出完整 HTML 数据。

NodeJS 和倍受推崇的 WEB 服务器 Nginx 类似,都采用 Event-driven 模型,对于单核服务器,只需要开一个进程就可以同时 serve 成千上万的客户。

不仅仅是 NodeJS, 对于 RIA 来说,支持最完善的仍然是 Javascript,基于 XUL 的客户端方案也需要 Javascript 作为开发语言,并且 Javascript 支持常见的高级特性比如 lambda,map-reduce 。

所以 Javascript 在服务器端和客户端都有很大的前景。设计模式既是解决问题的template,也是代码的组织方式。略去常见模式,仅仅涉及几个在 Javascript 中比较常用的模式:

用 prototype 的构建模式:

function Car(model, year, miles){
   this.model = model;
   this.year    = year;
   this.miles  = miles;
}

/*
 Note here that we are using Object.prototype.newMethod rather than
 Object.prototype so as to avoid redefining the prototype object
*/
Car.prototype.toString = function(){
		return this.model + " has done " + this.miles + " miles";
};

var civic = new Car("Honda Civic", 2009, 20000);
var mondeo = new Car("Ford Mondeo", 2010, 5000);

console.log(civic.toString());

单件模式:

var Singleton =(function(){
	var instantiated;
	function init (){
		/*singleton code here*/
		return {
			publicMethod:function(){
				console.log('hello world')
			},
			publicProperty:'test'
		}
	}

	return {
		getInstance :function(){
			if (!instantiated){
				instantiated = init();
			}
			return instantiated;
		}
	}
})()

/*calling public methods is then as easy as:*/
Singleton.getInstance.publicMethod();

可以传递参数的单件模式:

var SingletonTester = (function(){

  //args: an object containing arguments for the singleton
  function Singleton(args) {

   //set args variable to args passed or empty object if none provided.
    var args = args || {};
    //set the name parameter
    this.name = 'SingletonTester';
    //set the value of pointX
    this.pointX = args.pointX || 6; //get parameter from arguments or set default
    //set the value of pointY
    this.pointY = args.pointY || 10;  

  }

 //this is our instance holder
  var instance;

 //this is an emulation of static variables and methods
  var _static = {
    name: 'SingletonTester',
   //This is a method for getting an instance

   //It returns a singleton instance of a singleton object
    getInstance: function (args){
      if (instance === undefined) {
        instance = new Singleton(args);
      }
      return instance;
    }
  };
  return _static;
})();

var singletonTest = SingletonTester.getInstance({pointX: 5});
console.log(singletonTest.pointX); // outputs 5

模块模式:

var someModule = (function(){

  //private attributes
  var privateVar = 5;

  //private methods
  var privateMethod = function(){
  return 'Private Test';
  };

  return {
        //public attributes
        publicVar    : 10,
        //public methods
        publicMethod : function(){
        return ' Followed By Public Test ';
         }, 

         //let's access the private members
          getData : function(){
          return privateMethod() + this.publicMethod() + privateVar;
         }
       }
    })(); //the parens here cause the anonymous function to execute and return

someModule.getData();

观察者模式:

它适合pub-sub进行信息分发

function Observer(){
    this.functions = [];
}

Observer.prototype = {
    subscribe : function(fn) {
        this.functions.push(fn);
    },

    unsubscribe : function(fn) {
        this.functions = this.functions.filter(
            function(el) {
                if ( el !== fn ) {
                    return el;
                }
            }
        );
    },

    update : function(o, thisObj) {
        var scope = thisObj || window;
        this.functions.forEach(
            function(el) {
                el.call(scope, o);
            }
        );
    }
};
/*
    * Publishers are in charge of "publishing" eg: Creating the Event
    * They're also in charge of "notifying" (firing the event)
*/
var obs = new Observer;
obs.update('here is some test information');

/*
    * Subscribers basically... "subscribe" (or listen)
    * And once they've been "notified" their callback functions are invoked
*/
var fn = function() {
    // my callback stuff
};
obs.subscribe(fn);

/*
    * Unsubscribe if you no longer wish to be notified
*/
obs.unsubscribe(fn);

JQuery 中的遍历:

  $.each(function(){});
  $('.items').each(function(){});

更多有用资源

http://www.hunlock.com/blogs/Functional_Javascript

http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#designpatternsjavascript

Backbone 是一个前端 JS 代码 MVC 框架,被著名的 37signals 用来构建他们的移动客户端。它不可取代 Jquery,不可取代现有的 template 库。而是和这些结合起来构建复杂的 web 前端交互应用。

如果项目涉及大量的 javascript 代码,实现很多复杂的前端交互功能,首先你会想到把数据和展示分离。使用 Jquery 的 selector 和 callback 可以轻松做到这点。但是对于富客户端的WEB应用大量代码的结构化组织非常必要。

Backbone 就提供了 javascript 代码的组织的功能。Backbone 主要包括 models, collections, views 和 events, controller 。

Models 用来创建数据,校验数据,存储数据到服务器端, Collections 包含你创建的 functions ,Views 用来展示数据。

Models 还可以绑定事件。比如用户动作变化触发 models 的 change 事件,所有展示此model 数据的 views 都会接收到 这个 change 事件,进行重绘。

事件的绑定和触发

以下是object 绑定 alert 事件和匿名回调函数的代码例子,object 之后 触发 alert 事件,并且传入参数 “an event”

var object = {};
 
_.extend(object, Backbone.Events);
 
object.bind("alert", function(msg) {
  alert("Triggered " + msg);
});
 
object.trigger("alert", "an event");

假如你的网页上事件很多也可以用proxy的方式来分发所有事件:

proxy.bind("all", function(eventName) {
  object.trigger(eventName);
});

Backbone 的 Models 是应用的核心

他包含了数据对象的属性,操作数据对象的函数。还实现和服务端交互的动作。

以下是定时从服务器端更新 channel 的数据:

// Poll every 10 seconds to keep the channel model up-to-date.
setInterval(function() {
  channel.fetch();
}, 10000);

以下是存储 book 的数据,这里通过重载 sync 函数,只让数据 alert 出来,sync 中的默认触发事件包括 fetch save refresh

Backbone.sync = function(method, model) {
  alert(method + ": " + JSON.stringify(model));
};
 
var book = new Backbone.Model({
  title: "The Rough Riders",
  author: "Theodore Roosevelt"
});
 
book.save();

Backbone 的 Controller 用来对 URL 和事件进行绑定

以下下例子中,分别将不同的以#开头的 URL 片段 绑定到不同的函数,实现服务器端 MVC 模型中的 router 一样的功能

var Workspace = Backbone.Controller.extend({
 
  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/kiwis
    "search/:query/p:page": "search"   // #search/kiwis/p7
  },
 
  help: function() {
    ...
  },
 
  search: function(query, page) {
    ...
  }
 
});

值得提出的是 Backbone 的 router 也支持正则表达式的匹配

initialize: function(options) {
 
  // Matches #page/10, passing "10"
  this.route("page/:number", "page", function(number){ ... });
 
  // Matches /117-a/b/c/open, passing "117-a/b/c"
  this.route(/^(.*?)\/open$/, "open", function(id){ ... });
 
}

Backbone 的 Sync 默认通过调用Jquery的ajax方法来实现和服务器端的交互,实现数据的 CURD

比如 fetch 方法会触发 read 事件

Backbone 的 Views 用来接收用户的操作和修改 Model 的数据 ,另外通过 render 来展示数据

默认 render 并没有实现,你可以用 Mustache.js 或者 Underscore.js 来实现。

以下是接收用户操作的代码例子:

var DocumentView = Backbone.View.extend({
 
  events: {
    "dblclick"                : "open",
    "click .icon.doc"         : "select",
    "contextmenu .icon.doc"   : "showMenu",
    "click .show_notes"       : "toggleNotes",
    "click .title .lock"      : "editAccessLevel",
    "mouseover .title .date"  : "showTooltip"
  },
 
  render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    return this;
  },
 
  open: function() {
    window.open(this.model.get("viewer_url"));
  },
 
  select: function() {
    this.model.set({selected: true});
  },
 
  ...
 
});

以下是数据渲染 render 的例子

var Bookmark = Backbone.View.extend({
  render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    return this;
  }
});

参考

http://documentcloud.github.com/backbone/

http://documentcloud.github.com/backbone/examples/todos/index.html