最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript正則表達(dá)式中g(shù)標(biāo)志詳解

 更新時(shí)間:2022年03月18日 11:47:10   作者:伍陸柒  
正則的思想都是一樣的,但是具體的寫(xiě)法會(huì)有所不同,下面這篇文章主要給大家介紹了關(guān)于JavaScript正則表達(dá)式中g(shù)標(biāo)志的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

緣起

有一天在思否社區(qū)看到有個(gè)問(wèn)題,大致描述如下

const list = ['a', 'b', '-', 'c', 'd'];
const reg = /[a-z]/g;
const letters = list.filter(i => reg.test(i));

// letters === ['a', 'c'];
// 如果正則不使用`g`標(biāo)志可以得到所有的字母
// 為什么加入`g`之后就不可以了

對(duì)問(wèn)題而言,遍歷中的i就是一個(gè)字符,不需要用到g。

但是就我對(duì)正則的理解(過(guò)于淺薄)感覺(jué)上有沒(méi)有g(shù)(只是全局搜索,不會(huì)匹配到就停下來(lái))應(yīng)該不影響,激發(fā)了我的好奇心。

上面題的建議寫(xiě)法如下

const reg = /[a-z]/g;
reg.test('a'); // => true
reg.test('a'); // => false
reg.test('a'); // => true
reg.test('a'); // => false
reg.test('a'); // => true

解密過(guò)程

首先可以確定的表現(xiàn)一定是g導(dǎo)致的

搜索引擎

打開(kāi) MDN 仔細(xì)查看g標(biāo)志的作用,得到結(jié)論和我的理解無(wú)二。

我猜想應(yīng)該就是g可能啟用了某種緩存,又因?yàn)閞eg相對(duì)過(guò)濾器是全局變量,我將代碼改為:

const list = ['a', 'b', '-', 'c', 'd'];
const letters = list.filter(i => /[a-z]/g.test(i));

// letters === ['a', 'b', 'c', 'd'];

將正則聲明到每一次遍歷,得到結(jié)論就是正確的,驗(yàn)證了我的猜想。也得到了,緩存就是正則中的某個(gè)地方

下面我找到對(duì)應(yīng)的源碼來(lái)查看問(wèn)題的原因

源碼層面

由于最近在看 Rust,所以使用 Rust 編寫(xiě)的源碼查看

打開(kāi)項(xiàng)目后,點(diǎn)擊.進(jìn)入 vscode 模式,command+p 搜索 regexp 關(guān)鍵詞

進(jìn)入test.rs文件,command+f 搜索/g可以找到在 90 行有個(gè)last_index()的測(cè)試

#[test]
fn last_index() {
    let mut context = Context::default();
    let init = r#"
        var regex = /[0-9]+(\.[0-9]+)?/g;
        "#;
    // forward 的作用:更改 context,并返回結(jié)果的字符串。
    eprintln!("{}", forward(&mut context, init));
    assert_eq!(forward(&mut context, "regex.lastIndex"), "0");
    assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "true");
    assert_eq!(forward(&mut context, "regex.lastIndex"), "3");
    assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "false");
    assert_eq!(forward(&mut context, "regex.lastIndex"), "0");
}

看到了有l(wèi)astIndex關(guān)鍵字,這里再已經(jīng)大致猜到問(wèn)題的原因了,g 標(biāo)志存在匹配后的最后一個(gè)下標(biāo),導(dǎo)致出現(xiàn)問(wèn)題。

我們將視線移入到mod.rs文件中,搜索test

在 631 行看到了fn test()方法

pub(crate) fn test(
    this: &JsValue,
    args: &[JsValue],
    context: &mut Context,
) -> JsResult<JsValue> {
    // 1. Let R be the this value.
    // 2. If Type(R) is not Object, throw a TypeError exception.
    let this = this.as_object().ok_or_else(|| {
        context
            .construct_type_error("RegExp.prototype.test method called on incompatible value")
    })?;

    // 3. Let string be ? ToString(S).
    let arg_str = args
        .get(0)
        .cloned()
        .unwrap_or_default()
        .to_string(context)?;

    // 4. Let match be ? RegExpExec(R, string).
    let m = Self::abstract_exec(this, arg_str, context)?;

    // 5. If match is not null, return true; else return false.
    if m.is_some() {
        Ok(JsValue::new(true))
    } else {
        Ok(JsValue::new(false))
    }
}

test()方法中找到了Self::abstract_exec()方法

