jQuery:从零开始,DIY一个jQuery(1)
从本篇开始会陪大家一起从零开始走一遍 jQuery 的奇妙旅途,在整个系列的实践中,我们会把 jQuery 的主要功能模块都了解和实现一遍。
这会是一段很长的历程,但也会很有意思 —— 作为前端领域的经典之作,jQuery 里有着太多奇思妙想,如果能够深入理解它,对于我们稳固js基础、提升前端大法技能来说大有裨益。
另外,本系列的相关代码均可以从 我的github 上获取到(DIY-A-jQuery-master)。
1. 免 new 实现
我们在使用很多插件的时候,都需要使用 new XXX() 的写法来实例化一个引用:
var list = new Slip(document.getElementById('slip'), {//options });
jQuery 同样作为一个面向对象的工具库,在我们创建一个实例时却无需使用 new 语法,节省了一些代码量:
var $div = $('div'); //不需要如下写法: //var $div = new $('div');
这种便捷的形式依赖了工厂模式,其实现非常简单,把 new 封装在库内即可,让每次调用 jQuery() 时自行在内部进行一次实例化:
(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 雏形,简单跑一下:
<div></div> <script>var $div = $('div'); //<div></div>console.log($div.jquery); //0.0.1 </script>
别忘了后续我们还希望能通过 $.extend / $.fn.extend 来扩展 JQ 的静态方法和原型方法,我们把出口方法抽出来增加这个 extend 的API:
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 的原型对象,再修改下这句代码即可:
Factory.prototype = Factory.fn = jQuery.prototype;
2. 写法优化
事实上我们不太喜欢再写多一个冗余的 Factory 构造函数来作为 window.jQuery 的引用,也不喜欢(在模块内部)使用 Factory.extend() 来扩展 JQ,它听起来和 JQ 没有半毛钱关系。
如果可以,直接把 jQuery 方法作为接口输出,且在模块内部能以 jQuery.extend() 的形式来调用扩展接口,这样的形式更佳。
也就是说我们希望代码应该是这样写的:
jQuery.extend = jQuery.fn.extend = function(){console.log(this)};window.jQuery = window.$ = jQuery;
“直接把 jQuery 方法作为接口输出”意味着我们要把工厂模式挪入 jQuery 方法中,显然我们不能这样改:
var version = "0.0.1",jQuery = function (selector) {return new jQuery(selector);};
这样死循环了,调用栈会直接爆掉~
于是我们可以抽出一个 init 方法来做初始化处理(比如简单地注入检索到的元素到JQ对象中),把 jQuery 方法中的内容更改为 return new init(selector) 就行了。
保证两个前提:
1. this 指向 jQuery 上下文2. 其原型指向 jQuery 的原型
第一点很好理解,方便我们直接在 init 方法中通过对 this 的操作来处理 JQ 实例上下文,如:
//注入元素到 JQ 实例对象中this[0] = elem;this.length = 1;
针对这点,我们不妨把 init 作为 jQuery.prototype 的属性方法来实现:
var version = "0.0.1",jQuery = function(selector) {return new jQuery.fn.init() //修改点1};//方便我们使用 jQuery.fn 来引用 jQuery 原型对象jQuery.fn = jQuery.prototype = {jquery: version,constructor: jQuery};//修改点2 —— init 作为原型方法,确保 this 指向正确jQuery.fn.init = function( selector ) {if ( !selector ) {return;} else {var elem = document.querySelector( selector );if ( elem ) {this[0] = elem;this.length = 1;}}};jQuery.extend = jQuery.fn.extend = function(){console.log(this)};window.$ = window.jQuery = jQuery;
然而这时候存在一个问题 —— JQ实例对象无法访问原型属性/方法:
var $div = $('div');console.log($div.jquery); //undefined
原因很简单——我们还未实现上述提及的第二个前提——“init 原型指向 jQuery 的原型”
在 js 中,实例的内部原型(__proto__)总是指向其构造函数的原型(prototype),而经过我们这番修改,JQ实例的构造函数已经变成了 jQuery.fn.init ,而其原型并非指向 jQuery 的原型,这导致 JQ 实例无法顺其原型链爬取到 jQuery.prototype。
要实现这个条件,只需要做小小改动——把 jQuery.fn.init 的原型指向 jQuery 的原型(jQuery.prototype / jQuery.fn)即可:
var init = jQuery.fn.init = function( selector ) {if ( !selector ) {return;} else {var elem = document.querySelector( selector );if ( elem ) {this[0] = elem;this.length = 1;}}};init.prototype = jQuery.fn; //修改点
这里贴下完整代码:
var version = "0.0.1",jQuery = function(selector) {return new jQuery.fn.init(selector)};jQuery.fn = jQuery.prototype = {jquery: version,constructor: jQuery};var init = jQuery.fn.init = function( selector, context, root ) {if ( !selector ) {return;} else {var elem = document.querySelector( selector );if ( elem ) {this[0] = elem;this.length = 1;}}};init.prototype = jQuery.fn;jQuery.extend = jQuery.fn.extend = function(){console.log(this)};window.$ = window.jQuery = jQuery;
3. 链式写法实现
JQ 里一个很大的亮点是,它支持链式写法,调用起来非常方便:
$('div').removeClass('hide').css('width', '100px')
其实现其实非常简单 —— 确保每个调用的方法尾部均返回自身即可,这里我们新增两个实例方法做示例:
jQuery.fn = jQuery.prototype = {jquery: version,constructor: jQuery,setBackground: function(){this[0].style.background = 'yellow';return this //返回自身引用},setColor: function(){this[0].style.color = 'blue';return this //返回自身引用}};var init = jQuery.fn.init = function( selector ) {if ( !selector ) {return this; } else {var elem = document.querySelector( selector );if ( elem ) {this[0] = elem;this.length = 1;}return this;}};
链式调用:
<div>hello world</div><script>var $div = $('div');$div.setBackground().setColor();
</script>
效果如下,杠杠的:
4. 冲突处理
存在某些情况,用户可能并不想拿 window.$ 甚至 window.jQuery 来引用 JQ 接口,或者已经有其它库使用了 window.$ 这个变量,如果我们粗暴地改变其引用肯定是不合理的。
so 我们来实现 JQ 中冲突处理的静态接口 jQuery.noConflict,这意味着在代码段开始时,就得先保存下当前 window.$ 和 window.jQuery 两个变量:
(function(){var _jQuery = window.jQuery,_$ = window.$;//var version = "0.0.1"...... })()
然后是实现 noConflict 方法,退耕还林,把保存的变量吐回去即可:
(function(){var _jQuery = window.jQuery,_$ = window.$;//var version = "0.0.1"......jQuery.noConflict = function( deep ) {//确保window.$没有再次被改写if ( window.$ === jQuery ) {window.$ = _$;}//确保window.jQuery没有再次被改写if ( deep && window.jQuery === jQuery ) {window.jQuery = _jQuery;}return jQuery; //返回 jQuery 接口引用};window.jQuery = window.$ = jQuery; })();
deep 参数类型为 Boolean,若为真,表示要求连window.jQuery 变量都需要吐回去。
留意在尾部我们返回了 jQuery 的接口引用,这意味着我们可以以
var $$$ = jQuery.noConflict()
的形式来把它赋予新的变量。
接着在外部运行如下代码:
<head><meta charset="UTF-8"><title>DIY A JQ</title><script>$ = 'old $';jQuery = 'old JQ'</script><script src="jQuery.js"></script>
</head>
<body><div>hello world</div><script>var $div = $('div');$div.setBackground().setColor();var $$$ = $.noConflict(true);console.log($); console.log(jQuery); console.log($$$);
</script>
输出如下:
第一篇就写到这里,相关的代码可以从 我的github 上下载(DIY-A-jQuery-master)到。
下次我们会试着实现模块化的写法,并与时俱进,改用 ES6解构赋值语法 + Rollup 来进行打包以减少可能存在的冗余代码段。
共勉~
转自:http://www.cnblogs.com/vajoy/p/5510743.html
更多参考:
jQuery: 操作select option方法集合
jQuery: 插件开发模式详解 $.extend(), $.fn, $.widget()
AngularJS jQuery 共存法则
下一篇:jQuery:从零开始,DIY一个jQuery(2)
本文转自: jQuery:从零开始,DIY一个jQuery(1)
jQuery:从零开始,DIY一个jQuery(1)相关推荐
- jQuery:从零开始,DIY一个jQuery(2)
在上篇文章我们简单实现了一个 jQuery 的基础结构,不过为了顺应潮流,这次咱把它改为模块化的写法,此举得以有效提升项目的可维护性,因此在后续也将以模块化形式进行持续开发. 模块化开发和编译需要用上 ...
- 从零开始,DIY一个jQuery(2)
在上篇文章我们简单实现了一个 jQuery 的基础结构,不过为了顺应潮流,这次咱把它改为模块化的写法,此举得以有效提升项目的可维护性,因此在后续也将以模块化形式进行持续开发. 模块化开发和编译需要用上 ...
- jquery从零开始-2.2 结构选择器(一)
接上一节 jquery从零开始-2.1 jQuery 选择器基础 结构选择器就是根据 HTML 文档结构中节点之间的包含或并列关系决定匹配元素的一种方法. jQuery 模仿 css 的关系过滤模式定 ...
- jQuery Masonry 一个 jQuery动态网格布局的插件
jQuery Masonry 是一个 jQuery动态网格布局的插件. 每个元素都是漂浮在固定的网格布局上面,就像一枚图钉定在墙上一样. 我们发现以下的15网站使用jQuery Masonry的范例. ...
- 用jQuery写的一个翻页,并封装为插件,
用jQuery写的一个翻页,并封装为插件, 1 *{ 2 margin:0; 3 padding: 0; 4 list-style: none; 5 text-decoration: none; 6 ...
- 所谓 jQuery 插件,怎样开发一个 jQuery 插件
简单来说,所谓 jQuery 插件就是扩展在 jQuery 原型对象上的一个方法.通过扩展 jQuery 对象,每次调用 jQuery 对象的时候,对象里面都包含了我们自己所添加的那个方法. 一般插件 ...
- 【jQuery插件】用jQuery Masonry快速构建一个pinterest网站布局(转)
[jQuery插件]用jQuery Masonry快速构建一个pinterest网站布局 时间:2011年03月21日作者:愚人码头查看次数:29,744 views评论次数:25条评论 前段时间领导 ...
- jQuery加载一个html页面到指定的div里
一.jQuery加载一个html页面到指定的div里 把a.html里面的某一部份的内容加载到b.html的一个div里. 比如:加载a.html里面的<div id="row&quo ...
- 如何编写一个Jquery插件
首先我们来搞清楚一些关于Jquery插件的知识: 一.插件的种类: 封装对象方法的插件 这种插件是将对象方法封装起来,用于对通过选择器获取的jQuery对象进行操作,是最常见的一种插件 封装全局函数的 ...
最新文章
- 没有执行力,谈什么目标理想?
- mysql mairadb skysql
- 《Java编码指南:编写安全可靠程序的75条建议》—— 指南20:使用安全管理器创建一个安全的沙盒...
- $(@_config=)什么意思?
- 初探Stage3D(一) 3D渲染基础原理
- Hystrix能解决的问题
- Buying Feed, 2010 Nov (单调队列优化DP)
- 格雷码 matlab,基于格雷码的结构光重建代码(MATLAB版本)
- AliOS Things网络连接技术概述
- libeio-异步I/O库初窥
- 【转】Canny 算法
- STL中的序列式容器——list(列表)
- hdu 1284 dp
- python画立体温度分布图_Origin绘制3D立体温度分布图的方法
- 数据以及空值数据处理方法
- IT行业都有什么职位?
- 各国家 MCC 和 MNC 列表2
- 自己想要什么 过什么样的生活
- 一图看懂信用报告在线查询指南
- 常见的手机端头部banner切换代码设置
热门文章
- すぬけ君の地下鉄旅行 / Snuke's Subway Trip(AtCoder-2069)
- A*B问题(信息学奥赛一本通-T1036)
- 29 WM配置-策略-出库策略2-定义“紧急FIFO”策略(Stringent FIFO)
- 为什么openstack要用rabbitmq这类消息中间件来进行RPC这类的操作呢,直接rpc不行吗?
- python中的lambda函数用法--无需定义函数名的函数或子程序,避免代码充斥着大量单行函数
- PhpStorm调用浏览器运行php文件
- HTML5新特性基础学习笔记下
- [有限元]证明常应变三角形单元形函数面积分线积分公式
- python海龟绘图画树_Python:海龟绘图(二)
- C++算法七:插入排序