不二 发布的文章

第四章:控制器和状态《Developing JavaScript Web Applications》学习笔记

模块模式

模块模式是用来封装逻辑并避免全局命名空间污染的好方法。使用匿名函数可以做到这一点,匿名函数也是JavaScript 中被证明最优秀的特性之一。通常是创建一个匿名函数并立即执行它。在匿名函数里的逻辑都在闭包里运行,为应用中的变量提供了局部作用域和私有的运行环境:

(function(){
/* ... */
})();

在执行这个匿名函数之前,我们用一对括号() 将它包起来。这样才能让JavaScript 解释器正确地将这段代码解析为一个语句。

全局导入和导出

可以将页面的window 导入我们的模块,直接给它定义属性,通过这种方式可以暴露全局变量:

var exports=this;
(function($){
    exports.Foo = "wem";
})(jQuery);

这里我们使用的变量名叫exports,用它来暴露全局变量,这样代码看起来更干净易读,可以直接看出模块创建了哪些全局变量。

一个完整的控制器

(function($, exports){
      var mod = {};
  mod.create = function(includes){
    var result = function(){
      this.init.apply(this, arguments);
    };
    
    result.fn = result.prototype;
    result.fn.init = function(){};
    
    result.proxy = function(func){ return $.proxy(func, this); };
    result.fn.proxy = result.proxy;

    result.include = function(ob){ $.extend(this.fn, ob); }; 
    result.extend = function(ob){  $.extend(this, ob); };
    if (includes) result.include(includes);
    
    return result;
  };
  
  exports.Controller = mod;
})(jQuery, window);

var exports = this;

$(function($){
  exports.SearchView = Controller.create({
    elements: {
      "input[type=search]": "searchInput",
      "form": "searchForm"
    },
    
    init: function(element){
      this.el = $(element);
      this.refreshElements();
      this.searchForm.submit(this.proxy(this.search));
    },
    
    search: function(){
      alert("Searching: " + this.searchInput.val());
      return false;
    },
    
    // Private
    
    $: function(selector){
      return $(selector, this.el);
    },
    
    refreshElements: function(){
      for (var key in this.elements) {
        this[this.elements[key]] = this.$(key);
      }
    }
  });
  
  new SearchView("#users");
});</code></pre>

See the Pen CaEbA by buer (@buer) on CodePen.

状态机

考虑这样一个场景,应用中存在一些视图,它们的显示是相互独立的,比如一个视图用来显示联系人,另一个视图用来编辑联系人。这两个视图一定是互斥的关系,其中一个显示时另一个一定是隐藏的。这个场景就非常适合引入状态机,因为它能确保每个时刻只有一种视图是激活的。的确,如果我们想添加一些新视图,比如一个承载设置操作的视图,用状态机来处理这种场景绰绰有余。

一个完整的状态机

 var Events = {
      bind: function(){
        if ( !this.o ) this.o = $({});
        this.o.bind.apply(this.o, arguments);
      },
  trigger: function(){
    if ( !this.o ) this.o = $({});
    this.o.trigger.apply(this.o, arguments);
  }
};

var StateMachine = function(){};
StateMachine.fn  = StateMachine.prototype;
$.extend(StateMachine.fn, Events);

StateMachine.fn.add = function(controller){
  this.bind(&quot;change&quot;, function(e, current){
    if (controller == current)
      controller.activate();
    else
      controller.deactivate();
  });
  
  controller.active = $.proxy(function(){
    this.trigger(&quot;change&quot;, controller);
  }, this);
};

var con1 = {
  activate: function(){ 
    console.log(&quot;controller 1 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 1 deactivated&quot;);
  }
};

var con2 = {
  activate: function(){ 
    console.log(&quot;controller 2 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 2 deactivated&quot;);
  }
};
 
var con3= {
  activate: function(){ 
    console.log(&quot;controller 3 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 3 deactivated&quot;);
  }
};

var sm = new StateMachine;
sm.add(con1);
sm.add(con2);
sm.add(con3);

