Sizzle源码分析(二) 工具方法

506 阅读3分钟

02.jpeg

简介

sizzle在使用的时候会用一些工具的方法,比如检查一个css标识符是否合规。下面的一些知识还涉及到了关于字符集的知识可以看我的另一篇博客。看了以后我写的注释大家应该一看就会明白。

funescape

var whitespace = "[\\x20\\t\\r\\n\\f]",
runescape = new RegExp("\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g")
// 分析一下这个正则 匹配\数字加字母1到6个匹配空白0 到多个,或者不是空白开头的
// ----> /\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\([^\r\n\f])/g

runescape这个正则表达式是匹配一个转义的16进制的数字 稍微解析一下"\\" ,假如我们现在要匹配一个16进制的字符串“\\26EE”(10进制23356),匹配后的结果是[\26EE],U+26EE在unicode 对应的字符是"⛮" 。

  • 在CSS 2.1中,反斜杠(\)字符可以表示三种类型的字符转义符之一。
    • 它取消了特殊CSS字符的含义。任何字符(十六进制数字,换行符,回车符或换页符除外)都可以使用反斜杠转义以删除其特殊含义 例如,“ \”“是一个由双引号引起来的字符串。样式表预处理器不得从样式表中删除这些反斜杠,因为这会改变样式表的含义。
    • 在CSS注释中,反斜杠代表其自身
    • 反斜杠转义符使作者可以引用他们不容易放入文档中的字符。
  • 注意:反斜杠转义符始终被认为是标识符或字符串的一部分(即,“ \ 7B”不是标点符号,即使“ {”是允许的,并且在类名的开头也允许“ \ 32”,即使尽管“ 2”不是)。

这里有个小知识点想给大家说一下,Ecmascript 标准规定JavaScript语言基于Unicode标准进行开发,JavaScript内核完全采用UCS字符集进行编写,因此在JavaScript代码中每个字符都使用两个字节来表示。 觉得这里涉及到了字符集的编码的知识,不懂的同学建议进行学习一下。在我另一篇http知识点总结的博客中我也写了一点关于字符集的知识点,这个api可以参考mdn来进行学习呢

/**
 * [funescape description]
 * @param  {[String]} escape [类16进制的字符串]传入一个需要转义的16进制的字符串
 * @param  {[Boolean]} nonHex  是否是16进制
 * @return {[String]}        [ UTF-16 代码单元序列创建的字符串] 返回对应的Unicode字符
 */
funescape = function (escape, nonHex) {
// 这里看不懂的可以看我的另一篇文章字符集知识。
   var high = "0x" + escape.slice(1) - 0x10000;
   return nonHex ?  nonHex :
        high < 0 ?
        // 如果小于0说明是BMP平面
	String.fromCharCode(high + 0x10000) :
        //如果大于0 说明是其他辅助平面双字节
        //高比特位是向右移动10位剩下的就高位字节,然后or 0xD800 相当于 + ,然后就求出了高位字节了。
        //低比特位就是high&(0x3FF,1111 1111 11),0x3FF高位全是0所以低位&就能求出低位比特位,然后 + 加上0xDC00就求出了低位字节了
	String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
},

fcssescape

//0-1f之间都是控制字符
//1f-7f之间ascii的字符
//7f-后跟数字控制字符或者其他字符 
//uFFFF最大码位 在一个平面内
rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
[\0-\x1f\x7f] 匹配控制字符捕获 或者匹配-开头数字一个 或者匹配 “-”开始“-”结束的字符
[^\0-\x1f\x7f-\uFFFF\w-]单独分析:不是\0-\x1f 、\x7f-\uFFFF、\w-任意一个字符
https://condor.depaul.edu/sjost/lsp121/documents/ascii-npr.htm
/**
 * [fcssescape description]
 * @param  {[String]} ch rcssescape对应匹配字符
 * @param  {[Boolean]} asCodePoint  是否是转为码点,对应传入正则匹配的捕获的位置
 * @return {[String]}  对应ch的码位的16进制 如果是“-”则返回\\-
 */
fcssescape = function (ch, asCodePoint) {
   if (asCodePoint) {
        // 用unitcde代替
        // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
        if (ch === "\0") {
           return "\uFFFD";
        }
        控制字符 和数字(取决于位置)作为代码点转义
        return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
   }
  // Other potentially-special ASCII characters get backslash-escaped
   return "\\" + ch;
}

createCache

/**
 * [createCache description] 创建一个队列缓存
 * @return {[type]} [description]
 */
function createCache() {
        var keys = [];
        // 这里创建了一个闭包来把数据持久化在内存之中
        function cache(key, value) {
                // 如果存储的值大于需要缓存的变量长度,然后就让缓存中数据出队
                // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
                if (keys.push(key + " ") > Expr.cacheLength) {

                        // Only keep the most recent entries
                        delete cache[keys.shift()];
                }
                return (cache[key + " "] = value);
        }
        return cache;
}

markFunction

var expando = "sizzle" + 1 * new Date(), // sizzle实例的唯一标识
/**
 * [markFunction description]  给传入的函数做一个唯一标识
 * @param  {Function} fn 
 * @return {[Function]}    返回这个方法
 */

function markFunction(fn) {
	fn[expando] = true;
	return fn;
}

assert

/**
 * [assert description] 测试使用的元素是否被支持
 * @param  {Function} fn 传递了创建元素,返回结果值是一个boolean
 * @return {[bolean]}     
 */
 
function assert(fn) {
        var el = document.createElement("fieldset");

        try {
                return !!fn(el); // !! 强制转为Boolean类型
        } catch (e) {
                return false;
        } finally {

                // Remove from its parent by default
                if (el.parentNode) {
                        el.parentNode.removeChild(el);
                }

                // release memory in IE 这样做的好处是可以释放这个变量在浏览器内存中的位置
                el = null;
        }
}

Sizzle.error

/**
 * [error description] 描述报错信息
 * @param  {[string]} msg 错误信息
 * @throw  {[Error]}   抛出一个错误原因
 */
Sizzle.error = function(msg) {
        throw new Error("Syntax error, unrecognized expression: " + msg);
};

isXML


/**
 * [isXML ] 检测XML节点
 * @param  {[type]}  elem XML元素
 * @return {Boolean}     
 */
Sizzle.isXML = function(elem) {
	// 获取元素命名空间的 URI
	var namespace = elem.namespaceURI,
	// 拿到元素父级的文档|| 或者当前的
		docElem = (elem.ownerDocument || elem).documentElement;
	// Support: IE <=8
	// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
	// 假设HTML文档元素还没有加载,例如在加载内部iframe的时候
	// https://bugs.jquery.com/ticket/4833
	// rhtml = /HTML$/i,
	// 如果存在namespace or用docElem && docElem.nodeName or || HTML
	// 如果是html就返回rhtml.test true  
	return !rhtml.test(namespace || docElem && docElem.nodeName || "HTML");
};

sortOrder

/**
 * [sortOrder 比较函数]
 * @param  {any} a 
 * @param  {any} b 
 * @return {number}   0 
 * // Array.sort(sortOrder) 如果返回0相对位置不变
 */
sortOrder = function (a, b) {
	if (a === b) {
		// 是否重复
			hasDuplicate = true
	}
	return 0
}

indexOf

/**
 * [indexOf description] 查找元素的位置
 * @param  {[array]} list 被查找对象
 * @param  {[any]} elem 查找对象
 * @return {[number]}     元素的位置
 */
indexOf = function (list, elem) {
	var i = 0, len = list.length
	for (;i < len; i++) {
		if (list[i]=== item) {
			return i
		}
	}
	return -1
}

andHandle

/**
 * [addHandle description] 用来把传入的字符串分割成数组为每个属性,Expr.attrHandle给每个属性添加方法
 * @param {[string]} attrs  字符串
 * @param {[function]} handler 
 */
function addHandle( attrs, handler ) {
	var arr = attrs.split( "|" ),
		i = arr.length;

	while ( i-- ) {
		Expr.attrHandle[ arr[ i ] ] = handler;
	}
}

如果要转载请写明来源谢谢大家 Sizzle源码分析(一) 基本概念

Sizzle源码分析(二) 工具方法

Sizzle源码分析(三) 兼容处理

Sizzle源码分析(五) 如何根据css选择器,选中对应的html节点