1. 程式人生 > >深入學習jquery原始碼之繼承框架的實現

深入學習jquery原始碼之繼承框架的實現

深入學習jquery原始碼之繼承框架的實現

繼承框架的實現

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function(){
  //initializing是為了解決我們之前說的繼承導致原型有多餘引數的問題。當我們直接將父類的例項賦值給子類原型時。是會呼叫一次父類的建構函式的。所以這邊會把真正的構造流程放到init函式裡面,通過initializing來表示當前是不是處於構造原型階段,為true的話就不會呼叫init。
  //fnTest用來匹配程式碼裡面有沒有使用super關鍵字。對於一些瀏覽器`function(){xyz;}`會生成個字串,並且會把裡面的程式碼弄出來,有的瀏覽器就不會。`/xyz/.test(function(){xyz;})`為true代表瀏覽器支援看到函式的內部程式碼,所以用`/\b_super\b/`來匹配。如果不行,就不管三七二十一。所有的函式都算有super關鍵字,於是就是個必定匹配的正則。
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
 
  // The base Class implementation (does nothing)
  // 超級父類
  this.Class = function(){};
 
  // Create a new Class that inherits from this class
  // 生成一個類,這個類會具有extend方法用於繼續繼承下去
  Class.extend = function(prop) {
    //保留當前類,一般是父類的原型
    //this指向父類。初次時指向Class超級父類
    var _super = this.prototype;
   
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    //開關 用來使原型賦值時不呼叫真正的構成流程
    initializing = true;
    var prototype = new this();
    initializing = false;
   
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      //這邊其實就是很簡單的將prop的屬性混入到子類的原型上。如果是函式我們就要做一些特殊處理
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          //通過閉包,返回一個新的操作函式.在外面包一層,這樣我們可以做些額外的處理
          return function() {
            var tmp = this._super;
           
            // Add a new ._super() method that is the same method
            // but on the super-class
            // 呼叫一個函式時,會給this注入一個_super方法用來呼叫父類的同名方法
            this._super = _super[name];
           
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            //因為上面的賦值,是的這邊的fn裡面可以通過_super呼叫到父類同名方法
            var ret = fn.apply(this, arguments);  
            //離開時 儲存現場環境,恢復值。
            this._super = tmp;
           
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
   
    // 這邊是返回的類,其實就是我們返回的子類
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
   
    // 賦值原型鏈,完成繼承
    Class.prototype = prototype;
   
    // 改變constructor引用
    Class.prototype.constructor = Class;
 
    // 為子類也新增extend方法
    Class.extend = arguments.callee;
   
    return Class;
  };
})();

初始化Class