pub(crate) fn abstract_exec(
    this: &JsObject,
    input: JsString,
    context: &mut Context,
) -> JsResult<Option<JsObject>> {
    // 1. Assert: Type(R) is Object.
    // 2. Assert: Type(S) is String.

    // 3. Let exec be ? Get(R, "exec").
    let exec = this.get("exec", context)?;

    // 4. If IsCallable(exec) is true, then
    if let Some(exec) = exec.as_callable() {
        // a. Let result be ? Call(exec, R, ? S ?).
        let result = exec.call(&this.clone().into(), &[input.into()], context)?;

        // b. If Type(result) is neither Object nor Null, throw a TypeError exception.
        if !result.is_object() && !result.is_null() {
            return context.throw_type_error("regexp exec returned neither object nor null");
        }

        // c. Return result.
        return Ok(result.as_object().cloned());
    }

    // 5. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]).
    if !this.is_regexp() {
        return context.throw_type_error("RegExpExec called with invalid value");
    }

    // 6. Return ? RegExpBuiltinExec(R, S).
    Self::abstract_builtin_exec(this, &input, context)
}

又在Self::abstract_exec()方法中找到了Self::abstract_builtin_exec()方法

pub(crate) fn abstract_builtin_exec(
    this: &JsObject,
    input: &JsString,
    context: &mut Context,
) -> JsResult<Option<JsObject>> {
    // 1. Assert: R is an initialized RegExp instance.
    let rx = {
        let obj = this.borrow();
        if let Some(rx) = obj.as_regexp() {
            rx.clone()
        } else {
            return context.throw_type_error("RegExpBuiltinExec called with invalid value");
        }
    };

    // 2. Assert: Type(S) is String.

    // 3. Let length be the number of code units in S.
    let length = input.encode_utf16().count();

    // 4. Let lastIndex be ?(? ToLength(? Get(R, "lastIndex"))).
    let mut last_index = this.get("lastIndex", context)?.to_length(context)?;

    // 5. Let flags be R.[[OriginalFlags]].
    let flags = &rx.original_flags;

    // 6. If flags contains "g", let global be true; else let global be false.
    let global = flags.contains('g');

    // 7. If flags contains "y", let sticky be true; else let sticky be false.
    let sticky = flags.contains('y');

    // 8. If global is false and sticky is false, set lastIndex to 0.
    if !global && !sticky {
        last_index = 0;
    }

    // 9. Let matcher be R.[[RegExpMatcher]].
    let matcher = &rx.matcher;

    // 10. If flags contains "u", let fullUnicode be true; else let fullUnicode be false.
    let unicode = flags.contains('u');

    // 11. Let matchSucceeded be false.
    // 12. Repeat, while matchSucceeded is false,
    let match_value = loop {
        // a. If lastIndex > length, then
        if last_index > length {
            // i. If global is true or sticky is true, then
            if global || sticky {
                // 1. Perform ? Set(R, "lastIndex", +0??, true).
                this.set("lastIndex", 0, true, context)?;
            }

            // ii. Return null.
            return Ok(None);
        }

        // b. Let r be matcher(S, lastIndex).
        // Check if last_index is a valid utf8 index into input.
        let last_byte_index = match String::from_utf16(
            &input.encode_utf16().take(last_index).collect::<Vec<u16>>(),
        ) {
            Ok(s) => s.len(),
            Err(_) => {
                return context
                    .throw_type_error("Failed to get byte index from utf16 encoded string")
            }
        };
        let r = matcher.find_from(input, last_byte_index).next();

        match r {
            // c. If r is failure, then
            None => {
                // i. If sticky is true, then
                if sticky {
                    // 1. Perform ? Set(R, "lastIndex", +0??, true).
                    this.set("lastIndex", 0, true, context)?;

                    // 2. Return null.
                    return Ok(None);
                }

                // ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).
                last_index = advance_string_index(input, last_index, unicode);
            }

            Some(m) => {
                // c. If r is failure, then
                #[allow(clippy::if_not_else)]
                if m.start() != last_index {
                    // i. If sticky is true, then
                    if sticky {
                        // 1. Perform ? Set(R, "lastIndex", +0??, true).
                        this.set("lastIndex", 0, true, context)?;

                        // 2. Return null.
                        return Ok(None);
                    }

                    // ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).
                    last_index = advance_string_index(input, last_index, unicode);
                // d. Else,
                } else {
                    //i. Assert: r is a State.
                    //ii. Set matchSucceeded to true.
                    break m;
                }
            }
        }
    };

    // 13. Let e be r's endIndex value.
    let mut e = match_value.end();

    // 14. If fullUnicode is true, then
    if unicode {
        // e is an index into the Input character list, derived from S, matched by matcher.
        // Let eUTF be the smallest index into S that corresponds to the character at element e of Input.
        // If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S.
        // b. Set e to eUTF.
        e = input.split_at(e).0.encode_utf16().count();
    }

    // 15. If global is true or sticky is true, then
    if global || sticky {
        // a. Perform ? Set(R, "lastIndex", ??(e), true).
        this.set("lastIndex", e, true, context)?;
    }

    // 16. Let n be the number of elements in r's captures List. (This is the same value as 22.2.2.1's NcapturingParens.)
    let n = match_value.captures.len();
    // 17. Assert: n < 23^2 - 1.
    debug_assert!(n < 23usize.pow(2) - 1);

    // 18. Let A be ! ArrayCreate(n + 1).
    // 19. Assert: The mathematical value of A's "length" property is n + 1.
    let a = Array::array_create(n + 1, None, context)?;

    // 20. Perform ! CreateDataPropertyOrThrow(A, "index", ??(lastIndex)).
    a.create_data_property_or_throw("index", match_value.start(), context)
        .expect("this CreateDataPropertyOrThrow call must not fail");

    // 21. Perform ! CreateDataPropertyOrThrow(A, "input", S).
    a.create_data_property_or_throw("input", input.clone(), context)
        .expect("this CreateDataPropertyOrThrow call must not fail");

    // 22. Let matchedSubstr be the substring of S from lastIndex to e.
    let matched_substr = if let Some(s) = input.get(match_value.range()) {
        s
    } else {
        ""
    };

    // 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr).
    a.create_data_property_or_throw(0, matched_substr, context)
        .expect("this CreateDataPropertyOrThrow call must not fail");

    // 24. If R contains any GroupName, then
    // 25. Else,
    let named_groups = match_value.named_groups();
    let groups = if named_groups.clone().count() > 0 {
        // a. Let groups be ! OrdinaryObjectCreate(null).
        let groups = JsValue::from(JsObject::empty());

        // Perform 27.f here
        // f. If the ith capture of R was defined with a GroupName, then
        // i. Let s be the CapturingGroupName of the corresponding RegExpIdentifierName.
        // ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue).
        for (name, range) in named_groups {
            if let Some(range) = range {
                let value = if let Some(s) = input.get(range.clone()) {
                    s
                } else {
                    ""
                };

                groups
                    .to_object(context)?
                    .create_data_property_or_throw(name, value, context)
                    .expect("this CreateDataPropertyOrThrow call must not fail");
            }
        }
        groups
    } else {
        // a. Let groups be undefined.
        JsValue::undefined()
    };

    // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
    a.create_data_property_or_throw("groups", groups, context)
        .expect("this CreateDataPropertyOrThrow call must not fail");

    // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do
    for i in 1..=n {
        // a. Let captureI be ith element of r's captures List.
        let capture = match_value.group(i);

        let captured_value = match capture {
            // b. If captureI is undefined, let capturedValue be undefined.
            None => JsValue::undefined(),
            // c. Else if fullUnicode is true, then
            // d. Else,
            Some(range) => {
                if let Some(s) = input.get(range) {
                    s.into()
                } else {
                    "".into()
                }
            }
        };

        // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(??(i)), capturedValue).
        a.create_data_property_or_throw(i, captured_value, context)
            .expect("this CreateDataPropertyOrThrow call must not fail");
    }

    // 28. Return A.
    Ok(Some(a))
}