con2.active();

See the Pen ljyLq by buer (@buer) on CodePen.

URL 中的hash

定位当前页面所用的URL(基于URL)是不能更改的,如果改变则会引起页面的刷新,这是我们要避免发生的。幸好我们有一些解决办法。操作URL 的一种传统办法是改变它的hash。hash 不会发送给服务器,因此更改hash 不会造成页面的刷新。比如,这个
URL 是Twitter 的页面,它的hash 值就是#!/maccman:

http://twitter.com/#!/maccman

可以通过location 对象来读取或修改页面hash :

// 设置hash
window.location.hash = "foo";
assertEqual( window.location.hash , "#foo" );
// 去掉“#”
var hashValue = window.location.hash.slice(1);
assertEqual( hashValue, "foo" );

如果URL 中没有hash,location.hash 则返回空字符串。否则,location.hash 和URL的hash 部分相等,带有# 前缀。
太过频繁地设置hash 也会影响性能,特别是在移动终端里的浏览器中。因此,如果你对hash 的改动太频繁,就要注意限制这种改动,否则在移动终端里可能会造成页面的频繁滚动。

检测hash 的变化

以往检测hash 变化的方法是通过轮询的计时器来监听,这种方法非常原始。现在情形有所改观,现代浏览器都支持hashchange 事件。这是一个window 的事件,如果想检测hash 的改变就需要绑定这个监听:

window.addEventListener("hashchange", function(){ /* ... */ }, false);

使用jQuery 的代码:

$(window).bind("hashchange", function(event){
// hash 发生改变,更改状态
});

当触发hashchange 事件时,我们需要确定应用的当前状态。这个事件的浏览器兼容性非
常不错,主流浏览器的最新版本都支持这个事件:

  • IE >= 8。
  • Firefox >= 3.6。
  • Chrome。
  • Safari >= 5。
  • Opera >= 10.6。

第三章:模型和数据《Developing JavaScript Web Applications》学习笔记

MVC 和命名空间

引入MVC 模式,数据管理则归入模型(MVC 中的M)。模型应当从视图和控制器中解耦出来。与数据操作和行为相关的逻辑都应当放入模型中,通过命名空间进行管理。
在JavaScript 中,我们通过给对象添加属性来管理一个命名空间,这个命名空间可以是函数,也可以是变量,比如:

var User = {
records: [ /* ... */ ]
};

User 的数组数据就在命名空间User.records 中。和user 相关的函数也可以放入User 模型的命名空间里。比如,我们使用fetchRemote() 函数来从服务器端获取user 的数据:

var User = {
records: [],
fetchRemote: function(){ /* ... */ }
};

将模型的属性保存至命名空间中的做法可以确保不会产生冲突,这也是符合MVC 原则
的,同时也能避免你的代码变成一堆函数和回调混杂在一起的大杂烩。

原型继承

这里使用Object.create() 来构造我们的ORM,这和在第1 章中提到的基于类的例子有一点不同。这里使用基于原型(prototype-based)的继承,而没有用到构造函数和new关键字。
Object.create() 只有一个参数即原型对象,它返回一个新对象,这个新对象的原型就是传入的参数。换句话说,传入一个对象,返回一个继承了这个对象的新对象。Object.create() 最近才添加进了ECMAScript 第5 版规范,因此在有些浏览器中并未
实现,比如IE。但这并不是什么大问题,如果需要的话,我们可以很容易地模拟出这个函数:

if (typeof Object.create !== "function")
Object.create = function(o) {
function F() {}
F.prototype = o;
return new F();
};