(function(scope){
	var ListPager = Class.extend({
		init: function(){},
		render: function(){
			var _self = this;
			//彈出卡片介面的大小[60%,80%]
			_self.cardSize = _self.cardSize || ['80%','80%'];
			//彈出卡片介面方式:頁面層(這裡content是一個普通的String):1;iframe層(content是一個URl):2
			_self.cardPopStyle = _self.cardPopStyle || LayuiPopStyle.LAYUI_IFRAME_LAYER;
			_self.tableId = _self.container + "." + _self.primaryKey;
			//plugins 這個可以不傳,但是請不要傳空陣列過來
			var plugins = _self.plugins || ['table','form'];
			var p = _self.assemblyFormPrams();
			//利用layui 繪製列表  ( url : _self.url+"?decAjaxReq=yes", 給ajax請求加密)
			layui.use(plugins, function(){
				var option = $.extend({elem:  "#" + _self.container, 
									   url : _self.url, 
									   cols: _self.title,
									   method: RequestMethod.METHOD_POST,
									   id : _self.tableId,							
									   even: true,
									   page: true,									//是否顯示分頁
									   pageNum: 1,
									   limit: _self.pageSize, 						//每頁預設顯示的數量
									   limits:[5,10,15,20,30]}, _self.layOption);
				//展示已知資料
				layui.table.render(option);
				
				//渲染部分layui元件
				_self.initLayuiPlugin();
				
				//監聽工具條
				layui.table.on('tool(' + _self.container + ')', function(obj){ //注:tool是工具條事件名,test是table原始容器的屬性 lay-filter="對應的值"
					if(_self.hookMethod && $.isFunction(_self.hookMethod)){
						 _self.hookMethod(obj); 					//回撥 子類中的 鉤子方法
					}
				});
				//複選框事件選中以後回撥
				layui.table.on('checkbox(' + _self.container + ')', function(obj){
					if(_self.tableAfterChecked && $.isFunction(_self.tableAfterChecked)){
						 _self.tableAfterChecked(obj); 					//回撥 子類中的 鉤子方法
					}
				});
			});
			//介面繪製完成, 初始化介面事件
			_self.initEvent();
		},
		initLayuiPlugin: function(){
			var _self = this;
		},
		initEvent: function(){
			var _self = this;
			//列表 增刪改查
			$("div[name='listBtns'] button").unbind('click').bind('click', function(){
				var action = "_self." + $(this).attr("action");
				eval(action);
			});
			
			//列表查詢、重置
			$("div button[type='button']",$("#" + _self.container + "-QueryForm"))
				.unbind('click')
				.bind('click', function(){
				var action = "_self." + $(this).attr("action");
				eval(action);
			});
		},
		assemblyFormPrams: function(){														//組裝列表模糊查詢表單資料
			var _self = this;
			var formParam = $("#" + _self.container + "-QueryForm").serializeArray(),
			reqParam = {};
			for(var o in formParam){
				if(formParam[o]["name"]){
					reqParam[formParam[o]["name"]] = formParam[o]["value"];
				}
			}
			return reqParam;
		},
		listQuery: function(){
			var _self = this;
			layui.table.reload(_self.tableId, {
                where: _self.assemblyFormPrams()
            });
		},
		/**
		 * 獲取選中的資料
		 */
		getSelectRows: function(){
			var checkStatus = layui.table.checkStatus(this.tableId);
			return checkStatus.data;
		},
		getSelectIds: function(){
			var data = this.getSelectRows();
			var ids = [];
			if($.isEmptyArray(data))
				return ids;
			for(var i = 0; i < data.length; i++){
				ids.push(data[i][this.primaryKey]);
			}
			return ids;
		},
		view: function(curDom, event){
			var _self = this;
			var data = _self.getSelectRows();
			if (data.length != 1) {  
				layer.msg("請選中一條資料進行檢視");
	            return;
	        }
			var url = $(curDom).attr("url") + "?act=" + WebConst.READ + "&entityId=" + data[0][_self.primaryKey];
			var title = $(curDom).attr("title");
			var layIndex = layer.open({
			      type: this.cardPopStyle,
			      title: title,
			      maxmin: true,
				  shadeClose: true, //開啟遮罩關閉
				  area : this.cardSize,
			      content: this.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER ? $.loadHtml(url) : url,
			      success: function(layero){
			    	  	layero = _self.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER ? layero : layer.getChildFrame('body', layIndex);
		        		if(_self.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER){
		        			layero.setValues(data[0]);
		        		}
		        		layui.form.render();
			      }
			});
		},
		add: function(curDom, event){
			var _self = this;
			
			//新增之前回調函式
			if(_self.beforeAdd && $.isFunction(_self.beforeAdd)){
				_self.beforeAdd();
			}
			
			var url = $(curDom).attr("url") + "?act=" + WebConst.ADD;
			var title = $(curDom).attr("title");
			layer.open({
				type: this.cardPopStyle,
				title: title,
				area: this.cardSize,
				maxmin: true,
				shadeClose: false, //開啟遮罩關閉
				content: this.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER ? $.loadHtml(url) : url,
				success : function(layero){
					layui.form.render();
				},
	        	end: function(){		//銷燬列表回撥方法
	        		layui.table.reload(_self.tableId)
	        		if(_self.closeCard && $.isFunction(_self.closeCard)){
	        			return _self.closeCard();
	    			}
	        		return false;
	        	},
	        	cancel: function(){
	        		
	        	}
			});
		},
		edit: function(curDom, event){
			var _self = this;
			var data = _self.getSelectRows();
			if (data.length != 1) {  
				layer.msg("請選中一條資料進行修改");
	            return;
	        }
			
			//修改之前回調函式
			if(_self.beforeUpd && $.isFunction(_self.beforeUpd)){
				_self.beforeUpd();
			}
			
			var url = $(curDom).attr("url") + "?act=" + WebConst.EDIT + "&entityId=" + data[0][_self.primaryKey];
			var title = $(curDom).attr("title");
	        var layIndex = layer.open({
	        	type: this.cardPopStyle,
	        	title: title,
	        	maxmin: true,
				shadeClose: false, //開啟遮罩關閉
	        	area : this.cardSize,
	        	content: this.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER ? $.loadHtml(url) : url,
	        	success: function(layero){
	        		layero = _self.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER ? layero : layer.getChildFrame('body', layIndex);
	        		if(_self.cardPopStyle == LayuiPopStyle.LAYUI_CONTENT_LAYER){
	        			layero.setValues(data[0]);
	        			layui.form.render();
	        		}
	        	},
	        	end: function(){		//銷燬列表回撥方法
	        		layui.table.reload(_self.tableId)
	        		if(_self.closeCard && $.isFunction(_self.closeCard)){
	        			return _self.closeCard();
	    			}
	        		return false;
	        	},
	        	cancel: function(){ 	//點選左上角關閉按鈕回撥方法
	        		if(_self.cardPopStyle == LayuiPopStyle.LAYUI_IFRAME_LAYER){  					//從列表呼叫卡片頁面資料
	        			var frameId = document.getElementById('layui-layer' + layIndex).getElementsByTagName("iframe")[0].id,
	        			closeCallback = $('#'+frameId)[0].contentWindow.beforeClose;
		        		if(closeCallback && $.isFunction(closeCallback)){
		        			 return closeCallback();
		        		}
	        		}
	        	}
	        	
	        });
		},
		del: function(curDom, event){
			var _self = this;
			var data = _self.getSelectIds();
			if(data.length == 0){
				layer.msg("請至少選擇一條需要刪除的資料");
				return;
			}
			var url = $(curDom).attr("url") + "?act=" + WebConst.DELETE;
			layer.confirm('確定刪除嗎', function(index){
				layer.close(index);
				$.ajaxReq(url,$.toJSON(data),function(){
					layui.table.reload(_self.tableId)
				});
			});
		}
	});
	scope.ListPager = ListPager;
})(window);

