jq框架封装学习笔记3-封装select引擎

parseHtml

在 jq 中有有一个创建 DOM 对象的方法

$( ‘

’ )[ 0 ] 或者 .get( 0 )

$( ‘#d’ )

$( ‘html’ )

$( dom )

( obj )

$( func )

function $ ( arg ) {
    if ( arg 是 选择器 ) {

    } else if ( arg 是函数 ) {

    } else if ( arg 是 DOM 对象 ) {

    } ...
    ...
}

如何实现 parseHTML 函数

  1. 方法的定义( 参数与返回值 )

    参数: 字符串

    返回: DOM 数组

  2. 有两种方法

    第一种方法使用 createElement

    第二种方法使用 innerHTML 技巧

两种方法虽然都可以但是在不同浏览器上性能是有区别的, chrome差不多,但是 IE 中第一种方法相对时间消耗会长一些,所以权衡之下,还是用第二种方法 innerHTML 比较好

性能测试:

第一种方法使用 createElement

    // 在 js 中有一个对象 Date 它表示时间,
    // 使用 + new Date() 可以得到当前的毫秒数
    var start = +new Date;

    // 第 1 种方法
    // 使用 documentfragment 的对象 来表示内存节点
    var dfg = document.createDocumentFragment();

    for ( var i = 0; i < 10000; i++ ) {
        var div = document.createElement( 'div' );
        var txt = document.createTextNode( 'Hello JavaScript - ' + i );

        div.style.border = '1px dashed red';
        div.style.margin = '5px';
        div.style.padding = '5px';
        div.style.backgroundColor = 'pink';

        div.appendChild( txt );

        dfg.appendChild( div );
    }

    document.body.appendChild( dfg );
    var end = +new Date;
    console.log( end - start );

第二种方法使用 innerHTML 技巧

    var start = +new Date;
    var str = [];
    for ( var i = 0; i < 10000; i++ ) {
        str.push('<div style="border:1px dashed red; background-color: pink; margin: 5px; padding: 5px;>Hello JavaScript - ' + i + '</div>');
    }
    document.body.innerHTML = str.join( '' );
    var end = +new Date;
    console.log( end - start );

实现 parseHTML

    function parseHTML( html ) {
        // 1, 需要一个容器
        var div = document.createElement( 'div' );
        // 2, 将字符串赋值给该容器的 innerHTML 属性
        div.innerHTML = html;
        // 3, 遍历 其 childeNodes
        var res = [];
        for ( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] );
        }
        // 4, 返回元素数组
        return res;
    }

    var list = parseHTML( '<div> 你好, 我是一个呗创建出来的 div </div><p>p</p>' )
    document.body.appendChild( list[ 0 ] );

框架结构的搭建

抽象框架的雏形

给数组原型添加appendTo

    function $( html ) {
        // 创建一个 div
        var div = document.createElement( 'div' );
        // 把字符串填充进来
        div.innerHTML = html;

        var res = []; // 声明数组保存返回的子节点
        for( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] ); // 遍历将子节点添加进去
        }

        // 添加方法
        res.appendTo = function( dom ) {
            for ( var i = 0; i < this.length; i++ ) {
                dom.appendChild( this[ i ] );
            }
        }
        return res; // 字符串转化成 res
    }

    // 需要数组提供 appendTo 方法, 给数组原型提供该方法. 至少给 res 提供
    // 参数是一个 DOM 对象, 要求将数组中存储的每一个 DOM 对象加到它上面去

    $( '<div style="border: 1px dashed red; background-color: pink;">123</div>' ).appendTo( document.body );

不应该给数组提供新的成员

这样无法实现复用

实际上应该是有一个构造函数,创建一个对象,该对象应该是一个伪数组,而由于有构造函数,就有原型,所有的方法应该加入到 原型中

    function $( html ) {
        return new F( html );
    }
    function F( html ) {
        // dom 数组 存到了 实例伪数组 中
        push.apply( this, parseHTML( html ) );
    }
    F.prototype = {
        length: 0,  // ??
        appendTo: function( dom ) {}
    }

基本抽象结构

    function parseHTML ( html ) {
        var div = document.createElement( 'div' );
        div.innerHTML = html;
        var res = [];
        for ( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] );
        }
        return res;
    }

    function $ ( html ) {
        return new F( html );
    }

    function F( html ) {
        console.log( this );
        [].push.apply( this, parseHTML( html ) );
    }
    F.prototype = {
        constructor: F,
        appendTo: function ( dom ) {
            for ( var i = 0; i < this.length; i++ ) {
                dom.appendChild( this[ i ] );
            }

        }
    }

    var $1 = $( '<div>123</div><div>123</div><div>123</div>' );
    var $2 = $( '<div>123</div><div>123</div>' );

    $1.appendTo( document.body );
    $2.appendTo( document.body );

jq 中 封装的更加完整

    function jepson(...) {
        new F(...)
    }
    将所有的功能都放到了 F 的 prototype 中,
    那么如果需要对框架进行扩展, 需要引入两个名字到 外面
    一个是 jepson, 一个是 F
    增加新功能就是
    F.prototype.新方法 = function() {...}

    jq 将 F 替换为 jepson 原型中的一个方法
    function jepson() {
        return new jepson.prototype.init(...);
    }
    jepson.prototype = {
        init: function( ... ) {  ... }
    }

    // 新问题出现了
    // 如何为其扩展
    // 在 jq 中多加一个方法是利用 给 jepson.prototype 添加方法来实现的
    // 问题: 现在给 jepson.prototype 添加方法, 可以让当前对象使用该方法吗 ?
    // 对象是被 jepson.prototype.init 所创建的, 因此只需
    // 要让 jepson.prototype.init.prototype 与 jepson.prototype 指向相同即可

有那么一点像了↓

    function parseHTML ( html ) {
        var div = document.createElement( 'div' );
        div.innerHTML = html;
        var res = [];
        for ( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] );
        }
        return res;
    }
    // 暴露出去的函数
    function jepson ( html ) {
        // 通过 原型方法 init 方法来构造
        return new jepson.prototype.init( html );
    }

    jepson.prototype = {
        constructor: jepson,
        length: 0,
        init: function ( html ) {
            [].push.apply( this, parseHTML( html ) );
        },
        appendTo: function ( dom ) {
            for ( var i = 0; i < this.length; i++ ) {
                dom.appendChild( this[ i ] );
            }

        }
    }
    // 构造出来的对象,修改了原型链
    jepson.prototype.init.prototype = jepson.prototype;

    // 使用
    var i = jepson( '<div>123</div>' );
    i.appendTo( document.body );

jepson.prototype 取个别名,fn 方便的使用.代码压缩

    function parseHTML ( html ) {
        var div = document.createElement( 'div' );
        div.innerHTML = html;
        var res = [];
        for ( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] );
        }
        return res;
    }

    function jepson ( html ) {
        return new jepson.prototype.init( html );
    }

    jepson.fn = jepson.prototype = {
        constructor: jepson,
        length: 0,
        init: function ( html ) {
            [].push.apply( this, parseHTML( html ) );
        },
        appendTo: function ( dom ) {
            for ( var i = 0; i < this.length; i++ ) {
                dom.appendChild( this[ i ] );
            }

        }
    }
    jepson.fn.init.prototype = jepson.prototype;

封装优化

    (function ( window, undefined ) {

    function parseHTML ( html ) {
        var div = document.createElement( 'div' );
        div.innerHTML = html;
        var res = [];
        for ( var i = 0; i < div.childNodes.length; i++ ) {
            res.push( div.childNodes[ i ] );
        }
        return res;
    }

    function jepson ( html ) {
        return new jepson.prototype.init( html );
    }

    jepson.fn = jepson.prototype = {
        constructor: jepson,
        length: 0,
        init: function ( html ) {
            [].push.apply( this, parseHTML( html ) );
        },
        appendTo: function ( dom ) {
            for ( var i = 0; i < this.length; i++ ) {
                dom.appendChild( this[ i ] );
            }

        }
    }
    jepson.fn.init.prototype = jepson.prototype;
    window.jepson = window.J = jepson;

    })( window );

整合选择器与框架

改良 init 函数,调整顺序, 添加混入

    (function ( window, undefined ) {
    var push = [].push;
    function itcast ( html ) {
        return new itcast.fn.init( html );
    }
    itcast.fn = itcast.prototype = {
        constructor: itcast,
        length: 0,
        init: function ( html ) {
            // [].push.apply( this, parseHTML( html ) );
            if ( html == null || html === '' ) {
                return;
            }
            if ( typeof html === 'function' ) {
            }
            if ( itcast.isString( html ) ) {
                if ( /^</.test( html ) ) {
                    push.apply( this, parseHTML( html ) );
                } else {
                    // 选择器
                    // select( html );
                    push.apply( this, itcast.select( html ) );
                }

            }
        }
    }
    itcast.fn.init.prototype = itcast.fn;


    // 添加可扩展的方法 extend
    itcast.extend = itcast.fn.extend = function ( obj ) {
      for ( var k in obj ) {
        // if ( k 是 obj 自己提供的方法 ) obj.hasOwnProperty( k )
        this[ k ] = obj[ k ];
      }
    }
    // select 引擎专门用于搜素元素
    var select =
    (function () {

    var push = [].push;

    // 如果出现了错误那么就需要重写 push
    try {
      var div = document.createElement( 'div' );
      div.innerHTML = '<p></p>';
      var arr = [];
      push.apply( arr, div.getElementsByTagName( 'p' ));
    } catch ( e ) {

      push = {
        apply: function ( array1, array2 ) {
          for ( var i = 0; i < array2.length; i++ ) {
            array1[ array1.length++ ] = array2[ i ];
          }
        }
      };
    }

    // 正则表达式
    var rnative = /\{\s*\[native/;
    var rtrim = /^\s+|\s+$/g;
    //                          1           2         3     4
    var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/;

    // 基本函数, support 对象, 验证 qsa 与 byclass
    var support = {};

    support.qsa = rnative.test( document.querySelectorAll + '' );
    support.getElementsByClassName =
        rnative.test( document.getElementsByClassName + '' );
    support.trim = rnative.test( String.prototype.trim + '' );
    support.indexOf = rnative.test( Array.prototype.indexOf + '' );

    // 基本方法
    function getByClassName ( className, node ) {
      node = node || document;
      var allElem, res = [], i;

      if ( support.getElementsByClassName ) {
        return node.getElementsByClassName( className );
      } else {
        allElem = node.getElementsByTagName( '*' );
        for ( i = 0; i < allElem.length; i++ ) {
          if ( allElem[ i ].className === className ) {
            res.push( allElem[ i ] );
          }
        }
        return res;
      }
    }

    // 自定义实现 trim 方法
    var myTrim = function ( str ) {
        // 表示两端去空格, 然后返回去除空格的结果
        if ( support.trim ) {
            return str.trim();
        } else {
            // 自定义实现
            return str.replace( rtrim, '' );
        }
    }

    var myIndexOf = function ( array, search, startIndex ) {
      startIndex = startIndex || 0;
      if ( support.indexOf ) {
        return array.indexOf( search, startIndex );
      } else {
        for ( var i = startIndex; i < array.length; i++ ) {
          if ( array[ i ] === search ) {
            return i;
          }
        }
        return -1;
      }
    }


    var unique = function ( array ) {
      var resArray = [], i = 0;
      for ( ; i < array.length; i++ ) {
        if ( myIndexOf( resArray, array[ i ] ) == -1 ) {
          resArray.push( array[ i ] );
        }
      }
      return resArray;
    }

    function basicSelect ( selector, node ) {
      node = node || document;
      var m, res;
      if ( m = rbaseselector.exec( selector ) ) {
        if ( m[ 1 ] ) { // id
          res = document.getElementById( m[ 1 ] );
          if ( res ) {
            return [ res ];
          } else {
            return [];
          }
        } else if ( m[ 2 ] ) {  // class
          // return node.getElementsByClassName( m[ 2 ] );
          return getByClassName( m[ 2 ], node );
        } else if ( m[ 3 ] ) {
          return node.getElementsByTagName( m[ 3 ] );
        } else if ( m[ 4 ] ) {
          return node.getElementsByTagName( m[ 4 ] );
        }
      }
      return [];
    }

    function select2 ( selector, results ) {
      results = results || [];
      var selectors = selector.split( ' ' );
      // 假定 'div p .c'
      var arr = [], node = [ document ];
      for ( var j = 0; j < selectors.length; j++ ) {
        for ( var i = 0; i < node.length; i++ ) {
          push.apply( arr, basicSelect( selectors[ j ], node[ i ] ));
        }
        node = arr;
        arr = [];
      }
      push.apply( results, node );
      return results;
    }

    function select ( selector, results ) {
        results = results || [];
      var m, temp, selectors, i, subselector;

      if ( typeof selector != 'string' ) return results;

      // 表明参数都没有问题, 接下来就是如何选择
      // 首先判断 qsa 是否可用
      // 然后再 一步步的 自己实现
      if ( support.qsa ) {
        push.apply( results, document.querySelectorAll( selector ) );
      } else {
        // 不存在再来考虑自己实现
        selectors = selector.split( ',' );
        // 循环
        for ( i = 0; i < selectors.length; i++ ) {
          subselector = myTrim( selectors[ i ] );
          // 接下来就是 处理 subselector
          if ( rbaseselector.test( subselector ) ) {
            // 基本选择器
            push.apply( results, basicSelect( subselector ) );
          } else {
            // select2 函数
            select2( subselector, results );
          }
        }
      }
      // 返回 result
      return unique( results );
    }
    return select;
    })();

    itcast.select = select;   // 在需要的时候 可以使用第三方的 选择器引擎

    // 需要一些判断的方法
    itcast.extend({
      isString: function ( data ) {
        return typeof data === 'string';
      }
    });


    // DOM 操作的方法
    // 将字符串转换为 DOM 对象的函数
    var parseHTML = (function () {
        var div = document.createElement( 'div' );
        function parseHTML ( html ) {
            div.innerHTML = html;
            var res = [];
            for ( var i = 0; i < div.childNodes.length; i++ ) {
                res.push( div.childNodes[ i ] );
            }
            div.innerHTML = '';
            return res;
        }
        return parseHTML;
    })();

    itcast.fn.extend({
      appendTo: function ( dom ) {
        for ( var i = 0; i < this.length; i++ ) {
          dom.appendChild( this[ i ] );
        }
      }
    });

    window.itcast = window.I = itcast;

    })( window )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值