JavaScript 高级框架设计 (二)

上一篇,JavaScript高级框架设计(一)我们 实现了对tag标签的选择

下来我们实现对id的选择,即id选择器.

我们将上一篇的get命名为getTag(),然后再编写一个getId(),和getClass()

然后在总的get方法中调用,这样做的好处就是模块化,便于维护.

我所有的代码都会托管到github上.

01.js

  1. var getId = function (id, result) {
  2. result = result || [];
  3. // 由于获取的Id是一个元素,所以这里使用call.
  4. result.push.call(result,document.getElementById(id));
  5. return result;
  6. }
  7. var getClass = function (className, result) {
  8. result = result || [];
  9. result.push.apply(result, document.getElementsByClassName(className));
  10. return result;
  11. }

好了,现在分别实现了三个get方法,但是怎么根据选择器表达式来调用者三个方法呢?


下面引出一个关键正则:

  1. /^(?:#([\w-]+)|\.([\w-]+)|([\w]+)|(\*))$/

这段正则目的就是匹配id,类和标签选择器,并且通过exec可以拿到匹配的内容.

那么,

  1. var m = expr.exec(selector);

m[1] 就表示id选择器,m[2]就表示类选择器,m[3]表示标签选择器,m[4]表示通用选择器

下面的代码就顺理成章了.

01.js

  1. var get = function (selector, result) {
  2. result = result || [];
  3. var rquickExpr = /^(?:#([\w-]+)|\.([\w-]+)|([\w]+)|(\*))$/,
  4. m = rquickExpr.exec(selector);
  5. if (!m) {
  6. return result;
  7. }
  8. if (m[1]) {
  9. result = getId(selector);
  10. } else if (m[2]) {
  11. result = getClass(selector);
  12. } else if (m[3]) {
  13. result = getTag(selector);
  14. } else if (m[4]) {
  15. result = getAll(selector);
  16. }
  17. return result;
  18. };

如果没有匹配到,就直接返回result,如果有匹配到,那么就一定是选择器中的一种,通过分组进行判断.

在对每个分组的判断可以优化,

  1. if (m[1]) {
  2. result = getId(m[1], result);
  3. } else if (m[2]) {
  4. result = getClass(m[2], result);
  5. } else {
  6. result = getTag(m[3] || '*', result)
  7. }

现在这样就已经可以完成对单独选择器的匹配了.

我们来进行测试一遍.

01.html

  1. <body>
  2. <div>1</div>
  3. <div id="k">2</div>
  4. <div class="pawn">3</div>
  5. <p></p>
  6. <p></p>
  7. </body>
  8. <script src="01.js"></script>

01.js

  1. onload = function(){
  2. each(get("p"),function(){
  3. this.style.backgroundColor = "green";
  4. })
  5. each(get("#k"),function(){
  6. this.style.backgroundColor = "red";
  7. });
  8. each(get(".pawn"),function () {
  9. this.style.backgroundColor = "pink";
  10. })
  11. }

下面是结果,是不是欧了?

单独的选择器似乎已经完成,但是一个问题出现了? 有考虑过兼容性吗?

document.getElementsByClassName() ie8以下是不兼容的!

下面引入两个问题:

  • 怎么判断兼容性?
  • 怎么解决不兼容?

怎么判断兼容性?

我们通过chrome watch写上document

然后查看getElementsByClassName() 方法.

经过不断在原型链中搜索,

我们发现其嵌套很深,每次调用都在原型链中搜索.

当我们这样的代码在IE8 以下运行这样的代码时,

  1. if(docuemnt.getElementsByClassName(className)){
  2. //
  3. }

它会一直向上寻找,直到Object对象,效率很低.

如果按照上面的代码,那么每调用一次就要就行一次能力检测,效率肯定太低了.

怎么办呢?

方法就是只做一次能力检测,并把它记录下来.

其中,jQuery中就是这么做的,它拥有一个专门的功能检测模块,$.support,里面的属性包含存在兼容问题的方法或属性,并用布尔值来记录.

所以在我们这里,在全局作用域中(最终都要用成沙箱或者闭包)提供一个support对象,里面提供所有的以方法名相同的属性,值均为布尔值,在浏览器加载js的开始的时候,就进行能力判断,凡是涉及到能力检测的时候就直接检测support即可.

所以我们在代码开头加入

  1. var support = {};
  2. support.getElementsByClassName = !!document.getElementsByClassName;

那么以后再需要能力检测的时候,可以这么写:

  1. if(support.getElementsByClassName){
  2. //...
  3. }

似乎已经可以了.

但是,有一种攻击叫做注入漏洞攻击,我在文件开头嵌入这样的代码(chrome是允许内嵌js代码的!!!):

  1. document.getElementsByClassName = 'pawn';

那么上面的检测还有用吗?

看看jQuery怎么做的,不仅要判断它是否存在,还要判断其能力是否符合要求.

看下面的代码

  1. var support = {};
  2. support.getElementsByClassName = function () {
  3. if (!document.getElementsByClassName) {
  4. return false;
  5. }
  6. var div = document.createElement('div'),
  7. pWithClass = document.createElement('p');
  8. pWithClass.className = 'pawn-pawn-pawn';
  9. div.appendChild(pWithClass);
  10. var res = div.getElementsByClassName('pawn-pawn-pawn');
  11. return res[0] === pWithClass;
  12. } ();

然后我们在chrome控制台输入

  1. support.getElementsByClassName

在ie8里,

可以看到,这样很有用.

现在查看jQuery源代码

看它的方法,实在是太简洁了,jQuery留给我们的,就是敬仰!!

现在检查已经完了,怎么解决不兼容问题呢?


解决兼容

怎么解决呢? 首先进行能力检测,如果有,就直接调用,如果没有,实现了一个自己的myGetClassName()方法.

原理很简单,通过通用选择器查找到所有的元素,然后看它有木有这个类名,如果有就push.

05.js

  1. var getClass = function (className, result) {
  2. result = result || [];
  3. // 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
  4. var res;
  5. if (support.getElementsByClassName) {
  6. res = document.getElementsByClassName(className);
  7. } else {
  8. // 自己实现getElementByClassName
  9. // 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
  10. res = myGetByClassName(className.document);
  11. }
  12. result.push.apply(result,res);
  13. return result;
  14. }

  1. var myGetByClassName = function (className, context) {
  2. var elements = context.getElementsByTagName("*"),
  3. res = [];
  4. // 循环判断是否符合要求
  5. each(elements, function () {
  6. // 这个细节非常重要 !!!!!
  7. if ((" " + this.className + " ").indexOf(" " + className + " ") != -1) {
  8. res.push(this);
  9. }
  10. })
  11. return res;
  12. }

这个细节非常重要!!

  1. if ((" " + this.className + " ").indexOf(" " + className + " ") != -1) {
  2. res.push(this);
  3. }

好了,我们已经添加了自己的getClassName() 方法,现在来测试一遍.

05.html

  1. <body>
  2. <div class="dv1">1</div>
  3. <div class="dv1">2</div>
  4. <div>3</div>
  5. <p clss='p1'>4</p>
  6. <p clss='p1'>5</p>
  7. <p>6</p>
  8. <script src="05.js"></script>
  9. </body>
  10. <script>
  11. onload = function () {
  12. each(get('dv1'), function () {
  13. this.style.backgroundColor = "red";
  14. });
  15. each(get('p1'), function () {
  16. this.style.backgroundColor = "green";
  17. });
  18. };
  19. </script>

chrome下效果:

ie8下效果:

关于getElementsByClassName()的兼容问题就讨论到这里,在下一篇,JavaScript高级框架设计(三),我会开始介绍push的兼容问题和 组合选择器.

最后特别感谢恩师蒋坤老师(jk)对我的知识学习和人生引导的极大帮助,非常感谢他.

JavaScript 框架设计(二)的更多相关文章

  1. JavaScript框架设计(三) push兼容性和选择器上下文

    JavaScript框架设计(三) push兼容性和选择器上下文 博主很久没有更博了. 在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTag和getClass ...

  2. 偶的《javascript框架设计》终于出版

    #cnblogs_post_body p{ text-indent:2em!important; } 历时两年多,我的书终于付梓出版了.应各方面的要求,写软文一篇,隆重介绍一下此书对各位程序员的钱途有 ...

  3. JS读书心得:《JavaScript框架设计》——第12章 异步处理

    一.何为异步   执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上 ...

  4. JavaScript框架设计(四) 字符串选择器(选择器模块结束)

    JavaScript框架设计(四) 字符串选择器(选择器模块结束) 经过前面JavaScript框架设计(三) push兼容性和选择器上下文的铺垫,实现了在某一元素下寻找,现在终于进入了字符串选择器 ...

  5. 游戏UI框架设计(二) : 最简版本设计

    游戏UI框架设计(二) --最简版本设计 为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念.这里先定义三个核心功能: 1:UI窗体的自动加载功能. 2:缓存UI窗体. 3:窗体生命周期(状 ...

  6. Javascript框架设计思路图

    这个系列的随笔都是关于Javascript框架设计一书的读书笔记(作者是司徒正美),不是本人原创!!! 一.简介: 1.市面上主流的JS框架,大多数是由一个个模块组合而成,模块化是大多数让软件所遵循的 ...

  7. JavaScript 框架设计

    JavaScript 高级框架设计 在现在,jQuery等框架已经非常完美,以致于常常忽略了JavaScript原生开发,但是这是非常重要的. 所以,我打算写一个简单的框架,两个目的 熟练框架的思想 ...

  8. WisDom.Net 框架设计(二) 服务总线

    WisDom.Net 框架设计--服务总线 1.Soa 简介     soa 就是面向服务的体系结构 是一个组件模型,不同的组件之间通过定义良好的接口联系起来.就像盖房子一块砖头一块砖头的砌墙,一片一 ...

  9. 浅谈JavaScript框架设计

    在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助. 一个框架应该包含哪些内容? 1.语言扩展 大部分现有的框架都提供了这部分内容,语言扩展应当是以ECMAScrip ...

随机推荐

  1. Android app应用多语言切换功能实现

    最近在做一个多语言切换的功能,类似于微信的语言切换,搜了下资料基本上都是以下这种: 1. 实现的效果 和微信类似,在设置界面打开切换语言的界面,选择语言后重启 HomeActivity,语言切换完成, ...

  2. css屏蔽元素的鼠标事件pointer-events

    // 屏蔽点击 $('body').css('pointer-events', 'none'); //恢复默认 $('body').css('pointer-events', 'auto');   用 ...

  3. if [ "$变量1"x = "$变量2"x ]中x的含义

    问题:if [ "$变量1"x = "$变量2"x ]中x的含义是? 答:“x”字符可以为任意字符,用于防止变量为空时,某些版本的bash中会产生错误: 在一个 ...

  4. mysql 数据表中查找重复记录

    select mobile_phone,count(*) as count from lawyer group by mobile_phone having count>1;

  5. ThinkPHP5 助手函数

    对于ThinkPHP5.0以前的版本,助手函数全部是单字母函数,但到ThinkPHP5之后,使用如下函数来代替单字母函数: 最常用: /** * 实例化Model * @param string $n ...

  6. 使用html5 地理位置技术 和 百度地图api查询当前位置

    使用了  zepto  和 requirejs define(['zepto'],function($){ var geolocation = { init:function(config,onSuc ...

  7. MySQL KEY分区

    200 ? "200px" : this.width)!important;} --> 介绍 KEY分区和HASH分区相似,但是KEY分区支持除text和BLOB之外的所有数 ...

  8. Spark的持久化简记

    摘要: 1.spark 提供的持久化方法 2.Spark的持久化级别 3.如何选择一种最合适的持久化策略 内容: 1.spark 提供的持久化方法 如果要对一个RDD进行持久化,只要对这个RDD调用c ...

  9. CentOS6编译安装PHP7+Nginx

    本文属于动手搭建PHP开发环境的一部分,更多点击链接查看. 本文以centos6为例. 安装PHP 下载 http://cn2.php.net/distributions/php-5.6.22.tar ...

  10. VS web项目 基于IIS调试和模拟域名调试

    1.安装IIS 2.注册.net framework 到IIS 打开程序-运行-cmd:输入一下命令重新注册IISC:\WINDOWS\Microsoft.NET\Framework\v4.0.303 ...