这会是一段很长的历程,但也会很有意思 —— 作为前端领域的经典之作,jQuery 里有着太多奇思妙想,如果能够深入理解它,对于我们稳固js基础、提升前端大法技能来说大有裨益。
另外,本系列的相关代码均可以从 我的github 上获取到。
1. 免 new 实现
我们在使用很多插件的时候,都需要使用 new XXX() 的写法来实例化一个引用:
1 2 3 |
var list = new Slip(document.getElementById('slip'), { //options }); |
jQuery 同样作为一个面向对象的工具库,在我们创建一个实例时却无需使用 new 语法,节省了一些代码量:
1 2 3 |
var $div = $('div'); //不需要如下写法: //var $div = new $('div'); |
这种便捷的形式依赖了工厂模式,其实现非常简单,把 new 封装在库内即可,让每次调用 jQuery() 时自行在内部进行一次实例化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
(function() { var _jQuery = window.jQuery, _$ = window.$; var version = "0.0.1", jQuery = function(selector) { console.log(document.querySelector(selector)) }; jQuery.prototype = { jquery: version, constructor: jQuery }; window.$ = window.jQuery = function(selector) { return new jQuery(selector); //notice here~ }; })(); |
留意这里我们走的 IIFE 形式,让 jQuery 代码库形成自己的作用域,避免污染外部变量。
于是乎以上就是咱写的第一个 JQ 雏形,简单跑一下:
1 2 3 4 5 |
<div></div> <script> var $div = $('div'); //<div></div> console.log($div.jquery); //0.0.1 </script> |
别忘了后续我们还希望能通过 $.extend / $.fn.extend 来扩展 JQ 的静态方法和原型方法,我们把出口方法抽出来增加这个 extend 的API:
1 2 3 4 5 6 7 8 9 10 11 |
function Factory(selector){ //抽出构造函数 return new jQuery(selector); } Factory.fn = jQuery.prototype; Factory.extend = Factory.fn.extend = function(){ console.log(this) }; window.$ = window.jQuery = init; |
这样我们也能直接通过 $.fn.jquery 来获取当前 JQ 版本号了。
如果希望可以通过 $.prototype 直接访问 jQuery 的原型对象,再修改下这句代码即可:
1 |
Factory.prototype = Factory.fn = jQuery.prototype; |
2. 写法优化
事实上我们不太喜欢再写多一个冗余的 Factory 构造函数来作为 window.jQuery 的引用,也不喜欢(在模块内部)使用 Factory.extend() 来扩展 JQ,它听起来和 JQ 没有半毛钱关系。
如果可以,直接把 jQuery 方法作为接口输出,且在模块内部能以 jQuery.extend() 的形式来调用扩展接口,这样的形式更佳。
也就是说我们希望代码应该是这样写的:
这会是一段很长的历程,但也会很有意思 —— 作为前端领域的经典之作,jQuery 里有着太多奇思妙想,如果能够深入理解它,对于我们稳固js基础、提升前端大法技能来说大有裨益。
另外,本系列的相关代码均可以从 我的github 上获取到。
1. 免 new 实现
我们在使用很多插件的时候,都需要使用 new XXX() 的写法来实例化一个引用:
1 2 3 |
var list = new Slip(document.getElementById('slip'), { //options }); |
jQuery 同样作为一个面向对象的工具库,在我们创建一个实例时却无需使用 new 语法,节省了一些代码量:
1 2 3 |
var $div = $('div'); //不需要如下写法: //var $div = new $('div'); |
这种便捷的形式依赖了工厂模式,其实现非常简单,把 new 封装在库内即可,让每次调用 jQuery() 时自行在内部进行一次实例化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
(function() { var _jQuery = window.jQuery, _$ = window.$; var version = "0.0.1", jQuery = function(selector) { console.log(document.querySelector(selector)) }; jQuery.prototype = { jquery: version, constructor: jQuery }; window.$ = window.jQuery = function(selector) { return new jQuery(selector); //notice here~ }; })(); |
留意这里我们走的 IIFE 形式,让 jQuery 代码库形成自己的作用域,避免污染外部变量。
于是乎以上就是咱写的第一个 JQ 雏形,简单跑一下:
1 2 3 4 5 |
<div></div> <script> var $div = $('div'); //<div></div> console.log($div.jquery); //0.0.1 </script> |
别忘了后续我们还希望能通过 $.extend / $.fn.extend 来扩展 JQ 的静态方法和原型方法,我们把出口方法抽出来增加这个 extend 的API:
1 2 3 4 5 6 7 8 9 10 11 |
function Factory(selector){ //抽出构造函数 return new jQuery(selector); } Factory.fn = jQuery.prototype; Factory.extend = Factory.fn.extend = function(){ console.log(this) }; window.$ = window.jQuery = init; |
这样我们也能直接通过 $.fn.jquery 来获取当前 JQ 版本号了。
如果希望可以通过 $.prototype 直接访问 jQuery 的原型对象,再修改下这句代码即可:
1 |
Factory.prototype = Factory.fn = jQuery.prototype; |
2. 写法优化
事实上我们不太喜欢再写多一个冗余的 Factory 构造函数来作为 window.jQuery 的引用,也不喜欢(在模块内部)使用 Factory.extend() 来扩展 JQ,它听起来和 JQ 没有半毛钱关系。
如果可以,直接把 jQuery 方法作为接口输出,且在模块内部能以 jQuery.extend() 的形式来调用扩展接口,这样的形式更佳。
也就是说我们希望代码应该是这样写的: