深入理解jQuery框架-框架结构
这是本人结合资料视频总结出来的jQuery大体框架结构,如果大家都熟悉了之后,相信你们也会写出看似高档的js框架;
jquery框架的总体结构
(function(w, undefined){ //定义一些变量和函数 var //对外提供的接口 jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }; //结jQuery对象添加一些方法和属性 //jQuery的继承方法 //jQuery.extend 扩展一些工具的方法(静态方法) jQuery.trim(); //Sizzle 复杂选择器的实现 //Callbacks 回调对象--函数的统一管理 //Deferred 延迟对象,对异步的统一管理 //support 功能检测 //Data 数据缓存 //queue 队列管理 //Attribute 属性操作 //Event 事件处理 //DOM操作 添加删除、包装筛选等 //CSS操作 //提交的数据和ajax //运动/动画 FX //坐标和大小 //支持的模块化的模式 w.jQuery = w.$ = jQuery; })(window);
jquery为什么要使用匿名函数来封装框架
阅读jquery源码,我们可以看出jquery使用的是匿名函数来封装框架,那么为什么要使用这种方式呢,又有什么好处?
1.什么是匿名函数?
// 匿名函数的写法 function(){ console.log("test"); } // 将变量赋值给一个匿名函数 var test = function(){ return 0; } console.log(test); //打印的是变量test,即函数 console.log(test()); //打印0,test + ()表示调用这个方法
2.匿名函数自调用的方式
匿名函数有两种调用方式,第一种是()()、第二种是 (())
// ()()调用方式
(function(){ console.log("匿名函数-自调用方法1"); })();
// (())调用方式 (function(){ console.log("匿名函数-自调用方法2"); }());
3.匿名函数传参
/*错误方式*/ (function(window){ console.log(window); //undefined })(); /*正确方式*/ var jQuery; (function(win, jQuery){ console.log(win); // })(window, jQuery);
4.使用匿名函数对外提供一个接口
(function(w) { var Person = function() { this.name = "cxb"; this.talk = function() { console.log("hello, I'm " + this.name); } } w.Person = Person; })(window) var p = new Person(); console.log(p.name); p.talk();
5.使用匿名函数的好处:
防止变量方法冲突 变量与方法是独立的,写在匿名函数的变量或函数属于局部的,不会受外面的干扰,也不会影响全局变量
(function(){ var a = 10; var fun = function() { console.log("fun method"); } })(); console.log(a); //报错 undefined fun(); //报错 fun is not a function
为什么可以直接使用jQuery 或 $为调用jquery方法?
对象创建的比较
创建对象方式一:
function Student() { this.name = "cxb"; this.talk = function() { return "我叫" + this.name; } } var stu1 = new Student(); var stu2 = new Student();
创建对象方式二:
function Student() { this.name = "cxb"; } Student.prototype.talk = function() { return "我叫" + this.name; } var stu1 = new Student(); var stu2 = new Student();
方式一与方式二产生的结构几乎是一样的,而本质区别就是:方式二new产生的二个实例对象共享了原型的talk方法,这样的好处节省了内存空间,方式一则是要为每一个实例复制talk方法,每个方法属性都占用一定的内存的空间,所以如果把所有属性方法都声明在构造函数中,就会无形的增大很多开销,这些实例化的对象的属性一模一样,都是对this的引用来处理。除此之外方式一的所有方法都是拷贝到当前实例对象上。方式二则是要通过scope连接到原型链上查找,这样就无形之中要多一层作用域链的查找了。
所以说在jQuery中,可以看到很多 jQuery.fn 这样的写法(事实上在jQuery中,使用jQuery.fn替换了jQuery.prototype)。
浅拷贝与深拷贝
1. js对象的浅拷贝
var a = [1,2,3]; var b = a; var c = {name:"cxb", age:26}; var d = c; console.log(a); console.log(b); console.log(c); console.log(d);
[1, 2, 3]
[1, 2, 3]
Object {name: "cxb", age: 26}
Object {name: "cxb", age: 26}
b[2] = 5; d.age = 20; console.log(a); console.log(b); console.log(c); console.log(d);
[1, 2, 5]
[1, 2, 5]
Object {name: "cxb", age: 20}
Object {name: "cxb", age: 20}
2.js数组的深拷贝
var arr1 = [1, 2, 3]; var arr2 = []; function deepCopy(arr1, arr2) { for(var i=0; i<arr1.length; i++) { arr2[i] = arr1[i]; } } deepCopy(arr1, arr2); console.log(arr1); console.log(arr2); arr2[2] = 5; console.log(arr1); console.log(arr2);
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 5]
3.js对象的深拷贝
function Parent() { this.name = "李四"; this.age = 40; this.talk = function() { console.log("hello, I'm "+ this.name); } } function Child() { this.name = "张三"; this.age = 20; this.sound = function() { console.log("haha"); } } Child.prototype = new Parent(); var child = new Child(); for(var key in child) { console.log(key); }
4.hasOwnProperty来过滤原型链上的属性
for(var key in child) { if(child.hasOwnProperty(key)) console.log(key); }
5.完整clone一个对象的方法
var child = new Child(); var childObject = {}; for(var key in child) { if(child.hasOwnProperty(key)) childObject[key] = child[key]; } console.log(childObject.name); console.log(childObject.age); childObject.sound(); childObject.talk();
jQuery框架的实现原理
/******************** jQuery 框架部分 ************************/ (function(w, undefined){ //第一步:创建一个jQuery函数 var jQuery = function() { //第四步: return new jQuery.fn.init(); } //第三步: jQuery.fn = jQuery.prototype; //是覆盖prototype jQuery.fn = { //当创建了一个函数之后,js源码会自动生成 jQuery.prototype.constructor = jQuery; //在jQuery中使用这个是为了防止恶意修改:如 jQuery.prototype.constructor = Array; constructor : jQuery, init : function() { return this; }, }; jQuery.fn.init.prototype = jQuery.fn; //如果不使用这个,在第四步当中是无法使用new的 //第五步:使用extend将jQuery模块化(其实原码并不是这样的)这里使用了拷贝,关于拷贝请看 4.js浅拷贝与深拷贝 //好处:插件扩展时直接使用此方法 jQuery.fn.extend = jQuery.extend = function( ) { var target = this, source = arguments[0] || { } ; for(var key in source) { if(source.hasOwnProperty(key)) { jQuery.fn[key] = jQuery[key] = source[key]; } } return target; } //第六步:模块一 jQuery.fn.extend({ html : function() {}, text : function() {}, }); //第六步:模块二 jQuery.extend({ ajax : function() { //处理json格式的参数及回调函数success,error console.log("ajax method"); }, each : function() {}, }); //第二步:对外提供一个接口 w.jQuery = w.$ = jQuery; })(window); //测试1 jQuery.ajax(); //测试2:外部框架的扩展 /******************** 基于jQuery框架的ui框架部分 ************************/ (function(w, jQuery) { //jQuery对象的方法扩展 jQuery.fn.extend({ drag : function() { console.log("drag method"); }, dialog : function() { console.log("dialog method"); }, }); //jQuery类的方法扩展 jQuery.extend({ tool : function() { console.log("tool method"); }, //.... }); })(window, jQuery);
示例
<html lang="zh-CN">
<head>
<script>
(function(win, undefined){ var doc = win.document; var loc = win.location; var jQuery; jQuery = function(selector, context){ return new jQuery.fn.init(selector, context); } var _jQuery = win.jQuery, _$ = win.$; jQuery.fn = jQuery.prototype; jQuery.fn = { constructor: jQuery, init : function(selector, context){ if(selector.charAt(0)=="#"){ this.context = doc.getElementById(selector.substr(1)); }else if(selector.charAt(0)=="."){ this.context = doc.getElementsByName(selector.substr(1)); }else if(selector.charAt(0)==":"){ this.context = doc.getElementsByTagName(selector.substr(1)); }else{ this.context = doc.getElementsByTagName(selector); } return this; } } jQuery.fn.init.prototype = jQuery.fn; jQuery.fn.extend = jQuery.extend = function() { var target = this; var source = agruments[0] || { }; for (var p in source) { if (source.hasOwnProperty(p)) { target[p] = source[p]; } } return target; } // 对象的方法 jQuery.fn.extend( { val : function() { if((typeof _value)=="undefined"){ return this.context.value; }else if((typeof _value)=="string"){ return this.context.value=_value; } }, html : function() {}, text : function() {}, attr : function() {}, prop : function() {}, //... }); // CSS操作 jQuery.fn.extend( { addClass : function() {}, removeClass : function() {}, css : function() {}, //... }); // 插件扩展的方法 jQuery.extend( { ajax: function() {}, each: function() {}, when: function() {}, //... });
// 对外提供的接口,即使用$ 或 jQuery win.jQuery = win.$ = jQuery; })(window); window.onload = function(){ var value = jQuery("#test").val(); console.log("------"+value); }
</script>
</head>
<body>
<input type="text" id="test" value="123456" />
</body>
</html>