var select = (function () {
  //这是一个异常与捕获的代码,它表示的意思是:如果push方法出现了错误那么就需要重写push方法
  try {
    //这边是自己模拟一个场景,来使用系统的push方法,如果可以实现的话就证明系统支持push方法
    //这种方法是系统能力检测中的方法功能检测
    var div = document.createElement( 'div' );
    div.innerHTML = '<p></p>';
    var arr = [];
    push.apply( arr, div.getElementsByTagName( 'p' ));
  } catch ( e ) {
    //这边是当try里面的push方法不执行的时候,会进入这里面
    //在这里面将push重新定义了一下,将其变为一个对象,这个对象里面有一个push方法
    var push = {
      //将apply变成了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;
  //这个是为了匹配出 Id 类名 通配符 标签名
  //              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 + '' );
  // 基本方法
  //封装了getElementsByClassName函数,这是为了解决兼容问题
  //传入两个参数,一个是className,另一个是node-->这个node指的是从页面上的node元素开始查找这个className
  function getByClassName ( className, node ) {
    //如果没有传入node的话就给它一个默认值:document
    node = node || document;
    //声明一些变量
    var allElem, res = [], i;
    //首先做判断,如果系统有这个方法会使用系统的
    if ( support.getElementsByClassName ) {
      //直接使用定义的push方法
      return push.apply(res,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 {
      // 自定义实现
      //这边是将rtrim转换成空字符串,即将空格去掉了
      return str.replace( rtrim, '' );
      }
    }
    //这边封装的是indexOf方法,传入三个参数,数组,要搜索的东西,要搜索的东西在数组里面的开始索引(从第几个开始找)
    var myIndexOf = function ( array, search, startIndex ) {
    //首先将索引赋值,如果传入了索引,那么就让开始的索引等于它,如果没有传入那么就让它从零开始找
    startIndex = startIndex || 0;
    //这边还是先判断系统有没有这种方法
    if ( support.indexOf ) {
    //这里表示系统有这种方法,那么就直接使用就OK了,将结果返回
      return array.indexOf( search, startIndex );
    } else {
      //如果没有的话,我们就自己动手封装一个
      //对这个数组进行一个遍历,遍历的初始值就是从startIndex开始
      for ( var i = startIndex; i < array.length; i++ ) {
      //判断一下,如果数组里面有值与要查询的值相等,那么就返回这个索引值
      if ( array[ i ] === search ) {
        return i;
      }
    }
    //当遍历完成之后,如果还是没有找到的话,就返回-1
    return -1;
   }
  }
  //封装一个去重的函数,传入的参数是一个数组
  var unique = function ( array ) {
    //声明一个空数组,用于接收去重之后的元素
    var resArray = [], i = 0;
    //对传入的数组进行一个遍历
    for ( ; i < array.length; i++ ) {
      //做一个判断,如果说resArray里面没有arr里面的元素,则将arr里面的元素放到resArray里面
      //注意,这边使用的是之前封装好的myIndexOf方法
      if ( myIndexOf( resArray, array[ i ] ) == -1 ) {
        //使用的是前面封装好的push方法
      resArray.push( array[ i ] );
    }
  }
    //将这个数组返回
    return resArray;
  }
  //这边封装的是四种基本选择器,ID选择器,类选择器,通配符选择器,标签选择器
  function basicSelect ( selector, node ) {
    //这边的node指的是要在哪一个下面去寻找selector
    node = node || document;
    var m, res;
    if ( m = rbaseselector.exec( selector ) ) {
      if ( m[ 1 ] ) { // id选择器
        res = document.getElementById( m[ 1 ] );
        if ( res ) {//如果res不是一个空的话,进入
          return [ res ];
        } else {//当res为空的话返回一个空数组
          return [];
        }
      } else if ( m[ 2 ] ) { // class选择器
        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 select ( selector, results ) {
    results = results || [];
    var m, temp, selectors, i, subselector;
    //这边是如果传入的selector不是一个字符串的话,那么返回空数组
    if ( typeof selector != 'string' ) return results;
    // 表明参数都没有问题, 接下来就是如何选择
    // 首先判断 qsa 是否可用
    // 然后再 一步步的 自己实现
    if ( support.qsa ) {//如果系统有querySelectorAll方法,那么就使用
    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
    push.apply( results, basicSelect( subselector ) );
    } else {
      //如果匹配不到那么就使用 select2 函数
      select2( subselector, results );
      }
    }
  }
    // 返回 result注意,这个数组要进行一个去重操作
    return unique( results );
  }
  //封装一个后代选择器的函数,传入两个参数
  function select2 ( selector, results ) {
    results = results || [];
    //将这个参数以逗号隔开
    var selectors = selector.split( ' ' );
    //声明一个数组,这个数组用于存放元素,以及一个node数组,这个数组用于存放从哪一个元素开始找
    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 ] ));
      }
    //在结束的时候将arr里面的值全部给node,要注意此时node里面的值的作用是什么
    node = arr;
    //将arr清空
    arr = [];
    }
    //在最后将最后一次获取到的node值赋给results
    //这里面的值是最后一次获取到的元素,也就是说是要获取的子代元素中的的最后一个元素
    push.apply( results, node );
    return results;
    }
    return select;
  })();

