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 的绝妙之处。理解这几句很重要,分点解析一下:

  1. 首先要明确,使用 $(‘xxx’) 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法去完成。

  2. 将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。

  3. 也就是实例化方法存在这么一个关系链

  • 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 的很关键的一部分。先看结论:

  1. jQuery.extend(object) 为扩展 jQuery 类本身,为类添加新的静态方法;

  2. jQuery.fn.extend(object) 给 jQuery 对象添加实例方法,也就是通过这个 extend 添加的新方法,实例化的 jQuery 对象都能使用,因为它是挂载在 jQuery.fn 上的方法(上文有提到,jQuery.fn = jQuery.prototype )。

2、官方解释


它们的官方解释是:

  1. jQuery.extend(): 把两个或者更多的对象合并到第一个当中,

  2. jQuery.fn.extend():把对象挂载到 jQuery 的 prototype 属性,来扩展一个新的 jQuery 实例方法。

也就是说,使用 jQuery.extend() 拓展的静态方法,我们可以直接使用 $.xxx 进行调用(xxx是拓展的方法名),

而使用 jQuery.fn.extend() 拓展的实例方法,需要使用 $().xxx 调用。

源码解析较长:


// 扩展合并函数

// 合并两个或更多对象的属性到第一个对象中,jQuery 后续的大部分功能都通过该函数扩展

// 虽然实现方式一样,但是要注意区分用法的不一样,那么为什么两个方法指向同一个函数实现,但是却实现不同的功能呢,

// 阅读源码就能发现这归功于 this 的强大力量

// 如果传入两个或多个对象,所有对象的属性会被添加到第一个对象 target

// 如果只传入一个对象,则将对象的属性添加到 jQuery 对象中,也就是添加静态方法

// 用这种方式,我们可以为 jQuery 命名空间增加新的方法,可以用于编写 jQuery 插件

// 如果不想改变传入的对象,可以传入一个空对象:$.extend({}, object1, object2);

// 默认合并操作是不迭代的,即便 target 的某个属性是对象或属性,也会被完全覆盖而不是合并

// 如果第一个参数是 true,则是深拷贝

// 从 object 原型继承的属性会被拷贝,值为 undefined 的属性不会被拷贝

// 因为性能原因,JavaScript 自带类型的属性不会合并

jQuery.extend = jQuery.fn.extend = function() {

    var src, copyIsArray, copy, name, options, clone,

        target = arguments[0] || {},

        i = 1,

        length = arguments.length,

        deep = false;

 

    // Handle a deep copy situation

    // target 是传入的第一个参数

    // 如果第一个参数是布尔类型,则表示是否要深递归,

    if (typeof target === "boolean") {

        deep = target;

        target = arguments[1] || {};

        // skip the boolean and the target

        // 如果传了类型为 boolean 的第一个参数,i 则从 2 开始

        i = 2;

    }

 

    // Handle case when target is a string or something (possible in deep copy)

    // 如果传入的第一个参数是 字符串或者其他

    if (typeof target !== "object" && !jQuery.isFunction(target)) {

        target = {};

    }

 

    // extend jQuery itself if only one argument is passed

    // 如果参数的长度为 1 ,表示是 jQuery 静态方法

    if (length === i) {

        target = this;

        --i;

    }

 

    // 可以传入多个复制源

    // i 是从 1或2 开始的

    for (; i < length; i++) {

        // Only deal with non-null/undefined values

        // 将每个源的属性全部复制到 target 上

        if ((options = arguments[i]) != null) {

            // Extend the base object

            for (name in options) {

                // src 是源(即本身)的值

                // copy 是即将要复制过去的值

                src = target[name];

                copy = options[name];

 

                // Prevent never-ending loop

                // 防止有环,例如 extend(true, target, {'target':target});

                if (target === copy) {

                    continue;

                }

 

                // Recurse if we're merging plain objects or arrays

                // 这里是递归调用,最终都会到下面的 else if 分支

                // jQuery.isPlainObject 用于测试是否为纯粹的对象

                // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的

                // 如果是深复制


## 总结

> 这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!


某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人![**资料免费领取方式:戳这里**](https://gitee.com/vip204888/java-p7)

cts or arrays

                // 这里是递归调用,最终都会到下面的 else if 分支

                // jQuery.isPlainObject 用于测试是否为纯粹的对象

                // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的

                // 如果是深复制


## 总结

> 这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!


某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人![**资料免费领取方式:戳这里**](https://gitee.com/vip204888/java-p7)

![](https://img-blog.csdnimg.cn/img_convert/b09fc118e9472934d3bc21c0200d0e5d.png)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值