现在来创建Model 对象,Model 对象将用于创建新模型和实例:

    var Model = {
    inherited: function(){
   //console.dir(this);
  },
    created: function(){
   // console.dir(this);
  },
    prototype: {
        init: function(){}
    },
    create: function(){
      var object = Object.create(this);
      object.parent = this;
      object.prototype = object.fn = Object.create(this.prototype);
      object.created();
      this.inherited(object);
      return object;
    },
    init: function(){
      var instance = Object.create(this.prototype);
      instance.parent = this;
      instance.init.apply(instance, arguments);
      return instance;
    },
  extend: function(o){
        var extended = o.extended;
        for(i in o){
        this[i]=o[i];
      }
        if (extended) extended(this);
    },
    include: function(o){
        var included = o.included;
            for(i in o){
        this.prototype[i]=o[i];
      }
        if (included) included(this);
    }
};

添加ORM 属性

现在如果给Model 对象添加属性,对于继承的模型来说,这些新增属性都是可访问的:

 Model.include({
        init: function(atts) {
            if (atts) this.load(atts);
        },
        load: function(attributes){
        for(var name in attributes)
        this[name] = attributes[name];
        }
    });

持久化记录

我们需要一种保持记录持久化的方法,即将引用保存至新创建的实例中以便任何时候都能访问它。我们通过在Model 中使用records 对象来实现。当我们保存一个实例的时候,就将它添加进这个对象中;当删除实例时,和将它从对象中删除:

Model.extend({
created: function(){
this.records = {}; 
   this.attributes=[];
}
});
Model.include({
   newRecord: true,
   create:function(){
     this.newRecord=false;
     if(!this.id)this.id=Math.guid();
     this.parent.records[this.id]=this.dup();
  },
  update:function(){
    this.parent.records[this.id]=this.dup();
  },
  destroy:function(){
    delete this.parent.records[this.id];
  },
  save:function(){
    this.newRecord?this.create():this.update();
  },
  dup:function(){
    return $.extend(true,{},this);
  }
});

寻址引用

Model.extend({
  // 通过ID查找
  find: function(id){
    var record=this.records[id];  
    return record.dup();
  }
});

向ORM中添加记录

向ORM中添加数据非常简单。我们只需从服务器抓取数据并更新模型的记录即可。现在给Model 对象增加populate() 函数,它会对任何给定的值做遍历、创建实例并更新records 对象:

//向ORM 中添加记录    
Model.extend({
      populate:function(datas){
        this.records={};
        for(var i=0,len=datas.length;i<len;i++){
          var record=this.init(datas[i]);
          record.newRecord=false;
          this.records[record.id]=record;
        }
      }
    });

现在我们可以使用Model.populate() 函数,传入请求的返回数据:

jQuery.getJSON("/assets", function(result){
Asset.populate(result);
});

本地存储数据

可以使用localStorage 和sessionStorage 对象来分别访问并操作local storage 和session storage。设置属性的操作和JavaScript 中的对象操作一样,这两个对象的设置属性操作一模一样:

// 设置一个值
localStorage["someData"] = "wem";
WebStorage API 还提供了一些新的特性:
// 存储数据的个数
var itemsStored = localStorage.length;
// 设置一个项(是一种hash 写法)
localStorage.setItem("someData", "wem");
// 得到一个已经存储的项,如果不存在则返回null
localStorage.getItem("someData"); //=> "wem";
// 删除一个项,如果不存在则返回null
localStorage.removeItem("someData");
// 清空本地存储
localStorage.clear();

数据均存储为字符串,所以如果你想保存的数据是对象或数字,则必须自己做类型转换,如果使用JSON 的话,则需要将对象先做序列化处理再保存它们,从本地存储中读取JSON 时也需要将它转换为对象:

var object = {some: "object"};
// 序列化并保存一个对象
localStorage.setItem("seriData", JSON.stringify(object));
// 读取并将JSON 转换为对象
var result = JSON.parse(localStorage.getItem("seriData"));

如果你存储的数据超出了上限(通常是每个域名5MB),再保存额外数据时则会抛出QUOTA-EXCEEDED-ERR 异常。

给ORM 添加本地存储

