整体而言,我觉得 jQuery 采用的是总–分的结构,虽然JavaScript有着作用域的提升机制,但是 9000 多行的代码为了相互的关联性,并不代表所有的变量都要定义在最顶部。在 jQuery 中,只有全局都会用到的变量、正则表达式定义在了代码最开头,而每个模块一开始,又会定义一些只在本模块会使用到的变量、正则、方法等。所以在一开始的阅读的过程中会有很多看不懂其作用的变量,正则,方法。
所以,我觉得阅读源码很重要的一点是,摒弃面向过程的思维方式,不要刻意去追求从上至下每一句都要在一开始弄明白。很有可能一开始你在一个奇怪的方法或者变量处卡壳了,很想知道这个方法或变量的作用,然而可能它要到几千行处才被调用到。如果去追求这种逐字逐句弄清楚的方式,很有可能在碰壁几次之后阅读的积极性大受打击。
道理说了很多,接来下进入真正的正文,对 jQurey 的一些前期准备,小的细节进行分析:
二、jQurey 闭包结构
=============
// 用一个函数域包起来,就是所谓的沙箱
// 在这里边 var 定义的变量,属于这个函数域内的局部变量,避免污染全局
// 把当前沙箱需要的外部变量通过函数参数引入进来
// 只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数
(function(window, undefined) {
// jQuery 代码
})(window);
jQuery 具体的实现,都被包含在了一个立即执行函数构造的闭包里面,为了不污染全局作用域,只在后面暴露 $ 和 jQuery 这 2 个变量给外界,尽量的避开变量冲突。常用的还有另一种写法:
(function(window) {
// JS代码
})(window, undefined);
比较推崇的的第一种写法,也就是 jQuery 的写法。二者有何不同呢,当我们的代码运行在更早期的环境当中(pre-ES5,eg. Internet Explorer 8),undefined 仅是一个变量且它的值是可以被覆盖的。意味着你可以做这样的操作:
undefined = 42
console.log(undefined) // 42
当使用第一种方式,可以确保你需要的 undefined 确实就是 undefined。
另外不得不提出的是,jQuery 在这里有一个针对压缩优化细节,使用第一种方式,在代码压缩的时候,window 和 undefined 都可以压缩为 1 个字母并且确保它们就是 window 和 undefined。
// 压缩策略
// w -> windwow , u -> undefined
(function(w, u) {
})(window);
三、jQuery 无new构造
===============
嘿,回想一下使用 jQuery 的时候,实例化一个 jQuery 对象的方法:
// 无 new 构造
$(‘#test’).text(‘Test’);
// 当然也可以使用 new
var test = new $(‘#test’);
test.text(‘Test’);
大部分人使用 jQuery 的时候都是使用第一种无 new 的构造方式,直接 $(‘’) 进行构造,这也是 jQuery 十分便捷的一个地方。当我们使用第一种无 new 构造方式的时候,其本质就是相当于 new jQuery(),那么在 jQuery 内部是如何实现的呢?看看:
(function(window, undefined) {
var
// …
jQuery = function(selector, context) {
// The jQuery object is actually just the init constructor ‘enhanced’
// 看这里,实例化方法 jQuery() 实际上是调用了其拓展的原型方法 jQuery.fn.init
return new jQuery.fn.init(selector, context, rootjQuery);
},
// jQuery.prototype 即是 jQuery 的原型,挂载在上面的方法,即可让所有生成的 jQuery 对象使用
jQuery.fn = jQuery.prototype = {
// 实例化化方法,这个方法可以称作 jQuery 对象构造器
init: function(selector, context, rootjQuery) {
// …
}
}
// 这一句很关键,也很绕
// jQuery 没有使用 new 运算符将 jQuery 实例化,而是直接调用其函数
// 要实现这样,那么 jQuery 就要看成一个类,且返回一个正确的实例
// 且实例还要能正确访问 jQuery 类原型上的属性与方法
// jQuery 的方式是通过原型传递解决问题,把 jQuery 的原型传递给jQuery.prototype.init.prototype
// 所以通过这个方法生成的实例 this 所指向的仍然是 jQuery.fn,所以能正确访问 jQuery 类原型上的属性与方法
jQuery.fn.init.prototype = jQuery.fn;
})(window);
大部分人初看
jQuery.fn.init.prototype = jQuery.fn
这一句都会被卡主,很是不解。但是这句真的算是 jQuery 的绝妙之处。理解这几句很重要,分点解析一下:
-
首先要明确,使用 $(‘xxx’) 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法去完成。
-
将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。
-
也就是实例化方法存在这么一个关系链
-
jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;
-
new jQuery.fn.init() 相当于 new jQuery() ;
-
jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。
四、jQuery 方法的重载
==============
1、方法重载基本介绍
jQuery 源码晦涩难读的另一个原因是,使用了大量的方法重载,但是用起来却很方便:
// 获取 title 属性的值
$(‘#id’).attr(‘title’);
// 设置 title 属性的值
$(‘#id’).attr(‘title’,‘jQuery’);
// 获取 css 某个属性的值
$(‘#id’).css(‘title’);
// 设置 css 某个属性的值
$(‘#id’).css(‘width’,‘200px’);
2、应用场景
方法的重载即是一个方法实现多种功能,经常又是 get 又是 set,虽然阅读起来十分不易,但是从实用性的角度考虑,这也是为什么 jQuery 如此受欢迎的原因,大多数人使用 jQuery() 构造方法使用的最多的就是直接实例化一个 jQuery 对象,但其实在它的内部实现中,有着 9 种不同的方法重载场景:
// 接受一个字符串,其中包含了用于匹配元素集合的 CSS 选择器
jQuery([selector,[context]])
// 传入单个 DOM
jQuery(element)
// 传入 DOM 数组
jQuery(elementArray)
// 传入 JS 对象
jQuery(object)
// 传入 jQuery 对象
jQuery(jQuery object)
// 传入原始 HTML 的字符串来创建 DOM 元素
jQuery(html,[ownerDocument])
jQuery(html,[attributes])
// 传入空参数
jQuery()
// 绑定一个在 DOM 文档载入完成后执行的函数
jQuery(callback)
所以读源码的时候,很重要的一点是结合 jQuery API 进行阅读,去了解方法重载了多少种功能,同时我想说的是,jQuery 源码有些方法的实现特别长且繁琐,因为 jQuery 本身作为一个通用性特别强的框架,一个方法兼容了许多情况,也允许用户传入各种不同的参数,导致内部处理的逻辑十分复杂,所以当解读一个方法的时候感觉到了明显的困难,尝试着跳出卡壳的那段代码本身,站在更高的维度去思考这些复杂的逻辑是为了处理或兼容什么,是否是重载,为什么要这样写,一定会有不一样的收获。其次,也是因为这个原因,jQuery 源码存在许多兼容低版本的 HACK 或者逻辑十分晦涩繁琐的代码片段,浏览器兼容这样的大坑极其容易让一个前端工程师不能学到编程的精髓,所以不要太执着于一些边角料,即使兼容性很重要,也应该适度学习理解,适可而止。
五、jQuery.fn.extend 与 jQuery.extend
==================================
1、基本分析
extend 方法在 jQuery 中是一个很重要的方法,jQuey 内部用它来扩展静态方法或实例方法,而且我们开发 jQuery 插件开发的时候也会用到它。但是在内部,是存在 jQuery.fn.extend 和 jQuery.extend 两个 extend 方法的,而区分这两个 extend 方法是理解 jQuery 的很关键的一部分。先看结论:
- jQuery.extend(object) 为扩展 jQuery 类本身,为类添加新的静态方法;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
总结
三套“算法宝典”
算法刷题LeetCode中文版(为例)
人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。
最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!
由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!
由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-XhvNckOI-1712503613174)]