繼承的實現

ShowPipeList = ListPager.extend({
	
	init : function(container, primaryKey, url){
		debugger;
		//列表容器
		this.container = container;
		//主鍵欄位
		this.primaryKey = primaryKey;
		//資料請求地址
		this.url = url;
		//介面需要引用的外掛
		this.plugins = ['table', 'element', 'form', 'laydate', 'layer','carousel'];
//		//彈出卡片介面的大小[60%,80%]
//		this.cardSize = ['100%','100%'];
//		//彈出卡片介面方式:頁面層(這裡content是一個普通的String):1;iframe層(content是一個URl):2
		this.cardPopStyle = 2;
		//每頁大小 
		this.pageSize = 5;
		//列表頭
		this.title = [[
			
			{field: 'startTime', title: '開始時間', width: 160},
			
			{field: 'endTime', title: '結束時間', width: 160},
			
			
	     ]]; 
		//外掛是基於layui封裝的, layOption是對layui的引數擴充套件(具體api參照layui屬性)
		this.layOption = {
				
		};
	},
	initLayuiPlugin: function(){
		this._super();
		
		  var table = layui.table;
		  var laydate = layui.laydate;
		    //時間選擇器
		  laydate.render({
		    elem: '#test5'
		    ,type: 'time'
		    ,theme: '#050a3c'
		  });
		  
		  laydate.render({
			    elem: '#test6'
			    ,type: 'time'
			    ,theme: '#050a3c'
		});
	},
	initEvent: function(){
		this._super();
		//TODO 父類僅僅綁定了工具條通用按鈕事件, 如有其他事件請在這裡定義
	},
	hookMethod:function(obj){
		
	 	var data = obj.data,
	 	url = CONTEXT_PATH + '/cusviews/pipe/listPipePoint';
		var param = {
				cobwebId : data.cobwebId
		}
		var retData = $.getData(url,param);
	}
});
$(function(){
	var url = CONTEXT_PATH + '/cusviews/pipe/listPipe';
	var showPipeList = new ShowPipeList("pipeList", "cobwebId", url);
	showPipeList.render();
	$(".tc_box .cbar").scrollBar();
})

繼承實現的過程