Model.include({
  attributes:function(){
    var result={};
    for(var i in this.parent.attributes){
      var attr=this.parent.attributes[i];
      result[attr]=this[attr];
    }
    result.id=this.id;
    return result;
  },
  //JSON.stringify()会序列化所有属性,实现toJSON() 方法就能覆盖JSON.stringify() 。
  toJSON:function(){
    return this.attributes();
  }
});
var LocalStorage={
  saveLocal:function(name){
    var result=[];
    for(var i in this.records){
      result.push(this.records[i]);
    }
    localStorage[name]=JSON.stringify(result);
  },
  loadLocal:function(name){
    var result = JSON.parse(localStorage[name]);
    this.populate(result);
  }
};
Model.extend(LocalStorage);

将新记录提交给服务器

Model.include({
createRemote: function(url, callback){
$.post(url, this.attributes(), callback);
},
updateRemote: function(url, callback){
$.ajax({
url: url,
data: this.attributes(),
success: callback,
type: "PUT"
});
}
});

一切尽在代码中


var Model = {
    inherited: function(){
   //console.dir(this);
  },
    created: function(){
   // console.dir(this);
  },
    prototype: {
        init: function(){}
    },
    create: function(){
      var object = Object.create(this);
      object.parent = this;
      object.prototype = object.fn = Object.create(this.prototype);
      object.created();
      this.inherited(object);
      return object;
    },
    init: function(){
      var instance = Object.create(this.prototype);
      instance.parent = this;
      instance.init.apply(instance, arguments);
      return instance;
    },
  extend: function(o){
        var extended = o.extended;
        for(i in o){
        this[i]=o[i];
      }
        if (extended) extended(this);
    },
    include: function(o){
        var included = o.included;
            for(i in o){
        this.prototype[i]=o[i];
      }
        if (included) included(this);
    }
};
//自动生成ID方法
Math.guid = function(){
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
    }).toUpperCase();
};

Model.extend({
created: function(){
this.records = {};
this.attributes=[];
}
});

//向ORM 中添加记录
Model.extend({
populate:function(datas){
this.records={};
for(var i=0,len=datas.length;i<len;i++){
var record=this.init(datas[i]);
record.newRecord=false;
this.records[record.id]=record;
}
}
});

Model.extend({
// 通过ID查找,找不到则抛出异常
find: function(id){
var record=this.records[id];
return record.dup();
}
});

Model.include({
init: function(atts) {
if (atts) this.load(atts);
},
load: function(attributes){
for(var name in attributes)
this[name] = attributes[name];
}
});
Model.include({
newRecord: true,
create:function(){
this.newRecord=false;
if(!this.id)this.id=Math.guid();
this.parent.records[this.id]=this.dup();
},
update:function(){
this.parent.records[this.id]=this.dup();
},
destroy:function(){
delete this.parent.records[this.id];
},
save:function(){
this.newRecord?this.create():this.update();
},
dup:function(){
return $.extend(true,{},this);
}

});
Model.include({
attributes:function(){
var result={};
for(var i in this.parent.attributes){
var attr=this.parent.attributes[i];
result[attr]=this[attr];
}
result.id=this.id;
return result;
},
toJSON:function(){
return this.attributes();
}
})
var LocalStorage={
saveLocal:function(name){
var result=[];
for(var i in this.records){
result.push(this.records[i]);
}
localStorage[name]=JSON.stringify(result);
},
loadLocal:function(name){
var result = JSON.parse(localStorage[name]);
this.populate(result);
}
};
Model.extend(LocalStorage);
Model.include({
createRemote: function(url, callback){
$.post(url, this.attributes(), callback);
},
updateRemote: function(url, callback){
$.ajax({
url: url,
data: this.attributes(),
success: callback,
type: "PUT"
});
}
});

//拷贝Model
var User=Model.create();
//console.log(User);
User.attributes=['id','name'];
var user1=User.init({id:1,name:"heyong"});
user1.save();

var user2=User.init({id:2,name:"lilei"});
user2.save();

var f_user1=User.find(1);
f_user1.name="22";
user2.name="33";
/*console.log(User);
*/
var Asset=Model.create();
Asset.attributes=['id','title'];

