zoukankan      html  css  js  c++  java
  • zepto源码研究

    index:

    $.fn = {
    ......

    indexOf: emptyArray.indexOf,
    }
    index: function(element){
          //这里的$(element)[0]是为了将字符串转成node,因为this是个包含node的数组
          //当不指定element时,取集合中第一条记录在其父节点的位置
          //this.parent().children().indexOf(this[0])这句很巧妙,和取第一记录的parent().children().indexOf(this)相同
          return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
        },

    obj.index(element) : 如果element有,则obj作为一个数组,取element在这个obj中的索引。如果element没有,则此函数的意思是取dom节点所在父类中的索引。

    <div id="first" class="test">
        <div id="second" class="test">
        </div>
    </div>
    
    测试:
    
    $(".test").index($("#second"));
    1
    $(".test").index($("#second")[0]);
    1
    $(".second").index();
    -1
    $("#second").index();
    0

    hasClass:

    /**
         * 是否含有指定的类样式
         * @param name
         * @returns {boolean}
         */
        hasClass: function(name){
          if (!name) return false
          //some ES5的新方法 有一个匹配,即返回true 。
          return emptyArray.some.call(this, function(el){
            //this是classRE(name)生成的正则
            return this.test(className(el))
          }, classRE(name))
        },

    array.some(function(curentValue,index,arr){},thisValue),thisValue作为回调函数中的this,若没有指定thisValue,则回调函数中的this为undefined,

    className(el) :一般取el.className(svg除外),classRE(name) 生成一个name的正则表达式,

    test是RegExp的方法,参数是字符串,返回值是boolean类型。
    match是String的方法,参数是正则表达式,返回值是数组。

    classRE:

    /**
       * 将参数变为正则表达式
       * @param name
       * @returns {*}
       */
      function classRE(name) {
        //classCache,缓存正则
        //TODO 缓存可以理解,但应该在重复使用第二次时再缓存吧,直接缓存?
        return name in classCache ?
            classCache[name] : (classCache[name] = new RegExp('(^|\s)' + name + '(\s|$)'))
      }

    className:

    /**
       * 对SVGAnimatedString的兼容?
       * @param node
       * @param value
       * @returns {*}
       */
      function className(node, value){
        var klass = node.className || '',
            svg   = klass && klass.baseVal !== undefined
    
        if (value === undefined) return svg ? klass.baseVal : klass
        svg ? (klass.baseVal = value) : (node.className = value) //class设值
      }

    addClass:

       /**
         * 增加一个或多个类名
         * @param name  类名/空格分隔的类名/函数
         * @returns {*}
         */
        addClass: function(name){
          if (!name) return this
    
          //遍历增加
          return this.each(function(idx){
            //已存在,返回   
            if (!('className' in this)) return
            classList = []
            var cls = className(this), newName = funcArg(this, name, idx, cls)  //修正类名,处理name是函数,SVG动画兼容的情况
    
            //多个类,空格分隔为数组
            newName.split(/s+/g).forEach(function(klass){
              if (!$(this).hasClass(klass)) classList.push(klass)
            }, this)
    
            //设值  如果所有的都是重复的,就不会执行
            classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
          })
        },

    addClass(name)的name参数可以为函数,newName.split(/s+/g) :根据空格分为数组,这是一个技巧,这里是先对newName过滤,把重复的class去掉,

    然后以字符串的形式连接原来的className并赋值给dom

    removeClass:

    /**
         *删除一个或多个类名 同addClass
         * 原理: className.repalce 替换撒谎年初
         * @param name 类名/空格分隔的类名/函数
         * @returns {*}
         */
        removeClass: function(name){
          return this.each(function(idx){
            if (!('className' in this)) return
            if (name === undefined) return className(this, '')
            classList = className(this)
            funcArg(this, name, idx, classList).split(/s+/g).forEach(function(klass){
              //替换删除
              classList = classList.replace(classRE(klass), " ")
            })
            className(this, classList.trim())
          })
        },

    这里要删除一个字符串里面的指定的子字符串,可以使用replace(classRE," ")

    toggleClass:

        /**
         * 切换类的添加或移除
         * 原理 如果存在,即removeClass移除,不存在,即addClass添加
         * @param name   类名/空格分隔的类名/函数
         * @param when
         * @returns {*}
         */
        toggleClass: function(name, when){
          if (!name) return this
          return this.each(function(idx){
    var $this = $(this), names = funcArg(this, name, idx, className(this))
    names.split(
    /s+/g).forEach(function(klass){
          // 这里的when可以作为标记,来判断是add还是remove (when
    === undefined ? !$this.hasClass(klass) : when) ? $this.addClass(klass) : $this.removeClass(klass) })
    }) },

    attr:

    /**
       * 设置属性
       * @param node
       * @param name
       * @param value
       */
      function setAttribute(node, name, value) {
        //value为null/undefined,处理成删除,否则设值
        value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
      }
    
    /**
         * 元素的HTML属性读写
         * 读:原理是getAttribute
         * 写:原理是setAttribute
         * @param name
         * @param value
         * @returns {undefined}
         */
        attr: function(name, value){
          var result;
          //仅有name,且为字符串时,表示读
          return (typeof name == 'string' && !(1 in arguments)) ?
              //$是空的 或里面的元素非元素,返回undefined
              (!this.length || this[0].nodeType !== 1 ? undefined :
                      //直接用getAttribute(name)读,
                      (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
              ) :  //否则是写,不管name为对象{k:v},或name value 都存在
              this.each(function(idx){
                if (this.nodeType !== 1) return   //非元素
                //如果name为对象,批量设置属性
                if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
                //处理value为函数/null/undefined的情况
                else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
              })
        },

    经整理后如下:

    //如果只有一个参数,且name为string类型,则此函数为读模式
            if((typeof name == 'string' && !(1 in arguments)) ){
                if(!this.length || this[0].nodeType !== 1) return undefined;
    
                if((result = this[0].getAttribute(name))){
                    // 如果dom有这个属性,则返回
                    return result;
                }else{
                    // 如果dom没有这个属性,则从内存中获取并返回
                    if(name in this[0]) return this[0][name];
                }
            }else{
                this.each(function(idx){
                    if (this.nodeType !== 1) return   //非元素
                    //如果name为对象,批量设置属性
                    if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
                    //处理value为函数/null/undefined的情况,这里的funcArg函数在整个zepto中复用了很多次,
                    // 考虑参数为函数的情况在很大方法中出现,setAttribute处理value为null的情况,实际就是removeAttribute
                    else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
                })
            }

    data:

    /**
         * 设置自定义数据
         * 注意与jQuery的区别,jQuery可以读写任何数据类型。这里原理是H5的data-,或直接setAttribute/getAttribute,只能读写字符串
         * @param name
         * @param value
         * @returns {*}
         */
        /*getAttribute/setAttribute可以操作所有的dataset内容,dataset内容只是attribute的一个子集,
        特殊就特殊在命名上了。那么为什么我们还要用data-*呢,
        一个最大的好处是我们可以把所有自定义属性在dataset对象中统一管理,遍历啊神马的都哦很方便,
        而不至于零零散散了,所以用用还是不错的。*/
        data: function(name, value){
          var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase()
    
          var data = (1 in arguments) ?
              this.attr(attrName, value) :
              this.attr(attrName)
    
          return data !== null ? deserializeValue(data) : undefined
        },

    函数中的第一步是将驼峰命名的name转化为带“ - ”的小写命名:

    var name = "aTcF";
    name = name.replace(/([A-Z])/g, '-$1').toLowerCase();
    console.log(name);
    结果: a-tc-f

    这里有一个不太懂的地方,就是如果value为对象的话,这里直接使用attr(attrName,value),最终属性值为"[object Object]",取出后直接JONS.parse会出错

    所以我觉得attr里面的value应该再校验一下是否为对象,若是,则value = JSON.stringify(value);

    这里存取自定义属性,可以用dateset 并兼容attr,例子如下:

    function data(name, value){
                var attrName = name.replace(capitalRE, '-$1').toLowerCase();
                if(isObject(value)) value = JSON.stringify(value);
                if(this[0].dataset){
                    var data = (1 in arguments) ?
                            this[0].dataset[attrName] = value :
                            this[0].dataset[attrName];
                }else{
                    attrName = 'data-'+attrName;
                    var data = (1 in arguments) ?
                            this.attr(attrName, value) :
                            this.attr(attrName)
                }
    
                return data !== null ? deserializeValue(data) : undefined
            }

    val:

    /**
         * 适合表单元素读写
         * 写: 写入每个元素   element.value
         * 读: 读第一个元素
         * @param value  值/函数
         * @returns {*}
         */
        val: function(value){
          return 0 in arguments ?
              //只有一个参数是写,
              this.each(function(idx){
                this.value = funcArg(this, value, idx, this.value)
              }) :
              //如果是读
              (this[0] && (this[0].multiple ?    //对多选的select的兼容处理,返回一个包含被选中的option的值的数组
                      $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') :
                      this[0].value)
              )
        },

    这里的this[0].multiple 可以判断是否为多选的select,select节点的value属性与被选中的option的value属性是相关联的。如下例子

    select#third选中的option的value为1
    console.log($("#third")[0].value);
    结果:1
    
    console.log($("#third")[0].value = 2);
    select#third选中的option的value变为2
    结果:2



     


  • 相关阅读:
    构造方法中使用this的含义
    Android Bundle类
    Android中使用PULL方式解析XML文件
    Android 创建与解析XML(四)—— Pull方式
    File的getPath()和getAbsolutePath()和getCanonicalPath()的区别
    Android-取出SDcard卡下指定后缀名的文件
    page、request、session和application有什么区别?
    prepareStatement的用法和解释
    pageContext对象的用法
    使用JSP连接MySql数据库读取HTML表单数据进行存贮
  • 原文地址:https://www.cnblogs.com/zhutao/p/5662610.html
Copyright © 2011-2022 走看看