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+'
'+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