var asset1=Asset.init({id:1,title:"1111标题唉唉唉"});

var asset2=Asset.init({id:2,title:"2222标题唉唉唉"});
asset1.save();
asset2.save();
asset2.createRemote("/asset");

Asset.saveLocal("Asset");
Asset.loadLocal("Asset");
console.log(Asset);

var attr=asset1.attributes();
console.log(attr);
var json = JSON.stringify(Asset.records);
console.log(json);

localStorage.setItem("someData", "wem");
console.log(localStorage.getItem("someData"));
//本地存储API

See the Pen sijfv by buer (@buer) on CodePen.

第二章:事件和监听《Developing JavaScript Web Applications》学习笔记

监听事件

绑定事件监听的函数addEventListener(),3 个参数:type(比如click),
listener(绑定的事件函数,比如callback)及useCapture(事件顺序,使用捕获,否则冒泡)。
使用removeEventListener() 来移除事件监听,参数和传入addEventListener() 的一样。如果监听的函数是匿名函数,没有任何引用指向它,在不销毁这个元素的前提下,这个监听是无法被移除的.

/*element.addEventListener(type,listener,useCapture);*/
btn1Obj.addEventListener("click",method1,false);
btn1Obj.addEventListener("click",method2,false);
btn1Obj.addEventListener("click",method3,false);//执行顺序为method1->method2->method3
btn1Obj.romoveEventListener("click",method3,false);//清除事件method3

在IE下,使用attachEvent()和 detachEvent()分别来进行事件监听和移出。IE下事件是冒泡的方式,所以没有第三个参数(useCapture)。事件type和w3c的函数不太一样,需要加上前缀on;

var btn1Obj = document.getElementById("btn1");
 //object.attachEvent(event,function);
btn1Obj.attachEvent("onclick",method1);
btn1Obj.attachEvent("onclick",method2);
btn1Obj.attachEvent("onclick",method3);//执行顺序为method3->method2->method1
btn1Obj.detachEvent("onclick",method3);//清除事件method3

取消事件

当事件冒泡时,可以通过stopPropagation() 函数来终止冒泡,这个函数是event 对象
中的方法。比如这段代码,任何父节点的事件回调都不会触发:

button.addEventListener("click", function(e){
e.stopPropagation();
/* ... */
}, false);

//IE浏览器:
event.cancelBubble=true;

另外,通过调用event 对象的preventDefault() 函数来阻止默认行为。

事件对象

var demo=document.getElementById("demo");
demo.addEventListener("click",function(e){
  console.log(e);
},false)
//控制台输出e对象:
altKey: false
bubbles: true
button: 0
cancelBubble: false
cancelable: true
charCode: 0
clientX: 75
clientY: 172
clipboardData: undefined
ctrlKey: false
currentTarget: null
dataTransfer: null
defaultPrevented: false
detail: 1
eventPhase: 0
fromElement: null
keyCode: 0
layerX: 75
layerY: 172
metaKey: false
offsetX: 67
offsetY: 105
pageX: 75
pageY: 172
path: NodeList[0]
relatedTarget: null
returnValue: true
screenX: 75
screenY: 712
shiftKey: false
srcElement: div#demo
target: div#demo
timeStamp: 1405400498039
toElement: div#demo
type: "click"
view: Window
webkitMovementX: 0
webkitMovementY: 0
which: 1
x: 75
y: 172

切换上下文

当使用浏览器内置的addEventListener() 时,上下文从局部变量切换为目标HTML 元素:

new function(){
this.appName = "wem";
document.body.addEventListener("click", function(e){
// 上下文发生改变,因此appName 是undefined
alert(this.appName);
}, false);
};

要想保持原有的上下文,需要将回调函数包装进一个匿名函数,然后定义一个引用指向它。我们在第1 章已经提到这种模式,即使用代理函数来保持当前的上下文。这在jQuery 中也是一种很常用的模式,包括一个proxy() 函数,只需将指定的上下文传入函数即可:

