zoukankan      html  css  js  c++  java
  • 【zepto学习笔记01】核心方法$()

    前言

    我们移动端基本使用zepto了,而我也从一个小白变成稍微靠谱一点的前端了,最近居然经常要改到zepto源码
    但是,我对zepto不太熟悉,其实前端水准还是不够,所以便私下偷偷学习下吧,别被发现了

    核心方法$()

    我们使用这个方法一般有几个用途(我这里只说我自己用到过的),这里根据使用度排个序:

    ① 选择器/$(selector)

    将返回一个包装过的dom集合对象(有很多选择器)

    ② html字符串/$(domStr)

    仍然返回一个包装过的dom对象,他会将字符串初始化为我们的dom结构
    PS:新增了一个方法可以直接赋予dom结构属性,我们这里不关注

    ③ 函数/$(function(){})

    我基本没这么用过,他实在文档加载结束后执行

    暂时不管这些东西吧。我们这里来一个个看看他们的实现:

     1 $('div')
     2 //=> all DIV elements on the page
     3 $('#foo')
     4 //=> element with ID "foo"
     5 
     6 // create element:
     7 $("<p>Hello</p>")
     8 //=> the new P element
     9 // create element with attributes:
    10 $("<p />",
    11 {
    12 text: "Hello",
    13 id: "greeting",
    14 css: { color: 'darkblue' }
    15 })
    16 //=> <p id=greeting style="color:darkblue">Hello</p>
    17 // execute callback when the page is ready:
    18 Zepto(function ($) {
    19 alert('Ready to Zepto!')
    20 })

    入口$()

    只如初见

    首先我们来看看选择器,在zepto代码中有这么一行代码:

    1 $ = function (selector, context) {
    2     return zepto.init(selector, context)
    3 }

    这里其实前面就定义了$这个变量:

    var undefined, key, $, classList, emptyArray = [],

    这里初始化了$这个变量为一个函数,最后并将这个变量公开化(现在再匿名还是中,外面是访问不到的),他具体是这样干的

     1 var Zepto = function () {
     2     var zepto = {}, $;
     3     zepto.init = function (selector, context) {
     4     };
     5     $ = function (selector, context) {
     6         return zepto.init(selector, context);
     7     };
     8     $.zepto = zepto;
     9     return $;
    10 };
    11 window.$ = Zepto;

    于是我们在页面中使用这样的代码:

    1 var el = $('#id');
    2 //实际上是
    3 var el = zepto.init('#id');

    具体里面的逻辑就是我们的重点,于是我们跟进去慢慢看吧

    一查到底

    我们详细看看zepto.init这个方法

     1 zepto.init = function (selector, context) {
     2     if (!selector) return zepto.Z()
     3     else if (isFunction(selector)) return $(document).ready(selector)
     4     else if (zepto.isZ(selector)) return selector
     5     else {
     6         var dom
     7         if (isArray(selector)) dom = compact(selector)
     8         else if (isObject(selector))
     9             dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
    10         else if (fragmentRE.test(selector))
    11             dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
    12         else if (context !== undefined) return $(context).find(selector)
    13         else dom = zepto.qsa(document, selector)
    14         return zepto.Z(dom, selector)
    15     }
    16 }

    第一步,如果选择器selector不存在的话,就调用默认的方法,说白了就是返回一个集合长度为0的包装对象

    1 //通过给dom设置__proto__属性指向$.fn来达到继承$.fn上所有方法的目的
    2 //ie自然是不支持的,zepto也基本不理睬ie
    3 zepto.Z = function (dom, selector) {
    4     dom = dom || []
    5     dom.__proto__ = $.fn
    6     dom.selector = selector || ''
    7     return dom
    8 }

    第二步,处理传入函数情况,如果传入是函数的话就在文档加载结束后执行
    第三步,处理传入zepto对象,如果已经是zepto包装对象了就直接返回

    1 zepto.isZ = function (object) {
    2     return object instanceof zepto.Z
    3 }

    第四步,开始了复杂的逻辑计算了,我们这里单独提出来

    selector处理

    ① 数组项

    如果对象是一个数组,则去掉其中无意义的数组项

    1 //清除给定的参数中的null或undefined,注意0==null,'' == null为false
    2 function compact(array) {
    3     return filter.call(array, function (item) {
    4         return item != null
    5     })
    6 }

    filter

    其中filter是javascript数组的一个新的方法,用以筛选满足条件的数组项

    1 var arr = [5, "element", 10, "the", true];
    2 var result = arr.filter(
    3     function (value) {
    4         return (typeof value === 'string');
    5     }
    6 );
    7 document.write(result);//Output: element, the

    最后返回数组项,我们这里认为其中每个数组项都是一个dom结构

    ② 传入对象

    因为typeof dom也是object,所以zepto这里做了一点扩展

    1 function isObject(obj) {
    2     return type(obj) == "object"
    3 }

    这个代码存在的意义,老夫也不知道了......if里面的代码比较关键:
    如果传入的是对象(比如{}),就将selector拷贝到一个对象,如果是dom结构就直接放入数组
    这里有两个方法需要注意,一个是extend,一个是isPlainObject

    extend

    extend用于为对象扩展对象

     1 function extend(target, source, deep) {
     2     for (key in source)
     3     //如果深度扩展
     4         if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
     5             //如果要扩展的数据是对象且target相对应的key不是对象
     6             if (isPlainObject(source[key]) && !isPlainObject(target[key])) target[key] = {}
     7             //如果要扩展的数据是数组且target相对应的key不是数组
     8             if (isArray(source[key]) && !isArray(target[key])) target[key] = []
     9             extend(target[key], source[key], deep)
    10         } else if (source[key] !== undefined) target[key] = source[key]
    11 }

    $.extend事实上也是调用的上面的方法,我们这里先不管他

     1 $.extend = function (target) {
     2 var deep, args = slice.call(arguments, 1)
     3 if (typeof target == 'boolean') { //当第一个参数为boolean类型的值时,表示是否深度扩展
     4 deep = target
     5 target = args.shift() //target取第二个参数
     6 }
     7 //遍历后面的参数,全部扩展到target上
     8 args.forEach(function (arg) {
     9 extend(target, arg, deep)
    10 })
    11 return target
    12 }

    isPlainObject

    这个方法有所不同,通过字面量定义的对象和new Object的对象返回true,new Object时传参数的返回false(测试对象是否纯粹的对象)

    1 function isPlainObject(obj) {
    2     return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype
    3 }
    4 $.isPlainObject({})// => true
    5 $.isPlainObject(new Object) // => true
    6 $.isPlainObject(new Date) // => false
    7 $.isPlainObject(window) // => false

    至于这个数组到底是不是dom,代码这里先不关注,完了这里也结束了

    ③ 传入html字符串

    如果传入的是html字符串,我们这里就要负责创建dom的工作了,整个这个东西其实比较复杂:

    1 //HTML代码片断的正则
    2 fragmentRE = /^s*<(w+|!)[^>]*>/,

    PS:我正则不行,暂时就不尝试去解释这个了

    字符串=>dom

     1 zepto.fragment = function (html, name, properties) {
     2     //将类似<div class="test"/>替换成<div class="test"></div>,算是一种修复吧
     3     if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
     4     //给name取标签名
     5     if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
     6     //设置容器标签名,如果不是tr,tbody,thead,tfoot,td,th,则容器标签名为div
     7     if (!(name in containers)) name = '*'
     8     var nodes, dom, container = containers[name] //创建容器
     9     container.innerHTML = '' + html //将html代码片断放入容器
    10     //取容器的子节点,这样就直接把字符串转成DOM节点了
    11     dom = $.each(slice.call(container.childNodes), function () {
    12         container.removeChild(this) //逐个删除
    13     })
    14     //如果properties是对象, 则将其当作属性来给添加进来的节点进行设置
    15     if (isPlainObject(properties)) {
    16         nodes = $(dom) //将dom转成zepto对象,为了方便下面调用zepto上的方法
    17         //遍历对象,设置属性
    18         $.each(properties, function (key, value) {
    19             //如果设置的是'val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset',则调用zepto上相对应的方法
    20             if (methodAttributes.indexOf(key) > -1) nodes[key](value)
    21             else nodes.attr(key, value)
    22         })
    23     }
    24     //返回将字符串转成的DOM节点后的数组,比如'<li></li><li></li><li></li>'转成[li,li,li]
    25     return dom
    26 }

    这个方法比较高深,各位慢慢消化,我稍后也再去消化下
    以上完了后,就返回了创建后的dom结构了......

    ④ 上下文

    如果带有上下文,就得使用上下文查找,没有就在document中查询

     1 zepto.qsa = function (element, selector) {
     2     var found
     3     return (isDocument(element) && idSelectorRE.test(selector)) ?
     4             ((found = element.getElementById(RegExp.$1)) ? [found] : []) :
     5             (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
     6             slice.call(
     7                 classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
     8                 tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
     9                 element.querySelectorAll(selector)
    10         )
    11 }

    这个函数与上面函数一样重要,各位下去消化吧

    ⑤ 结束

    最后的最后便返回我们的包装集合了,这里依旧使用zepto.Z(dom, selector)进行封装

    深入zepto.fragment

    待续......

    深入zepto.qsa

    待续......

    结语

    今天的学习暂时到这里,我们下次继续。

  • 相关阅读:
    第四次课堂作业
    12周课后作业
    12周上机(5.21)
    11周周五课后作业
    11周上机作业(5.14)
    第十周(5.7)上机
    第九周4.30上机作业
    第八周周五课后作业
    4.23 第八周上机作业
    4.17课后作业
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/3434046.html
Copyright © 2011-2022 走看看