前言:在这mvvm模式盛行的今天,很多人觉得没必要去了解jquery源码,而我并不认同以上的说法。jquery对javaScript进行了封装使其更加完善,jquery的源码中更是能看到对js原生方法的完美运用,还能学到很多没见过的操作JavaScript的技巧。简单的说,学习源码能让JavaScript使用基础更扎实。
学习jquery源码需解决的几个问题。
①、js打断点问题
②、jquery是如何做到里面定义的变量不暴露在全局,而且只暴露出来一个$操作符跟一个jQuery给我们调用。
③、jquery是如何做到不运用到new就可以实例化jquery对象的。
④、jquery是如果做到链式操作的。
一、问题①
平时调试代码时,学会打断点有时候能帮你解决很多问题,而学习源码学会打断点更是更是一大利器。
1、打开网页,点击调试工具中的sources。
2、找到需要进行断点的js代码、或js文件。
3、鼠标左键点击需要打断点的代码行数那里。
4、F5刷新页面。
5、最后点击右上角那个按钮就可以观看到代码的运行路径了。
6、小红圈左边的按钮是跳过当前这次的代码执行,到下一次的代码执行过程。
(网上能找到很多关于打断点的操作方法。这里就不一一细说了。)
二、问题②
我们无法直接访问jquery里面定义的变量,但是通过$操作符或者jQuery就可以进行访问了。
(function(window,undefined){
var jQuery = function(){ //构造函数
};
window.jQuery = window.$ = jQuery;
})(window);
①利用匿名函数自执行,来形成全局无法直接访问的函数作用域,然后将全局的window,undefined,通过传参的方式传入到该函数作用域中。
②传入window,undefined一是为了减少作用域链的全局搜索,二是为了避免undefined这个非关键字被其他变量名覆盖。(没有传入第二个实参,所以第二个参数的值就是undefined)。
③最后通过window.jQuery = jQuery,window.$ = jQuery的方式,将jQuery构造函数暴露出去。这样我们就可以通过jQuery或$访问该函数作用域里变量了。
三、问题③
jquery是如何做到不运用到new就可以实例化jquery对象的。
function Person(name,age){
this.init(name,age);
};
Person.prototype= {
init: function(name,age){
this.name = name;
this.age = age;
},
sayName: function(){
console.log('姓名: '+ this.name);
},
sayAge: function(){
console.log('年龄: '+ this.age);
}
};
var nick = new Person('nick',18); //实例化对象
nick.sayName();
nick.sayAge();
JavaScript中实例化一个对象需要通过new一个构造函数的方式是实现。而在jquery中我们可以直接通过类似$('.box')的方式直接实例出一个jQuery对象,并通过$('.box').css()的方式直接调用对象方法。这是怎么做到的。
现在来模拟jQuert中实现不用new实例化对象的写法
(function(window,undefined){
var jQ = function(name,age){
return new jQ.prototype.init(name,age);
};
jQ.prototype = {
init: function(name,age){
this.name = name;
this.age = age;
},
sayName: function(){
console.log('姓名: '+this.name);
},
sayAge: function(){
console.log('年龄: '+this.age);
}
};
jQ.prototype.init.prototype = jQ.prototype;
window.jQ = window.$$ = jQ;
})( window );
var nick = $$('nick',18);
nick.sayName();
nick.sayAge();
$$('freddy',24).sayName();
$$('freddy',24).sayAge();
输出结果:
这样也就实现了不用再外面用new就可以实例化对象了。其中比较抽象的就是
jQ.prototype.init.prototype = jQ.prototype;
了解过prototype也就知道,对象的实例化跟prototype原型密不可分。这里是把jQ.prototype.init.protype的指向指回了jQ.prototype。
我们在看回jquery源码中的写法
(function(window,undefined){
var jQuery = function(selector, context){
return new jQuery.fn.init(selector, context, rootjQuery);
};
jQuery.fn = jQuery.prototype = {
init: function(selector, context, rootjQuery){
}
}
jQuery.fn.init.prototype = jQuery.fn;
window.jQuery = window.$ = jQuery;
})( window );
跟我们模拟的写法稍有不同的是,他让jQuery.fn = jQuert.prototype = {},让jQuery.fn指向了同一个原型对象,这样操作jQuery.fn等同于操作jQuery原型对象。
四、问题④
链式操作
(function(window,undefined){
var jQ = function(name,age){
return new jQ.prototype.init(name,age);
};
jQ.prototype = {
init: function(name,age){
this.name = name;
this.age = age;
},
sayName: function(){
console.log('姓名: '+this.name);
return this; //添加return this
},
sayAge: function(){
console.log('年龄: '+this.age);
return this; //添加return this
}
};
jQ.prototype.init.prototype = jQ.prototype;
window.jQ = window.$$ = jQ;
})( window );
var nick = $$('nick',18);
nick.sayName().sayAge();
$$('freddy',23).sayName().sayAge();
像这样,直接return this就可以了。