$("signinForm").submit($.proxy(function(){ /* ... */ }, this));

事件委托

我们开发一个列表(ul>li)点击事件,为每个li绑定事件显然这样做不太好,这时候用到事件委托。

// 在ul 列表上做了事件委托
list.addEventListener("click", function(e){
if (e.currentTarget.tagName == "li") {
/* ... */
return false;
}
}, false);

jQuery的delegate() 函数正是解决事件委托而建立的。用法:

// 不要这样做,这样会给每个li 元素都添加事件监听(非常浪费)
$("ul li").click(function(){ /* ... */ });
// 这样只会添加一个事件监听
$("ul").delegate("li", "click", /* ... */);

自定义事件

// 绑定自定义事件
$(".class").bind("refresh.widget",function(){});
// 触发自定义事件
$(".class").trigger("refresh.widget");

pub/sub模式

var PubSub = {
subscribe: function(ev, callback) {
// 创建 _callbacks 对象,除非它已经存在了
var calls = this._callbacks || (this._callbacks = {});
// 针对给定的事件key 创建一个数组,除非这个数组已经存在
// 然后将回调函数追加到这个数组中
(this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
return this;
},
publish: function() {
// 将arguments 对象转换为真正的数组
var args = Array.prototype.slice.call(arguments, 0);
// 拿出第1 个参数,即事件名称
var ev = args.shift();
// 如果不存在_callbacks 对象,则返回
// 或者如果不包含给定事件对应的数组
var list, calls, i, l;
if (!(calls = this._callbacks)) return this;
if (!(list = this._callbacks[ev])) return this;
// 触发回调
for (i = 0, l = list.length; i < l; i++)
list[i].apply(this, args);
return this;
}
};
// 使用方法
PubSub.subscribe("wem", function(){
alert("Wem!");
});
PubSub.publish("wem");

jquery实现的简易的pub/sub

(function($){
var o = $({});
$.subscribe = function() {
o.bind.apply( o, arguments );
};
$.unsubscribe = function() {
o.unbind.apply( o, arguments );
};
$.publish = function() {
o.trigger.apply( o, arguments );
};
})(jQuery);

这里的API 和jQuery 的bind() 及trigger() 函数的参数一致。惟一的区别就是这两个
函数直接保存在jQuery 对象中,且名叫publish() 和subscribe() :

$.subscribe( "/some/topic", function( event, a, b, c ) {
console.log( event.type, a + b + c );
});
$.publish( "/some/topic", "a", "b", "c" );

jquery自定义插件

$.fn.tabs = function (control) {
var element = $(this);
control = $(control);
element.delegate("li", "click", function () {
// 遍历选项卡名称
var tabName = $(this).attr("data-tab");
// 在点击选项卡时触发自定义事件
element.trigger("change.tabs", tabName);
});
// 绑定到自定义事件
element.bind("change.tabs", function (e, tabName) {
element.find("li").removeClass("active");
element.find(">[data-tab='" + tabName + "']").addClass("active");
});
element.bind("change.tabs", function (e, tabName) {
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab='" + tabName + "']").addClass("active");
});
// 激活第1 个选项卡
var firstName = element.find("li:first").attr("data-tab");
element.trigger("change.tabs", firstName);
return this;
};

//how to use
$(document).ready(function(){
$("#tabs").tabs("#tabsContent");
});

See the Pen cDaBb by buer (@buer) on CodePen.

第一章:MVC和类《Developing JavaScript Web Applications》学习笔记

一.什么是MVC

MVC 是一种设计模式,它将应用划分为3 个部分:数据(模型)、展现层(视图)和用户交互层(控制器)。换句话说,一个事件的发生是这样的过程:

  1. 用户和应用产生交互。
  2. 控制器的事件处理器被触发。
  3. 控制器从模型中请求数据,并将其交给视图。
  4. 视图将数据呈现给用户。

二.关于类

1.类生成器建立

