Java教程从入门到精通百度云,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" 创建的

                // 如果是深复制

                if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {

                    // 数组

                    if (copyIsArray) {

                        copyIsArray = false;

                        clone = src && jQuery.isArray(src) ? src : [];

 

                        // 对象

                    } else {

                        clone = src && jQuery.isPlainObject(src) ? src : {};

                    }

 

                    // Never move original objects, clone them

                    // 递归

                    target[name] = jQuery.extend(deep, clone, copy);

 

                    // Don't bring in undefined values

                    // 最终都会到这条分支

                    // 简单的值覆盖

                } else if (copy !== undefined) {

                    target[name] = copy;

                }

            }

        }

    }

 

    // Return the modified object

    // 返回新的 target

    // 如果 i < length ,是直接返回没经过处理的 target,也就是 arguments[0]

    // 也就是如果不传需要覆盖的源,调用 $.extend 其实是增加 jQuery 的静态方法

    return target;

};

需要注意的是这一句 jQuery.extend = jQuery.fn.extend = function() {} ,也就是 jQuery.extend 的实现和 jQuery.fn.extend 的实现共用了同一个方法,但是为什么能够实现不同的功能了,这就要归功于 Javascript 强大(怪异?)的 this 了。

  1. 在 jQuery.extend() 中,this 的指向是 jQuery 对象(或者说是 jQuery 类),所以这里扩展在 jQuery 上;

  2. 在 jQuery.fn.extend() 中,this 的指向是 fn 对象,前面有提到 jQuery.fn = jQuery.prototype ,也就是这里增加的是原型方法,也就是对象方法。

六、jQuery 的链式调用及回溯

=================

另一个让大家喜爱使用 jQuery 的原因是它的链式调用,这一点的实现其实很简单,只需要在要实现链式调用的方法的返回结果里,返回 this ,就能够实现链式调用了。

当然,除了链式调用,jQuery 甚至还允许回溯,看看:


// 通过 end() 方法终止在当前链的最新过滤操作,返回上一个对象集合

$('div').eq(0).show().end().eq(1).hide();

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析,以及算法刷题LeetCode中文版的小伙伴们可以点赞后点击这里即可免费获取!

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

image

================

另一个让大家喜爱使用 jQuery 的原因是它的链式调用,这一点的实现其实很简单,只需要在要实现链式调用的方法的返回结果里,返回 this ,就能够实现链式调用了。

当然,除了链式调用,jQuery 甚至还允许回溯,看看:


// 通过 end() 方法终止在当前链的最新过滤操作,返回上一个对象集合

$('div').eq(0).show().end().eq(1).hide();

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析,以及算法刷题LeetCode中文版的小伙伴们可以点赞后点击这里即可免费获取!

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-NF5Ztrb6-1628285012803)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值