Jquery的一些源码解析

178 阅读3分钟

1.Jquery的构建方式

Jquery的构建方式有两种,一种是用new 构建,另一种是直接调用函数构建

    <div class="test"></div>
    <script type="text/javascript">
      var q = new $('.test')
      var s = $('.test')
      console.log(s)
      console.log(q)
    </script>

将s 和q打印出来可以得出s和q的数据结构是相同的,但两者是不相等的。

分析

  • Jquery的函数构建

安装Jquery的书写方式,我们可以使用类似().ready()去调用相关的属性和方法,要实现这样,得把jquery就要看成一个类,那么()应该是返回类的实例才对

    (function (window, undefined) {
        var jQuery = function (selector, context) {
          return new jQuery
        }
      })(window)

通过上面的例子可以看出,如果采用new jQuery的构建方式虽然会返回一个时例,但会出现死循环情况

  • 返回一个正确的实例

在jquery中jQuery函数是返回new jquery.fn.init(selector, context),相当于把jQuery函数看出一个工厂方法,从上面的分析得知,new jquery.fn.init(selector, context)应与new jQuery相等,才能实现两种构建方式,所以两者的原型应该相等,即jquery.fn.init.prototype==jQuery.prototype。

 (function (window, undefined) {
        var jQuery = function (selector, context) {
          return new jquery.fn.init(selector, context)
        }

        jquery.fn = jQuery.prototype = {
          init: function (selector, context) {
            
          },
        }
        jQuery.fn.init.prototype=jQuery.fn//jQuery.fn==jQuery.prototype
      })()

#2.链式调用

DOM链式调用的处理能够节约代码,所返回的都是同一个对象,可以提高代码的效率。在jquery中实现链式调用是通过简单扩展原型方法并通过return this的形式来实现跨浏览器的链式调用

jQuery.prototype = {
    init: function() {
        return this;
    },
    name: function() {
        return this
    }
}

3.val的实现

在jquery中val()没有传参时是获取相关的值,如果有传参,表示进行赋值。实现该功能主要运用的是函数的重载。函数的重载是指函数名相同,但传递的参数不同。但在js中是没有函数的重载概念的,后声明的函数会覆盖签名声明的函数,以下是jq的实现方式

(function (window, undefined) {
        function addMethod(obj, name, f) {
          var old = obj[name]
          obj[name] = function () {
            if (f.length === arguments.length) {
              return f.apply(this, arguments)
            } else {
             return old.apply(this, arguments)
            }
          }
        }
        var people = {
          name: ['Zhang san', 'Li si'],
        }
        addMethod(people, 'find',find1 )
        var find1=function () {
          console.log('无参数')
          return this.values
        }
        addMethod(people, 'find', find2)
        var find2=function (firstname) {
          console.log('一个参数')
          var ret = []
          for (var i = 0; i < this.values.length; i++) {
            if (this.name[i].indexOf(firstname) === 0) {
              ret.push(this.name[i])
            }
          }
          return ret
        }
    
        console.log(people.find())
        console.log(people.find('Zhang'))
      })(window)
  • 实现的关键是old,old相当于指针,每一次都指向上一次调用的函数。
  • obj[name]函数中的this是指向obj的,arguments是函数f的实参,f.length是函数f的形参
  • 第一次调用addMethod的时候,old=>undefined,obj[name]=>find1;因为old在obj[name]函数中调用了,所以此时obj不会被垃圾回收,会继续保存,相当于必包。
  • 第二次调用addMethod,old=>find1,obj[name]=>find2.这时的old指向上一次调用的函数,又是闭包,继续保存。 -内存中实际上有两个个 obj[name]和old,因为两次method的内存都没有删除,实现了两个个函数共存,用old把他们链接起来
  • people.find() 的时候,就会最先调用最后一次调用method时定义的function,如果参数个数相同 也就是 arguments.length === fnc.length 那么就执行就好了,也不用找别的函数了,如果不相同的话,那就得用到old了 return old.apply(this,arguments); old指向的是上次addMethod调用时定义的函数,所以我们就去上一次的找,如果找到了,继续执行 arguments.length === fnc.length 如果找不到,再次调用old 继续向上找,只要你定义过,肯定能找到的, 总结:运用闭包的原理使两个函数共存于内存中,old相当于一个指针,指向上一次定义的function,每次调用的时候,决定是否需要寻找。

参考:

blog.csdn.net/weixin_3373…