Self::abstract_builtin_exec()方法中存在global以及l(fā)ast_index這樣看來(lái)最終執(zhí)行的方法就是在這里了,仔細(xì)查看該方法中的代碼(代碼寫(xiě)的很詳細(xì)而且每一步都有注釋)

在第 12 步中:

  • lastIndex 超過(guò)文本長(zhǎng)度且當(dāng) global 存在時(shí)將 lastIndex 置為 0
  • 獲取匹配到的值(match_value

    如果未匹配到則置為advance_string_index()方法的返回值

    advance_string_index()不在當(dāng)前問(wèn)題的考慮范圍 https://tc39.es/ecma262/#sec-...

第 13 步獲取匹配到的值的 endIndex

第 15 步將 lastIndex 置為 endIndex

至此也就整明白了g標(biāo)志的含義,在正則的原型鏈中存在一個(gè)lastIndex,如果匹配為真時(shí)lastIndex不會(huì)重置為 0 ,下一次開(kāi)始時(shí)繼承了上次位置,

結(jié)論

在問(wèn)題代碼中分析

const reg = /[a-z]/g; // 聲明后,lastIndex 為 0
reg.test('a'); // => true;第一次匹配后,lastIndex 為 1
reg.test('a'); // => false;第二次匹配由于 lastIndex 為 1,且字符只有一個(gè),得到 false,將 lastIndex 置為 0
reg.test('a'); // => true;下面依次循環(huán)前兩次的邏輯
reg.test('a'); // => false;
reg.test('a'); // => true;

到此這篇關(guān)于JavaScript正則表達(dá)式中g(shù)標(biāo)志詳解的文章就介紹到這了,更多相關(guān)js正則中g(shù)標(biāo)志內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ECharts實(shí)現(xiàn)數(shù)據(jù)超出Y軸最大值max但不隱藏

    ECharts實(shí)現(xiàn)數(shù)據(jù)超出Y軸最大值max但不隱藏

    這篇文章主要為大家介紹了ECharts實(shí)現(xiàn)數(shù)據(jù)超出Y軸最大值max但不隱藏實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Javascript 加載和執(zhí)行-性能提高篇

    Javascript 加載和執(zhí)行-性能提高篇

    Javascript 在瀏覽器中的性能問(wèn)題,可能是最重要的可用性問(wèn)題;Js的阻塞性 瀏覽器用單一進(jìn)程來(lái)處理UI進(jìn)程和Js的執(zhí)行;不管是內(nèi)嵌的還是外鏈的,下載并立即執(zhí)行 因?yàn)樗锌赡軙?huì)修改頁(yè)面
    2012-12-12
  • layui上傳圖片到服務(wù)器的非項(xiàng)目目錄下的方法

    layui上傳圖片到服務(wù)器的非項(xiàng)目目錄下的方法

    今天小編就為大家分享一篇layui上傳圖片到服務(wù)器的非項(xiàng)目目錄下的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-09-09
  • JavaScript是否可實(shí)現(xiàn)多線程  深入理解JavaScript定時(shí)機(jī)制

    JavaScript是否可實(shí)現(xiàn)多線程 深入理解JavaScript定時(shí)機(jī)制

    JavaScript的setTimeout與setInterval是兩個(gè)很容易欺騙別人感情的方法,因?yàn)槲覀冮_(kāi)始常常以為調(diào)用了就會(huì)按既定的方式執(zhí)行, 我想不少人都深有同感,
    2009-12-12
  • js實(shí)現(xiàn)仿Discuz文本框彈出層效果

    js實(shí)現(xiàn)仿Discuz文本框彈出層效果

    這篇文章主要介紹了js實(shí)現(xiàn)仿Discuz文本框彈出層效果的方法,可實(shí)現(xiàn)點(diǎn)擊文本框彈出窗口選擇數(shù)據(jù)的效果,涉及鼠標(biāo)事件及頁(yè)面自定義彈出窗口的相關(guān)操作技巧,需要的朋友可以參考下
    2015-08-08
  • javascript 同時(shí)在IE和FireFox獲取KeyCode的代碼

    javascript 同時(shí)在IE和FireFox獲取KeyCode的代碼

    以前一直在IE8中測(cè)試網(wǎng)站,后來(lái)寫(xiě)的一部分含有Ajax的代碼出現(xiàn)了故障,不得已下載了Firefox以及它的插件Firebug,才發(fā)現(xiàn),F(xiàn)F不支持windows.event事件。于是換了一種思路。
    2010-02-02
  • 深入理解JS中的變量及作用域、undefined與null

    深入理解JS中的變量及作用域、undefined與null

    本篇文章主要是對(duì)JS中的變量及作用域、undefined與null進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助
    2014-03-03
  • 設(shè)置BFC功能及使用示例詳解

    設(shè)置BFC功能及使用示例詳解

    這篇文章主要為大家介紹了設(shè)置BFC功能及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • 編寫(xiě)高性能的JavaScript 腳本的加載與執(zhí)行

    編寫(xiě)高性能的JavaScript 腳本的加載與執(zhí)行

    把腳本放在body中,當(dāng)瀏覽器遇見(jiàn)<script>標(biāo)簽時(shí), 瀏覽器不知道腳本會(huì)插入文本還是html標(biāo)簽,因此瀏覽器會(huì)停止分析html頁(yè)面而去執(zhí)行腳本。
    2010-04-04
  • Javascript前端經(jīng)典的面試題及答案

    Javascript前端經(jīng)典的面試題及答案

    最近在網(wǎng)上看到了一些關(guān)于Javascript的面試題就整理了下來(lái),希望對(duì)有需要的朋友或者自己能有一定的幫助,后續(xù)看到再繼續(xù)補(bǔ)充。下面一起來(lái)看看這篇關(guān)于Javascript前端常見(jiàn)的面試題的文章,一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。
    2017-03-03

最新評(píng)論

惠安县| 房产| 上虞市| 漯河市| 阿克陶县| 英超| 济源市| 大埔区| 阿克苏市| 桦川县| 凤城市| 汕尾市| 夏河县| 乐清市| 德州市| 天门市| 越西县| 连江县| 会同县| 永仁县| 宜川县| 舟曲县| 隆回县| 商水县| 阳东县| 呼玛县| 太仆寺旗| 呼伦贝尔市| 莆田市| 绩溪县| 新宁县| 乳源| 商丘市| 扶绥县| 姜堰市| 习水县| 拜泉县| 镇康县| 阆中市| 岱山县| 清徐县|