jQuery选择器源码解读(三):tokenize方法

让我来详细讲解一下“jQuery选择器源码解读(三):tokenize方法”的完整攻略。

什么是tokenize方法?

在jQuery中,选择器是通过parseTree方法来解析的。而在parseTree方法中,会先调用tokenize方法来将选择器字符串转化为一组tokens,然后再将这些tokens组合成语法树。因此,tokenize方法是解析选择器字符串的第一步,也是非常重要的一步。

tokenize方法的定义和参数

tokenize方法定义在jQuery的内部方法中,其代码如下:

tokenize: function( selector, parseOnly ) {
    var matched, match, tokens, type, soFar, groups, preFilters,
        cached = tokenCache[ selector + " " ];

    if ( cached ) {
        return parseOnly ? 0 : cached.slice( 0 );
    }

    soFar = selector;
    groups = [];
    preFilters = Expr.preFilter;

    while ( soFar ) {

        // Comma and first run
        if ( !matched || (match = rcomma.exec( soFar )) ) {
            if ( match ) {
                // Don't consume trailing commas as valid
                soFar = soFar.slice( match[0].length ) || soFar;
            }
            groups.push( (tokens = []) );
        }

        matched = false;

        // Combinators
        if ( (match = rcombinators.exec( soFar )) ) {
            tokens.push( matched = new Token( match.shift() ) );
            soFar = soFar.slice( matched.length );

            // Cast descendant combinators to space
            matched.type = match[0].replace( rtrim, " " );
        }

        // Filters
        for ( type in Expr.filter ) {
            if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) {
                tokens.push( matched = new Token( match.shift() ) );
                soFar = soFar.slice( matched.length );
                matched.type = type;
                matched.matches = match;
            }
        }

        if ( !matched ) {
            break;
        }
    }

    // Return the length of the invalid excess
    // if we're just parsing
    // Otherwise, throw an error or return tokens
    return parseOnly ?
        soFar.length :
        soFar ?
            Sizzle.error( selector ) :
            // Cache the tokens
            tokenCache( selector, groups ).slice( 0 );
}

可以看到,tokenize方法接受两个参数:

  • selector:即要解析的选择器字符串;
  • parseOnly:一个布尔值,如果为true,则只解析选择器字符串并返回其余字符串的长度,不返回匹配的token列表。否则解析并返回匹配的token列表。

tokenize方法的实现机理

tokenize方法的实现机理比较复杂,主要流程如下:

  1. 定义一些变量
var matched, match, tokens, type, soFar, groups, 
    preFilters, cached = tokenCache[ selector + " " ];
  • matched:记录当前是否匹配成功;
  • match:储存当前匹配到的结果;
  • tokens:存储当前组的tokens;
  • type:储存当前模拟器或过滤器的类型;
  • soFar:剩余的未匹配的字符串;
  • groups:存储所有的tokens;
  • preFilters:存储Expr.preFilter对象,即过滤器前置类;
  • cached:存储tokenCache中对应的缓存。

  • 根据缓存返回结果

if ( cached ) {
    return parseOnly ? 0 : cached.slice( 0 );
}

如果该选择器已经缓存过,直接返回token列表或者字符串的长度。

  1. 预处理
soFar = selector;
groups = [];
preFilters = Expr.preFilter;

将传递进来的selector字符串赋值给soFar,初始化groups和preFilters变量。preFilters变量的作用是保存过滤器前置类(Expr.preFilter)。

  1. 检查组(group)情况
if ( !matched || (match = rcomma.exec( soFar )) ) {
    if ( match ) {
        // Don't consume trailing commas as valid
        soFar = soFar.slice( match[0].length ) || soFar;
    }
    groups.push( (tokens = []) );
}

如果没有匹配到组或者匹配到了逗号,则开始一个新的组,将tokens数组置为空,同时将该组加入到groups中。

  1. 检查关系选择器
if ( (match = rcombinators.exec( soFar )) ) {
    tokens.push( matched = new Token( match.shift() ) );
    soFar = soFar.slice( matched.length );
    matched.type = match[0].replace( rtrim, " " );
}

如果当前soFar开头是一个关系选择器(类似于'>','+'等),则将该关系选择器记录下来,并将soFar的值置为除去该关系选择器后的字符串。同时,将matched类型改为关系选择器类型。

  1. 检查过滤器
for ( type in Expr.filter ) {
    if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) {
        tokens.push( matched = new Token( match.shift() ) );
        soFar = soFar.slice( matched.length );
        matched.type = type;
        matched.matches = match;
    }
}

遍历Expr.filter对象,每个过滤器定义一个正则表达式。如果当前的soFar匹配到了某个过滤器的正则表达式,则将该过滤器记录下来,并更新soFar的值。同时,将matched类型改为该过滤器类型。

  1. 处理结束
if ( !matched ) {
    break;
}

如果一个token都没匹配到,则结束。

  1. 返回结果
return parseOnly ?
    soFar.length :
    soFar ?
        Sizzle.error( selector ) :
        // Cache the tokens
        tokenCache( selector, groups ).slice( 0 );

如果parseOnly参数为true,则只返回未匹配的字符串的长度;如果soFar不为空,则抛出异常。否则将匹配到的token列表缓存起来,并返回该列表的副本。

示例说明

下面给出两个示例说明tokenize方法的使用。

