zoukankan      html  css  js  c++  java
  • jQuery 源码分析:当 selector 传来一个函数时,怎么进行处理?

    本文章为 0.9 版本,将会在稍后润色更新。本文使用的 jQuery 版本为 3.4.0

    我们知道使用 $ 操作符时,可以往里面塞很多类型的参数,字符串,对象,函数...,jQuery 会根据不同的参数类型,让我们执行不同的操作。这其实就是“函数重载”的价值所在:它暴露出一个简洁的接口给用户,允许用户在使用这个接口时,通过参数类型控制函数的行为方式,是一种对用户非常友好的设计。

    那么 jQuery 在 $ 这里的函数重载是怎样实现的呢?这篇文章我们只关心其中的一个细枝末节,传入一个函数时,jQuery 会怎么做:

    首先我们看这里,jQuery 首先会判断传入的 selector 是不是一个函数,这里使用的是包装后的 isFunction 函数,它基本等同于 typeof 判断:

    if (isFunction(selector)) {
      return root.ready !== undefined ?
        root.ready(selector) :
    
        // Execute immediately if ready is not present
        selector(jQuery);
    }
    

    如果判断是一个函数,jQuery 会去判断 root.ready 的值,这里 root 是什么呢?看下面的代码:

    // Method init() accepts an alternate rootjQuery
    // so migrate can support jQuery.sub (gh-2101)
    root = root || rootjQuery;
    
    rootjQuery = jQuery(document);
    

    一般情况下,rootjQuery(document) 的返回值,这里就有点绕了,因为我们本来是要解决“选择器是函数的时候,jQuery 会怎么做”的问题,现在我们先要解决“选择器是对象的时候,jQuery 会怎么做”。在源码里 jQuery 是这样处理的:

    return jQuery.makeArray(selector, this);
    

    我们越来越深入了,现在我们要解决 jQuery.makeArray 这个方法在做什么的问题,这部分的代码在这里:

    makeArray: function (arr, results) {
        var ret = results || [];
    
        if (arr != null) {
          if (isArrayLike(Object(arr))) {
            jQuery.merge(ret,
              typeof arr === "string" ?
                [arr] : arr
            );
          } else {
            push.call(ret, arr);
          }
        }
    
        return ret;
      },
    

    可以简单理解为,jQuery 会把一个对象传入到它的数组中。

    所以到目前为止,我们大概弄懂了 root 到底是个什么东西,简单来说,是一个数组,并且第一个元素是 document 对象。我们继续,接下来,我们想知道的实际上是 root.ready 是什么,当我们回顾一下最初的代码就能知道,jQuery 处理函数的逻辑就是判断 root.ready 的值,如果该值为真值,就调用 root.ready 方法,并把我们的函数当做参数传进去,如果为假值,则直接调用这个函数,把我们的 jQuery 对象当做参数传进去。

    对不起,接下来的重头戏我将会在日后补上了,这次我先描述一个大概,让我们看看和 root.ready 有关的源码:

    // The deferred used on DOM ready
    var readyList = jQuery.Deferred();
    
    jQuery.fn.ready = function (fn) {
    
      readyList
        .then(fn)
    
        // Wrap jQuery.readyException in a function so that the lookup
        // happens at the time of error handling instead of callback
        // registration.
        .catch(function (error) {
          jQuery.readyException(error);
        });
    
      return this;
    };
    
    jQuery.extend({
    
      // Is the DOM ready to be used? Set to true once it occurs.
      isReady: false,
    
      // A counter to track how many items to wait for before
      // the ready event fires. See #6781
      readyWait: 1,
    
      // Handle when the DOM is ready
      ready: function (wait) {
    
        // Abort if there are pending holds or we're already ready
        if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
          return;
        }
    
        // Remember that the DOM is ready
        jQuery.isReady = true;
    
        // If a normal DOM Ready event fired, decrement, and wait if need be
        if (wait !== true && --jQuery.readyWait > 0) {
          return;
        }
    
        // If there are functions bound, to execute
        readyList.resolveWith(document, [jQuery]);
      }
    });
    
    // ===> readyList
    var readyList = jQuery.Deferred();
    

    其实注释里也写的很清晰了,jQuery.ready 将会在 DOM 加载完毕后,调用传入的参数,但是就是这样一个简单地功能,jQuery 采用了 jQuery.Deferred() 方法去实现,这个方法到底做了什么呢?请看我(可能的)下一篇文章:)

    总结一下,如果给 jQuery 传入一个函数类型的参数会发生什么?不完全准确的回答是,它会等 DOM 加载完毕后被调用。具体来说,是等待 doument 对象上的 DOMContentLoaded 事件被触发。

    下面是无声的证据:

            jQuery.ready.then = readyList.then;
    
    	// The ready event handler and self cleanup method
    	function completed() {
    		document.removeEventListener("DOMContentLoaded", completed);
    		window.removeEventListener("load", completed);
    		jQuery.ready();
    	}
    
    	// Catch cases where $(document).ready() is called
    	// after the browser event has already occurred.
    	// Support: IE <=9 - 10 only
    	// Older IE sometimes signals "interactive" too soon
    	if (document.readyState === "complete" ||
    		(document.readyState !== "loading" && !document.documentElement.doScroll)) {
    
    		// Handle it asynchronously to allow scripts the opportunity to delay ready
    		window.setTimeout(jQuery.ready);
    
    	} else {
    
    		// Use the handy event callback
    		document.addEventListener("DOMContentLoaded", completed);
    
    		// A fallback to window.onload, that will always work
    		window.addEventListener("load", completed);
    	}
    
  • 相关阅读:
    使用Apache的ab工具进行压力测试
    Effective Java开篇
    mysql删除同一表中重复字段记录
    正则表达式的元字符匹配
    几个学习git的地方
    创建和销毁对象
    遇到多个构造器参数时要考虑用构建器
    Java的类和接口
    转:流言粉碎机:每天对着电脑46小时的人必看
    页面选中文字弹出层,点击层中文字或者图片触发事件
  • 原文地址:https://www.cnblogs.com/libinfs/p/10750524.html
Copyright © 2011-2022 走看看