zepto源码系列一之核心$()

        最近在做移动端web项目,用到了开源框架zepto,语法上和jquery并无明显区别,所以怀着好奇的心情看了一下其源代码,接下来先介绍其核心方法$()

 

一、$()的源码      

// `$` will be the base `Zepto` object. When calling this
  // function just call `$.zepto.init, which makes the implementation
  // details of selecting nodes and creating Zepto collections
  // patchable in plugins.
  $ = function(selector, context){
    return zepto.init(selector, context)
  }

可以看到$()调用了zepto.init(selctor, context),下面进入zepto的init中

// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
  // takes a CSS selector and an optional context (and handles various
  // special cases).
  // This method can be overriden in plugins.
  zepto.init = function(selector, context) {
    var dom
    // If nothing given, return an empty Zepto collection
    if (!selector) return zepto.Z()
    // Optimize for string selectors
    else if (typeof selector == 'string') {
      selector = selector.trim()
      // If it's a html fragment, create nodes from it
      // Note: In both Chrome 21 and Firefox 15, DOM error 12
      // is thrown if the fragment doesn't begin with <
      if (selector[0] == '<' && fragmentRE.test(selector))
        dom = zepto.fragment(selector, RegExp.$1, context), selector = null
      // If there's a context, create a collection on that context first, and select
      // nodes from there
      else if (context !== undefined) return $(context).find(selector)
      // If it's a CSS selector, use it to select nodes.
      else dom = zepto.qsa(document, selector)
    }
    // If a function is given, call it when the DOM is ready
    else if (isFunction(selector)) return $(document).ready(selector)
    // If a Zepto collection is given, just return it
    else if (zepto.isZ(selector)) return selector
    else {
      // normalize array if an array of nodes is given
      if (isArray(selector)) dom = compact(selector)
      // Wrap DOM nodes.
      else if (isObject(selector))
        dom = [selector], selector = null
      // If it's a html fragment, create nodes from it
      else if (fragmentRE.test(selector))
        dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
      // If there's a context, create a collection on that context first, and select
      // nodes from there
      else if (context !== undefined) return $(context).find(selector)
      // And last but no least, if it's a CSS selector, use it to select nodes.
      else dom = zepto.qsa(document, selector)
    }
    // create a new Zepto collection from the nodes found
    return zepto.Z(dom, selector)
  }

第一步,在init方法中,首先判断如果传入的selector为空,则返回一个空的zepto集合z()

// `$.zepto.Z` swaps out the prototype of the given `dom` array
  // of nodes with `$.fn` and thus supplying all the Zepto functions
  // to the array. Note that `__proto__` is not supported on Internet
  // Explorer. This method can be overriden in plugins.
  zepto.Z = function(dom, selector) {
    dom = dom || []
    dom.__proto__ = $.fn
    dom.selector = selector || ''
    return dom
  }


在z方法中,待返回的dom的_proto_属性被赋给了$.fn,而$.fn表示了zepto对象的所有可用方法,而传入的dom和selector均为空,则dom的selector为空,下面编写代码验证一下

console.log($());

结果:

zepto源码系列一之核心$()_第1张图片

可以看到验证了以上所述

第二步,如果selctor是个string,并且是个html串,则调用zepto.fragment

zepto.fragment = function(html, name, properties) {
    var dom, nodes, container

    // 如果是类似于<div id="test" />,则先创建一个该node
    if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))

    if (!dom) {//如果node非空,则将类似于<div id="test" />替换为<div id="test"></div>
      if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
      if (name === undefined) name = fragmentRE.test(html) && RegExp.$1//取得标签名,例如div
	
	// containers = {
    // 'tr': document.createElement('tbody'),
    //  'tbody': table, 'thead': table, 'tfoot': table,
    //  'td': tableRow, 'th': tableRow,
    //  '*': document.createElement('div')
    //},
	
	if (!(name in containers)) name = '*'//设置容器标签名,如果不是tr,tbody,thead,tfoot,td,th,则容器标签名为div

      container = containers[name]
      container.innerHTML = '' + html
      dom = $.each(slice.call(container.childNodes), function(){//循环遍历每个子节点
        container.removeChild(this)
      })
    }
	
	//如果properties是原始对象
    if (isPlainObject(properties)) {
      nodes = $(dom)//将dom转成zepto对象,为了方便下面调用zepto上的方法
      $.each(properties, function(key, value) {
	  // methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
        if (methodAttributes.indexOf(key) > -1) nodes[key](value)
        else nodes.attr(key, value)
      })
    }
	//返回将字符串转成的DOM节点后的数组,比如'<li></li><li></li><li></li>'转成[li,li,li]
    return dom
  }

下面传入一个html字符串来进行验证:

不传入properties

var dom = $("<p>Hello</p>");
    console.log(dom.text());

运行结果:

Hello

 

传入properties

var dom = $("<p />",
        {
            text:"Hello",
            id:"greeting",
            css:{color:'darkblue'}
        });

    console.log(dom.attr('id'));
    console.log(dom.text());
    console.log(dom.css('color'));

运行结果:

greeting

Hello

rgb(0, 0, 139)

 

第三步,如果存在context,则在context中查找

第四步,如果是选择器,则使用选择器进行查找

// `$.zepto.qsa` is Zepto's CSS selector implementation which
  // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
  // This method can be overriden in plugins.
  zepto.qsa = function(element, selector){
    var found,
        maybeID = selector[0] == '#',
        maybeClass = !maybeID && selector[0] == '.',
        nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // 从第2个字符开始得到剩下的字符串,如#test->test,.test->test
        isSimple = simpleSelectorRE.test(nameOnly)//不是选择器集合
    return (isDocument(element) && isSimple && maybeID) ?
      ( (found = element.getElementById(nameOnly)) ? [found] : [] ) ://by id
      (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
      slice.call(
        isSimple && !maybeID ?
          maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class
          element.getElementsByTagName(selector) : // Or a tag
          element.querySelectorAll(selector) // Or it's not simple, and we need to query all
      )
  }

 

第五步,如果selector是一个函数,则直接执行这个函数

实例:

$(function() {
        console.log('hello world');
    });

结果:

hello world

第六步,如果selector是一个zepto对象,则直接返回此selector

 // `$.zepto.isZ` should return `true` if the given object is a Zepto
  // collection. This method can be overriden in plugins.
  zepto.isZ = function(object) {
    return object instanceof zepto.Z
  }

上述代码就是用来判断是否为zepto对象

 

第七步,如果selector是一个数组,则dom对象就等于此数组中的非空值组合

function isArray(value) { return value instanceof Array }

上述代码用来判断对象是否为数组

 

第八步,如果selector是一个对象,则dom对象就等于此对象构成的一个数组

  function isObject(obj)     { return type(obj) == "object" }
function type(obj) {
    return obj == null ? String(obj) :
      class2type[toString.call(obj)] || "object"
  }

上述代码用来判断是否为一个对象


综上,总的流程是根据selector为空,selector为string类型、selector为函数、selector为数组、selector为zepto对象、selector为对象等进行处理

 


 


 

 

你可能感兴趣的:(zepto源码系列一之核心$())