var Class = function(){
    var klass = function(){
        this.init.apply(this, arguments);
    };
    klass.prototype.init = function(){};
    return klass;
};
var Person = new Class();
Person.prototype.init = function(){
    // 基于Person 的实例做初始化
};
// 用法:
var person = new Person();

2.extend方法,给类添加属性

klass.extend = function (obj) {
    var extended = obj.extended;
    for (var i in obj) {
    klass[i] = obj[i];
    }
    if (extended) extended(klass)
};

3.include方法,用于添加实例方法

// 给实例添加属性
klass.include = function (obj) {
    var included = obj.included;
    for (var i in obj) {
    klass.fn[i] = obj[i];
    }
    if (included) included(klass)
};

4.基于原型prototype类继承的研究

if (parent) {
    var subclass = function() { };
    subclass.prototype = parent.prototype;
    klass.prototype = new subclass;
};

5.javascript作用域,事件代理。proxy方法,控制“类”库的作用域

// 添加一个proxy 函数
klass.proxy = function(func){
    var self = this;
    return (function(){
                return func.apply(self, arguments);
            });
}
// 在实例中也添加这个函数
klass.fn.proxy = klass.proxy;

6.匿名函数,局部变量

(function(exports){
    var foo = "bar";
    // 将变量暴露出去
    exports.foo = foo;
})(window);

以上研究,详见以下代码:

var Class=function(parentClass){
  var kclass=function(){
    this.init.apply(this,arguments);
  };
  if(parentClass){
    var subClass=function(){};
    subClass.prototype=parentClass.prototype;
    kclass.prototype=new subClass();
  }
  kclass.prototype.init=function(){};
  kclass.fn=kclass.prototype;
  kclass.fn.parent=kclass;
  kclass.proxy=function(func){
    var self=this;
    return (function(){
        func.apply(self,arguments);
    });
  },
  kclass.fn.proxy = kclass.proxy;

kclass.extend=function(obj){
for(var i in obj){
kclass[i]=obj[i];
}
if(obj.callback)obj.callback(kclass);
};
kclass.include=function(obj){
for(var i in obj){
kclass.fn[i]=obj[i];
}
if(obj.callback)obj.callback(kclass);
};
return kclass;
};

//第一个例子:类生成,实例生成
var Person=new Class();
Person.extend({
"say":function(say){
console.log(say);
}
});
Person.include({
"getWork":function(workName){
return workName;
}
});
Person.say("i can say");
var lili=new Person();

lili.getWork("计算机");
//第二个例子:继承
var Animal = new Class();
Animal.extend({
say: function(){
console.log('say');
}
});
Animal.include({
breath: function(){
console.log('breath');
}
});
var Cat = new Class(Animal);
// 用法
var tommy = new Cat();
//tommy.say(); say不是原型方法 无法被继承
tommy.breath();
//第三个例子:控制作用域proxy函数
var Button = new Class();
Button.include({
init: function(element){
this.element = jQuery(element);
// 代理了这个click 函数
this.element.click(this.proxy(this.click));
},
click: function(){ console.log(this)}
});

var click_m_button=new Button();
click_m_button.init("#m-button");

See the Pen sDixq by buer (@buer) on CodePen.

$(function(){ });与 (function(){})($); 区别

开发jQuery插件时总结的一些经验分享一下。

一、先看

$(function(){ 
}); 

全写为

$(document).ready(function(){     
}); 

意义为在DOM加载完毕后执行了ready()方法。
二、再看

(function(){ 

})($); 

其实际上是执行()(para)匿名方法,只不过是传递了jQuery对象。

三、总结

$(function(){});

用于存放操作DOM对象的代码,执行其中代码时DOM对象已存在。不可用于存放开发插件的代码,因为jQuery对象没有得到传递,外部通过jQuery.method也调用不了其中的方法(函数)。

(function(){})($);

用于存放开发插件的代码,执行其中代码时DOM不一定存在,所以直接自动执行DOM操作的代码请小心使用。