仿照jQuery写一个关于选择器的框架(带了注释,请多多指教~)的更多相关文章

  1. 基于JQUERY写的 LISTBOX 选择器

    本文来之于:http://blog.csdn.net/jetsteven/article/details/5104380# 1.经常用到如下图的选择器,而且要支持排序的,所以萌生用JQUERY写一个. ...

  2. 用Python写一个简单的Web框架

    一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...

  3. go server框架学习之路 - 写一个自己的go框架

    go server框架学习之路 - 写一个自己的go框架 用简单的代码实现一个go框架 代码地址: https://github.com/cw731/gcw 1 创建一个简单的框架 代码 packag ...

  4. Summer——从头开始写一个简易的Spring框架

    Summer--从头开始写一个简易的Spring框架                ​ 参考Spring框架实现一个简易类似的Java框架.计划陆续实现IOC.AOP.以及数据访问模块和事务控制模块. ...

  5. 动手写一个简单的Web框架(模板渲染)

    动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...

  6. 动手写一个简单的Web框架(Werkzeug路由问题)

    动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...

  7. 动手写一个简单的Web框架(HelloWorld的实现)

    动手写一个简单的Web框架(HelloWorld的实现) 关于python的wsgi问题可以看这篇博客 我就不具体阐述了,简单来说,wsgi标准需要我们提供一个可以被调用的python程序,可以实函数 ...

  8. 如何使用jQuery写一个jQuery插件

    jQuery插件其实是前端框架的思维,构成一个框架,个人认为必须满足以下几个基础条件:1. 可重用,2. 兼容性,3. 维护方便,虽说现在有很多比较成熟的前端框架,但是也有部分存在配置麻烦,学习成本大 ...

  9. 通过用jQuery写一个页面,我学到了什么

    概述 前几天面试,hr发来一个测试文件,让我做做看.我一看,其实就是根据PSD需求写一个页面,再加上一些互动效果即可. 刚好我之前学了切图,jquery等知识还没练手过,于是高兴的答应了. 最后花了3 ...

随机推荐

  1. 栈和递归的关系 144:Binary Tree Preorder Traversal

    前序遍历:根左右 //用栈来实现非递归解法/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeN ...

  2. poj3417 Network 树上差分+LCA

    题目传送门 题目大意:给出一棵树,再给出m条非树边,先割掉一条树边,再割掉一条非树边,问有几种割法,使图变成两部分. 思路:每一条 非树边会和一部分的树边形成一个环,分三种情况: 对于那些没有形成环的 ...

  3. vim大法

    $Linux vi/vim编辑器常用命令与用法总结 (一)vi/vim是什么?Linux世界几乎所有的配置文件都是以纯文本形式存在的,而在所有的Linux发行版系统上都有vi编辑器,因此利用简单的文字 ...

  4. 用leiningen来运行和打包clojure项目

    今天是2017年5月24日.隔壁team用clojure写了个工具,我们要拿来用,于是弄了个leiningen来尝试.还没有最后成功,先记录一下一些连蒙带猜的步骤,以免忘记. 一.单独运行的cloju ...

  5. Python入门笔记——(2)列表和元组

    一.序列 python包含6种内建的序列:列表.元组.字符串.Unicode字符串.buffer对象和xrange对象.序列中每个元素被分配一个序号即索引,第一个索引为0,第二个为1,以此类推.也可以 ...

  6. C语言实现排序

    //C语言版排序#include<stdio.h> #include<stdlib.h> //冒泡排序 void bubleSort(int data[], int n); / ...

  7. PIE SDK元素的删除

    1功能简介 元素删除是将根据需求将不符合的元素进行删除,PIE SDK支持元素的删除操作,下面对元素的删除功能进行介绍. 2功能实现说明 2.1.1 实现思路及原理说明 第一步 获取已经选择的元素 第 ...

  8. oracle12C--DG 状态集

    一,物理备库 01,状态查询与状态详解 select switchover_status from v$database 02,状态转换到备用数据库 alter database commit to ...

  9. (转)linux下进程的进程最大数、最大线程数、进程打开的文件数和ulimit命令修改硬件资源限制

    ulimit命令查看和更改系统限制 ulimit命令详解 ulimit用于shell启动进程所占用的资源,可以用来设置系统的限制 语法格式 ulimit [-acdfHlmnpsStvw] [size ...

  10. java多态简单例子

    /* 对象的多态性:动物 x = new 猫(); 函数的多态性:函数重载.重写 1.多态的体现 父类的引用指向了自己的子类对象 父类的引用也可以接收自己的对象 2.多态的前提 必须是类与类之间只有关 ...