jQuery 源码学习笔记
//检测 window 中新增的对象 //first var oldMap = {}; for(var i in window) { oldMap[i] = 1; } //second for(var i in window) { if(oldMap[i]) continue; alert(i); } $()选择器获取到的既不是一个dom元素,也不是节点列表,而是一个新的对象 $() 不传入任何参数会返回一个空的jquery对象 //google cdn获取jquery <script type="text/javascript" src="http://www.google.com/jsapi"></script> <script> google.load("jquery",'1.4.2'); google.setOnLoadCallback(function(){alert('aa'+$)}); </script> //这种写法更好 $ = [1,2]; jQuery(function($){ //自动传入jQuery对象,即使外边的$被覆盖依然可以使用$ alert($); }) //创建一个元素,同时给他添加一些属性和方法 $("<input>", { type: "text", val: "Test", focusin: function() { $(this).addClass("active"); }, focusout: function() { $(this).removeClass("active"); } }).appendTo("form"); //获取select标签的值,当为单选时为一个数值,当为多选时则会返回一个数组, //设置多选传入数组 $("select").val([1,2,3]); $("select option:selected").each(function(){ alert(this.value); }) //绑定事件时传入参数,很好的解决了闭包产生的问题 var message = 'Spoon!'; $('#foo').bind('click', {msg:message}, function(evt) { //访问传入参数的方式 alert(evt.data.msg); }); message = 'sjk'; 在事件处理函数内返回false就等价于执行事件对象上的.preventDefault()[ie:evt.returnValue=false;]和.stopPropagation()[ie:evt.cancelBubble=true;] jq绑定的事件只能用自身的方法来触发,但js绑定的事件同样可以用jq来触发 $("#song").click(function(evt){ alert(evt.originalEvent); //原始的事件对象 alert('click'); }) //事件绑定外边的一个函数 function test(evt, msg){ alert(evt); alert(msg); alert(this.innerHTML); } document.getElementById('song').onclick = function(evt){ test.call(this, evt, 'ok'); //important }; // trigger传入参数 $("#song").click( function (event, a, b) { // 一个普通的点击事件时,a和b是undefined类型 // 如果用下面的语句触发,那么a指向"foo",而b指向"bar" alert(a+'\n'+b); } ).trigger("click", ["foo", "bar"]); live事件委托的原理 $(document).bind("click", function (e) { $(e.target).closest("li").toggleClass("hilight"); }); //新发现mouse事件 mouseenter / mouseleave jquery返回的对象非常类似数组,但并不是继承自数组 instanceof Array //会自动把数组的下标赋值到this下面 [].push.apply(this,ary); //jquery对象实现原理 function list(ary) { this.ary = ary; [].push.apply(this,ary); } list.prototype = { attr:function(){ for(var i=0;i<this.ary.lenght;i++){ this.ary[i][name] = value; } }, get:function(index){ if(index === undefined) return this.ary; else{ return this.ary[index]; } }, push:[].push, shift:[].shift } var l = new list([1,2,3]); l.push(4); l.push(5); console.dir(l); 下面的script标签会等上面的script标签中的内容全部加载完毕后才执行 xpath:比css选择器更为强大些 //改变某个函数的this上下文 function closure(fn,scope) { return function(){ fn.apply(scope, arguments); } } //调用一个对象上指定的方法 ps:貌似没啥用? function invoke(obj, methodName) { return function(){ obj[methodName].apply(obj,arguments); }; } 都是返回一个经过包装过的函数 //给函数增加属性 function test(){ arguments.callee.guid = 1; } var fn = function(){ arguments.callee.guid = 1; } //fn();#必须执行后才能有此属性 alert(fn.guid); //一种变通的方式 给匿名 函数增加属性 var fn = function(){ var anony = function(){ // your code } anony.guid = 1; return anony; } $.proxy(fn,scope)用法 $(function(){ function test(){ alert(this.html()); } var h = $(":header"); h.click($.proxy(test,h)) //改变this的值 alert(test.guid); //每个函数赋值一个guid,当此函数经过闭包处理后返回的函数仍旧此函数的guid相同, 可以用guid来判断这两个函数是相同的(一般情况下我们无法比较两个函数是否相同) }) w3c浏览器: addEventListener removeEventListener IE: attachEvent detachEvent //closest对于处理事件委托非常有用, 可以实现 live 、delegate效果 $(document).bind("click", function (e) { $(e.target).closest("li").toggleClass("hilight"); }); 解决$命名空间冲突的问题 jQuery.noConflict(); jQuery(function($){ 传入一个函数后,jquery会自动把他的全局命名空间($),传入次函数的参数中 var len = $("ul li").length; console.log(len); }) 在引入jquery文件后立刻调用 //释放$命名空间 jQuery.noConflict(); //释放jQuery命名空间 jQuery.noConflict(true); 一般写jquery代码时可以把$全局命名空间释放掉,这样就避免了和其他插件的冲突 $可以用局部的那个 //数据缓存 $("#song").data("title", 'title'); $("#song").data("age", 27); //不传入参数则会返回一个js对象,并不是dom对象 var obj = $("#song").data(); alert(obj.title); alert(obj.age); //可以传入obj,键值对 $("#song").data({name:12,age:27}); 源码分析: //注册命名空间 function test(){} var _nspool = {}; test.ns = function(name, value){ var names = name.split('.'); o = window; for(var i=0;i<names.length;i++) { if(!o[names[i]]) { o[names[i]] = {}; } o = o[names[i]]; _nspool[name] = o[names[i]]; o[names[i]] = value; } } test.ns('a.b.c', 123); console.dir(_nspool); //反注册时只需要把 _nspool中的key值删除 命名空间太长,可能会引起效率问题,可以把他赋给一个值 [object Object] [基本类型 构造函数] alert({}.toString()); //数组上的toString方法被改写了,所以我们借用{}上的toString方法 判断是否是一个数组 {}.toString.call([]); // [object Array] //插件机制 jQuery本身添加 jQuery.newMethod = function(){} jQuery.extend(obj); jQuery对象添加 jQuery.fn = jQuery.fn.init.prototype = function(){} jQuery.fn.extend(obj); //extend 函数默认复杂类型以引用传递 var o1 = {name:1}; var o2 = {age:{sex:{hel:1}}}; jQuery.extend(o1,o2); console.log(o1.age === o2.age); 代理函数:把新返回的函数的guid和原函数的guid设置成相同的,这样解除绑定的时候传入原函数即可 var obj = { name: "John", test: function() { alert( this.name ); $("#test").unbind("click", obj.test); } }; $("#song").click( jQuery.proxy( obj, "test" ) ); //以下代码跟上面那句是等价的: ("#song").click( jQuery.proxy( obj.test, obj ) ); //可以与单独执行下面这句做个比较。 $("#song").click( obj.test ); 原生js实现jquery的proxy方法 //改变某个函数的内部this指针 function closure(fn, scope) { return function (){ fn.apply(scope, arguments); }; } //调用某个对象的某个函数,并将次对象作为方法的作用域 function invoke(obj, methodName) { return function(){ obj.methodName(obj, arguments); }; } 函数绑定原理 var fns = [],conter = 0; //绑定 function addEvent(){ fn.guid = counter ++; fns[type] = fns[type] || {}; fns[type][fn.guid] = fn; o["on"+type] = function(){ //遍历 fns } } //解除绑定 function delEvent(o, type, fn) { delete fns[type][fn.guid]; } jquery 插件 轮播器:slideview slinkySlider anythingSlider