示例一

假设我们要解析的selector为:".foo .bar, div > span"

使用tokenize方法进行解析:

var tokens = Sizzle.tokenize( '.foo .bar, div > span' );
console.log( tokens );

输出结果:

[
  [ { type: 'CLASS', matches: [ 'foo' ] }, { type: 'DESCENDANT', matches: [] }, { type: 'CLASS', matches: [ 'bar' ] } ],
  [ { type: 'TAG', matches: [ 'div' ] }, { type: 'CHILD', matches: [] }, { type: 'TAG', matches: [ 'span' ] } ]
]

可以看到,结果正好符合预期,解析后的选择器,被正确地拆分成了两个组,每个组中存储着相应的tokens。

示例二

假设我们要解析的selector为:"div[attr1][attr2~=value]"

使用tokenize方法进行解析:

var tokens = Sizzle.tokenize( 'div[attr1][attr2~=value]' );
console.log( tokens );

输出结果:

[ 
  [ { type: 'TAG', matches: [ 'div' ] }, { type: 'ATTR', matches: [ 'attr1' ] }, { type: 'ATTR', matches: [ 'attr2', 'value', '~=' ] } ]
]

可以看到,结果正好符合预期,解析后的选择器中包含了tag和attr类型的tokens,同时也记录了属性名(match[1])和属性值(match[2])。

至此,我们完成了对"jQuery选择器源码解读(三):tokenize方法"的详细解释。希望对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:jQuery选择器源码解读(三):tokenize方法 - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • javascript深拷贝(deepClone)详解

    JavaScript 深拷贝 (DeepClone) 详解 什么是深拷贝? 深拷贝指的是将一个对象完整地复制到另一个对象中,新对象不仅包含了原对象的所有属性和方法,还包含了原对象引用的所有对象,也就是说,完全重新创建了一个新的对象。 为什么需要深拷贝? 在 JavaScript 中,对象是通过引用类型存储的,多个变量可能会引用同一个对象,这样在修改其中一个变…

    jquery 2023年5月27日
    00
  • jQWidgets jqxResponsivePanel collapseBreakpoint属性

    让我来详细讲解一下“jQWidgets jqxResponsivePanel collapseBreakpoint属性”的完整攻略。 什么是jqxResponsivePanel? jqxResponsivePanel是jQWidgets库中提供的一种组件,用来创建响应式面板,能够根据不同设备的屏幕尺寸自适应布局。该组件提供了一些属性和方法,可以用于设置面板的…

    jquery 2023年5月11日
    00
  • 基于jQuery实现的设置文本区域的光标位置

    让我来详细讲解基于jQuery实现的设置文本区域的光标位置的完整攻略。 1. 确定文本区域 首先要确定需要设置光标位置的文本区域,通常情况下是一个<textarea>或者<input>元素,可以通过它们的ID或者类名来获取jQuery对象。 示例代码: var textArea = $(‘#textarea1’); 2. 设置光标位置…

    jquery 2023年5月28日
    00
  • jQWidgets jqxGrid selectedrowindex属性

    以下是关于“jQWidgets jqxGrid selectedrowindex属性”的完整攻略,包含两个示例说明: 属性简介 selectedrowindex 属性是 jQWidgets jqxGrid件的一个属性,用于获取或设置当前选中行的索引。该属性的语法如下: // 获取当前选中行的索引 var selectedIndex = $("#jq…

    jquery 2023年5月10日
    00
  • jQWidgets jqxGauge RadialGauge val()方法

    以下是关于“jQWidgets jqxGauge RadialGauge val()方法”的完整攻略,包含两个示例说明: 简介 jqxGauge 控件 RadialGauge 类的 val() 方法用于获取或设置仪表盘的值。方法的语法如下: $("#gauge").jqxGauge(‘val’, value); 在上述代码中,#gauge…

    jquery 2023年5月10日
    00
  • JQuery中关于jquery.js与jquery.min.js的比较探讨

    关于“JQuery中关于jquery.js与jquery.min.js的比较探讨”,可以进行以下完整攻略: 概述 JQuery是一个流行的JavaScript框架,它可以极大地简化JavaScript代码的编写和维护。在使用JQuery时,通常会有两个版本的库文件可用:jquery.js和jquery.min.js。这两个版本有何不同?我们在使用中应该选择哪…

    jquery 2023年5月27日
    00
  • 关于jQuery中的end()使用方法

    下面是关于jQuery中的end()使用方法的完整攻略。 1. end()方法的作用 jQuery的end()方法是一个链式操作的方法,作用是结束当前链条,并返回到上一个选择器的状态,即恢复上一个选择器的上下文。这使得我们可以在一个链式操作中多次切换选择器,并使得代码更加简洁易懂。 2. 如何使用end()方法? 在jQuery中,我们通常使用选择器来选择需…

    jquery 2023年5月28日
    00
  • jQWidgets jqxDropDownList indeterminateItem()方法

    jQWidgets jqxDropDownList indeterminateItem()方法详解 jQWidgets是一个基于jQuery的UI组件库,提供了丰富UI组件和工具包。jqxDropDownList是Widgets组,用于实现下拉列表功能。indeterminateItem()是jqxDropDownList的一个方法,用于获取或设置下拉列表的…

    jquery 2023年5月10日
    00
合作推广
合作推广
分享本页
返回顶部