国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

underscore 系列之實(shí)現(xiàn)一個(gè)模板引擎(下)

gyl_coder / 2876人閱讀

摘要:前言本篇接著上篇系列之實(shí)現(xiàn)一個(gè)模板引擎上。字符串中的每個(gè)字符均可由一個(gè)轉(zhuǎn)義序列表示。在中,有四個(gè)字符被認(rèn)為是行終結(jié)符,其他的折行字符都會(huì)被視為空白。

前言

本篇接著上篇 underscore 系列之實(shí)現(xiàn)一個(gè)模板引擎(上)。

鑒于本篇涉及的知識(shí)點(diǎn)太多,我們先來(lái)介紹下會(huì)用到的知識(shí)點(diǎn)。

反斜杠的作用
var txt = "We are the so-called "Vikings" from the north."
console.log(txt);

我們的本意是想打印帶 "" 包裹的 Vikings 字符串,但是在 JavaScript 中,字符串使用單引號(hào)或者雙引號(hào)來(lái)表示起始或者結(jié)束,這段代碼會(huì)報(bào) Unexpected identifier 錯(cuò)誤。

如果我們就是想要在字符串中使用單引號(hào)或者雙引號(hào)呢?

我們可以使用反斜杠用來(lái)在文本字符串中插入省略號(hào)、換行符、引號(hào)和其他特殊字符:

var txt = "We are the so-called "Vikings" from the north."
console.log(txt);

現(xiàn)在 JavaScript 就可以輸出正確的文本字符串了。

這種由反斜杠后接字母或數(shù)字組合構(gòu)成的字符組合就叫做“轉(zhuǎn)義序列”。

值得注意的是,轉(zhuǎn)義序列會(huì)被視為單個(gè)字符。

我們常見(jiàn)的轉(zhuǎn)義序列還有 表示換行、 表示制表符、 表示回車(chē)等等。

轉(zhuǎn)義序列

在 JavaScript 中,字符串值是一個(gè)由零或多個(gè) Unicode 字符(字母、數(shù)字和其他字符)組成的序列。

字符串中的每個(gè)字符均可由一個(gè)轉(zhuǎn)義序列表示。比如字母 a,也可以用轉(zhuǎn)義序列 u0061 表示。

轉(zhuǎn)義序列以反斜杠  開(kāi)頭,它的作用是告知 JavaScript 解釋器下一個(gè)字符是特殊字符。 

轉(zhuǎn)義序列的語(yǔ)法為 uhhhh,其中 hhhh 是四位十六進(jìn)制數(shù)。

根據(jù)這個(gè)規(guī)則,我們可以算出常見(jiàn)字符的轉(zhuǎn)義序列,以字母 m 為例:

// 1. 求出字符 `m` 對(duì)應(yīng)的 unicode 值
var unicode = "m".charCodeAt(0) // 109
// 2. 轉(zhuǎn)成十六進(jìn)制
var result = unicode.toString(16); // "6d"

我們就可以使用 u006d 表示 m,不信你可以直接在瀏覽器命令行中直接輸入字符串 "u006d",看下打印結(jié)果。

值得注意的是: 雖然也是一種轉(zhuǎn)義序列,但是也可以使用上面的方式:

var unicode = "
".charCodeAt(0) // 10
var result = unicode.toString(16); // "a"

所以我們可以用 u000A 來(lái)表示換行符 ,比如在瀏覽器命令行中直接輸入 "a b""a u000A b" 效果是一樣的。

講了這么多,我們來(lái)看看一些常用字符的轉(zhuǎn)義序列以及含義:

Unicode 字符值 轉(zhuǎn)義序列 含義
u0009 t 制表符
u000A n 換行
u000D r 回車(chē)
u0022 " 雙引號(hào)
u0027 " 單引號(hào)
u005C 反斜杠
u2028 行分隔符
u2029 段落分隔符
Line Terminators

Line Terminators,中文譯文行終結(jié)符。像空白字符一樣,行終結(jié)符可用于改善源文本的可讀性。

在 ES5 中,有四個(gè)字符被認(rèn)為是行終結(jié)符,其他的折行字符都會(huì)被視為空白。

這四個(gè)字符如下所示:

字符編碼值 名稱(chēng)
u000A 換行符
u000D 回車(chē)符
u2028 行分隔符
u2029 段落分隔符
Function

試想我們寫(xiě)這樣一段代碼,能否正確運(yùn)行:

var log = new Function("var a = "1	23";console.log(a)");
log()

答案是可以,那下面這段呢:

var log = new Function("var a = "1
23";console.log(a)");
log()

答案是不可以,會(huì)報(bào)錯(cuò) Uncaught SyntaxError: Invalid or unexpected token

這是為什么呢?

這是因?yàn)樵?Function 構(gòu)造函數(shù)的實(shí)現(xiàn)中,首先會(huì)將函數(shù)體代碼字符串進(jìn)行一次 ToString 操作,這時(shí)候字符串變成了:

var a = "1
23";console.log(a)

然后再檢測(cè)代碼字符串是否符合代碼規(guī)范,在 JavaScript 中,字符串表達(dá)式中是不允許換行的,這就導(dǎo)致了報(bào)錯(cuò)。

為了避免這個(gè)問(wèn)題,我們需要將代碼修改為:

var log = new Function("var a = "1
23";console.log(a)");
log()

其實(shí)不止 ,其他三種 行終結(jié)符,如果你在字符串表達(dá)式中直接使用,都會(huì)導(dǎo)致報(bào)錯(cuò)!

之所以講這個(gè)問(wèn)題,是因?yàn)樵谀0逡娴膶?shí)現(xiàn)中,就是使用了 Function 構(gòu)造函數(shù),如果我們?cè)谀0遄址惺褂昧?行終結(jié)符,便有可能會(huì)出現(xiàn)一樣的錯(cuò)誤,所以我們必須要對(duì)這四種 行終結(jié)符 進(jìn)行特殊的處理。

特殊字符

除了這四種 行終結(jié)符 之外,我們還要對(duì)兩個(gè)字符進(jìn)行處理。

一個(gè)是

比如說(shuō)我們的模板內(nèi)容中使用了:

var log = new Function("var a = "123";console.log(a)");
log(); // 1

其實(shí)我們是想打印 "123",但是因?yàn)榘? 當(dāng)成了特殊字符的標(biāo)記進(jìn)行處理,所以最終打印了 1。

同樣的道理,如果我們?cè)谑褂媚0逡娴臅r(shí)候,使用了 字符串,也會(huì)導(dǎo)致錯(cuò)誤的處理。

第二個(gè)是 "

如果我們?cè)谀0逡嬷惺褂昧?",因?yàn)槲覀儠?huì)拼接諸如 p.push(" ") 等字符串,因?yàn)?" 的原因,字符串會(huì)被錯(cuò)誤拼接,也會(huì)導(dǎo)致錯(cuò)誤。

所以總共我們需要對(duì)六種字符進(jìn)行特殊處理,處理的方式,就是正則匹配出這些特殊字符,然后比如將 替換成 替換成 " 替換成 ",處理的代碼為:

var escapes = {
    """: """,
    "": "",
    "
": "r",
    "
": "n",
    "u2028": "u2028",
    "u2029": "u2029"
};

var escapeRegExp = /|"|
|
|u2028|u2029/g;

var escapeChar = function(match) {
    return "" + escapes[match];
};

我們測(cè)試一下:

var str = "console.log("I am 
 Kevin");";
var newStr = str.replace(escapeRegExp, escapeChar);

eval(newStr)
// I am 
// Kevin
replace

我們來(lái)講一講字符串的 replace 函數(shù):

語(yǔ)法為:

str.replace(regexp|substr, newSubStr|function)

replace 的第一個(gè)參數(shù),可以傳一個(gè)字符串,也可以傳一個(gè)正則表達(dá)式。

第二個(gè)參數(shù),可以傳一個(gè)新字符串,也可以傳一個(gè)函數(shù)。

我們重點(diǎn)看下傳入函數(shù)的情況,簡(jiǎn)單舉一個(gè)例子:

var str = "hello world";
var newStr = str.replace("world", function(match){
    return match + "!"
})
console.log(newStr); // hello world!

match 表示匹配到的字符串,但函數(shù)的參數(shù)其實(shí)不止有 match,我們看個(gè)更復(fù)雜的例子:

function replacer(match, p1, p2, p3, offset, string) {
    // match,表示匹配的子串 abc12345#$*%
    // p1,第 1 個(gè)括號(hào)匹配的字符串 abc
    // p2,第 2 個(gè)括號(hào)匹配的字符串 12345
    // p3,第 3 個(gè)括號(hào)匹配的字符串 #$*%
    // offset,匹配到的子字符串在原字符串中的偏移量 0
    // string,被匹配的原字符串 abc12345#$*%
    return [p1, p2, p3].join(" - ");
}
var newString = "abc12345#$*%".replace(/([^d]*)(d*)([^w]*)/, replacer); // abc - 12345 - #$*%

另外要注意的是,如果第一個(gè)參數(shù)是正則表達(dá)式,并且其為全局匹配模式, 那么這個(gè)方法將被多次調(diào)用,每次匹配都會(huì)被調(diào)用。

舉個(gè)例子,如果我們要在一段字符串中匹配出 <%=xxx%> 中的值:

var str = "
  • <%=baidu%>
  • " str.replace(/<%=(.+?)%>/g, function(match, p1, offset, string){ console.log(match); console.log(p1); console.log(offset); console.log(string); })

    傳入的函數(shù)會(huì)被執(zhí)行兩次,第一次的打印結(jié)果為:

    <%=www.baidu.com%>
    www.baidu.com
    13
    
  • <%=baidu%>
  • 第二次的打印結(jié)果為:

    <%=baidu%>
    "baidu"
    33
    
  • <%=baidu%>
  • 正則表達(dá)式的創(chuàng)建

    當(dāng)我們要建立一個(gè)正則表達(dá)式的時(shí)候,我們可以直接創(chuàng)建:

    var reg = /ab+c/i;

    也可以使用構(gòu)造函數(shù)的方式:

    new RegExp("ab+c", "i");

    值得一提的是:每個(gè)正則表達(dá)式對(duì)象都有一個(gè) source 屬性,返回當(dāng)前正則表達(dá)式對(duì)象的模式文本的字符串:

    var regex = /fooBar/ig;
    console.log(regex.source); // "fooBar",不包含 /.../ 和 "ig"。
    正則表達(dá)式的特殊字符

    正則表達(dá)式中有一些特殊字符,比如 d 就表示了匹配一個(gè)數(shù)字,等價(jià)于 [0-9]。

    在上節(jié),我們使用 /<%=(.+?)%>/g 來(lái)匹配 <%=xxx%>,然而在 underscore 的實(shí)現(xiàn)中,用的卻是 /<%=([sS]+?)%>/g

    我們知道 s 表示匹配一個(gè)空白符,包括空格、制表符、換頁(yè)符、換行符和其他 Unicode 空格,S
    匹配一個(gè)非空白符,[sS]就表示匹配所有的內(nèi)容,可是為什么我們不直接使用 . 呢?

    我們可能以為 . 匹配任意單個(gè)字符,實(shí)際上,并不是如此, .匹配除行終結(jié)符之外的任何單個(gè)字符,不信我們做個(gè)試驗(yàn):

    var str = "<%=hello world%>"
    
    str.replace(/<%=(.+?)%>/g, function(match){
        console.log(match); // <%=hello world%>
    })

    但是如果我們?cè)?hello world 之間加上一個(gè)行終結(jié)符,比如說(shuō) "u2029":

    var str = "<%=hello u2029 world%>"
    
    str.replace(/<%=(.+?)%>/g, function(match){
        console.log(match);
    })

    因?yàn)槠ヅ洳坏剑砸膊粫?huì)執(zhí)行 console.log 函數(shù)。

    但是改成 /<%=([sS]+?)%>/g 就可以正常匹配:

    var str = "<%=hello u2029 world%>"
    
    str.replace(/<%=([sS]+?)%>/g, function(match){
        console.log(match); // <%=hello ? world%>
    })
    惰性匹配

    仔細(xì)看 /<%=([sS]+?)%>/g 這個(gè)正則表達(dá)式,我們知道 x+ 表示匹配 x 1 次或多次。x?表示匹配 x 0 次或 1 次,但是 +? 是個(gè)什么鬼?

    實(shí)際上,如果在數(shù)量詞 *、+、? 或 {}, 任意一個(gè)后面緊跟該符號(hào)(?),會(huì)使數(shù)量詞變?yōu)榉秦澙罚?non-greedy) ,即匹配次數(shù)最小化。反之,默認(rèn)情況下,是貪婪的(greedy),即匹配次數(shù)最大化。

    舉個(gè)例子:

    console.log("aaabc".replace(/a+/g, "d")); // dbc
    
    console.log("aaabc".replace(/a+?/g, "d")); // ffffdbc

    在這里我們應(yīng)該使用非惰性匹配,舉個(gè)例子:

    var str = "
  • <%=baidu%>
  • " str.replace(/<%=(.+?)%>/g, function(match){ console.log(match); }) // <%=www.baidu.com%> // <%=baidu%>

    如果我們使用惰性匹配:

    var str = "
  • <%=baidu%>
  • " str.replace(/<%=(.+)%>/g, function(match){ console.log(match); }) // <%=www.baidu.com%>"><%=baidu%>
    template

    講完需要的知識(shí)點(diǎn),我們開(kāi)始講 underscore 模板引擎的實(shí)現(xiàn)。

    與我們上篇使用數(shù)組的 push ,最后再 join 的方法不同,underscore 使用的是字符串拼接的方式。

    比如下面這樣一段模板字符串:

    <%for ( var i = 0; i < users.length; i++ ) { %>
        
  • <%=users[i].name%>
  • <% } %>

    我們先將 <%=xxx%> 替換成 "+ xxx +",再將 <%xxx%> 替換成 "; xxx __p+=":

    ";for ( var i = 0; i < users.length; i++ ) { __p+="
        
  • "+ users[i].name +"
  • "; } __p+="

    這段代碼肯定會(huì)運(yùn)行錯(cuò)誤的,所以我們?cè)偬砑有╊^尾代碼,然后組成一個(gè)完整的代碼字符串:

    var __p="";
    with(obj){
    __p+="
    
    ";for ( var i = 0; i < users.length; i++ ) { __p+="
        
  • "+ users[i].name +"
  • "; } __p+=" "; }; return __p;

    整理下代碼就是:

    var __p="";
    with(obj){
        __p+="";
        for ( var i = 0; i < users.length; i++ ) { 
            __p+="
  • "+ users[i].name +"
  • "; } __p+=""; }; return __p

    然后我們將 __p 這段代碼字符串傳入 Function 構(gòu)造函數(shù)中:

    var render = new Function(data, __p)

    我們執(zhí)行這個(gè) render 函數(shù),傳入需要的 data 數(shù)據(jù),就可以返回一段 HTML 字符串:

    render(data)
    第五版 - 特殊字符的處理

    我們接著上篇的第四版進(jìn)行書(shū)寫(xiě),不過(guò)加入對(duì)特殊字符的轉(zhuǎn)義以及使用字符串拼接的方式:

    // 第五版
    var settings = {
        // 求值
        evaluate: /<%([sS]+?)%>/g,
        // 插入
        interpolate: /<%=([sS]+?)%>/g,
    };
    
    var escapes = {
        """: """,
        "": "",
        "
    ": "r",
        "
    ": "n",
        "u2028": "u2028",
        "u2029": "u2029"
    };
    
    var escapeRegExp = /|"|
    |
    |u2028|u2029/g;
    
    var template = function(text) {
    
        var source = "var __p="";
    ";
        source = source + "with(obj){
    "
        source = source + "__p+="";
    
        var main = text
        .replace(escapeRegExp, function(match) {
            return "" + escapes[match];
        })
        .replace(settings.interpolate, function(match, interpolate){
            return ""+
    " + interpolate + "+
    ""
        })
        .replace(settings.evaluate, function(match, evaluate){
            return "";
     " + evaluate + "
    __p+=""
        })
    
        source = source + main + "";
     }; 
     return __p;";
    
        console.log(source)
    
        var render = new Function("obj",  source);
    
        return render;
    };

    完整的使用代碼可以參考 template 示例五。

    第六版 - 特殊值的處理

    不過(guò)有一點(diǎn)需要注意的是:

    如果數(shù)據(jù)中 users[i].url 不存在怎么辦?此時(shí)取值的結(jié)果為 undefined,我們知道:

    "1" + undefined // "1undefined"

    就相當(dāng)于拼接了 undefined 字符串,這肯定不是我們想要的。我們可以在代碼中加入一點(diǎn)判斷:

    .replace(settings.interpolate, function(match, interpolate){
        return ""+
    " + (interpolate == null ? "" : interpolate) + "+
    ""
    })

    但是吧,我就是不喜歡寫(xiě)兩遍 interpolate …… 嗯?那就這樣吧:

    var source = "var __t, __p="";
    ";
    
    ...
    
    .replace(settings.interpolate, function(match, interpolate){
        return ""+
    ((__t=(" + interpolate + "))==null?"":__t)+
    ""
    })

    其實(shí)就相當(dāng)于:

    var __t;
    
    var result = (__t = interpolate) == null ? "" : __t;

    完整的使用代碼可以參考 template 示例六。

    第七版

    現(xiàn)在我們使用的方式是將模板字符串進(jìn)行多次替換,然而在 underscore 的實(shí)現(xiàn)中,只進(jìn)行了一次替換,我們來(lái)看看 underscore 是怎么實(shí)現(xiàn)的:

    var template = function(text) {
        var matcher = RegExp([
            (settings.interpolate).source,
            (settings.evaluate).source
        ].join("|") + "|$", "g");
    
        var index = 0;
        var source = "__p+="";
    
        text.replace(matcher, function(match, interpolate, evaluate, offset) {
            source += text.slice(index, offset).replace(escapeRegExp, function(match) {
                return "" + escapes[match];
            });
    
            index = offset + match.length;
    
            if (interpolate) {
                source += ""+
    ((__t=(" + interpolate + "))==null?"":__t)+
    "";
            } else if (evaluate) {
                source += "";
    " + evaluate + "
    __p+="";
            }
    
            return match;
        });
    
        source += "";
    ";
    
        source = "with(obj||{}){
    " + source + "}
    "
    
        source = "var __t, __p="";" +
            source + "return __p;
    ";
    
        var render = new Function("obj", source);
    
        return render;
    };

    其實(shí)原理也很簡(jiǎn)單,就是在執(zhí)行多次匹配函數(shù)的時(shí)候,不斷復(fù)制字符串,處理字符串,拼接字符串,最后拼接首尾代碼,得到最終的代碼字符串。

    不過(guò)值得一提的是:在這段代碼里,matcher 的表達(dá)式最后為:/<%=([sS]+?)%>|<%([sS]+?)%>|$/g

    問(wèn)題是為什么還要加個(gè) |$ 呢?我們來(lái)看下 $:

    var str = "abc";
    str.replace(/$/g, function(match, offset){
        console.log(typeof match) // 空字符串
        console.log(offset) // 3
        return match
    })

    我們之所以匹配 $,是為了獲取最后一個(gè)字符串的位置,這樣當(dāng)我們 text.slice(index, offset)的時(shí)候,就可以截取到最后一個(gè)字符。

    完整的使用代碼可以參考 template 示例七。

    最終版

    其實(shí)代碼寫(xiě)到這里,就已經(jīng)跟 underscore 的實(shí)現(xiàn)很接近了,只是 underscore 加入了更多細(xì)節(jié)的處理,比如:

    對(duì)數(shù)據(jù)的轉(zhuǎn)義功能

    可傳入配置項(xiàng)

    對(duì)錯(cuò)誤的處理

    添加 source 屬性,以方便查看代碼字符串

    添加了方便調(diào)試的 print 函數(shù)

    ...

    但是這些內(nèi)容都還算簡(jiǎn)單,就不一版一版寫(xiě)了,最后的版本在 template 示例八,如果對(duì)其中有疑問(wèn),歡迎留言討論。

    underscore 系列

    underscore 系列目錄地址:https://github.com/mqyqingfeng/Blog。

    underscore 系列預(yù)計(jì)寫(xiě)八篇左右,重點(diǎn)介紹 underscore 中的代碼架構(gòu)、鏈?zhǔn)秸{(diào)用、內(nèi)部函數(shù)、模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫(xiě)出自己的 undercore。

    如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對(duì)作者也是一種鼓勵(lì)。

    文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/92760.html

    相關(guān)文章

    • underscore 的源碼該如何閱讀?

      摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個(gè)系列后,再跟著其他系列的文章接著學(xué)習(xí)。如何閱讀我在寫(xiě)系列的時(shí)候,被問(wèn)的最多的問(wèn)題就是該怎么閱讀源碼我想簡(jiǎn)單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個(gè)系列再見(jiàn)啦 前言 別名:《underscore 系列 8 篇正式完結(jié)!》 介紹 underscore 系列是我寫(xiě)的第三個(gè)系列,前兩個(gè)系列分別是 JavaScript 深入系列、...

      weknow619 評(píng)論0 收藏0
    • underscore 系列實(shí)現(xiàn)一個(gè)模板引擎(上)

      摘要:第一版我們來(lái)嘗試實(shí)現(xiàn)第一版第一版為了驗(yàn)證是否有用文件文件完整的可以查看示例一在這里我們使用了,實(shí)際上在文章中使用的是構(gòu)造函數(shù)。構(gòu)造函數(shù)創(chuàng)建一個(gè)新的對(duì)象。 前言 underscore 提供了模板引擎的功能,舉個(gè)例子: var tpl = hello: ; var compiled = _.template(tpl); compiled({name: Kevin}); // hello:...

      LeexMuller 評(píng)論0 收藏0
    • 淺談 Web 中前后端模板引擎的使用

      摘要:前端模板的出現(xiàn)使得前后端分離成為可能。總結(jié)本文簡(jiǎn)單介紹了模板引擎在前后端的使用,下文我們回到,重點(diǎn)分析下的使用方式以及源碼原理。樓主對(duì)于模板引擎的認(rèn)識(shí)比較淺顯,有不正之處希望指出感謝 前言 這篇文章本來(lái)不打算寫(xiě)的,實(shí)話說(shuō)樓主對(duì)前端模板的認(rèn)識(shí)還處在非常初級(jí)的階段,但是為了整個(gè) 源碼解讀系列 的完整性,在深入 Underscore _.template 方法源碼后,覺(jué)得還是有必要記下此文,...

      chenjiang3 評(píng)論0 收藏0
    • underscore 系列防沖突與 Utility Functions

      摘要:你可以輕松為你的函數(shù)庫(kù)添加防沖突功能。系列系列目錄地址。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑?qǐng)務(wù)必給予指正,十分感謝。 防沖突 underscore 使用 _ 作為函數(shù)的掛載對(duì)象,如果頁(yè)面中已經(jīng)存在了 _ 對(duì)象,underscore 就會(huì)覆蓋該對(duì)象,舉個(gè)例子: var _ = {value: 1 } // 引入 underscore 后 console.log(_.value); // un...

      qiangdada 評(píng)論0 收藏0
    • underscore 系列內(nèi)部函數(shù) restArgs

      摘要:與最后,使用我們的寫(xiě)的函數(shù)重寫(xiě)下函數(shù)系列系列目錄地址。系列預(yù)計(jì)寫(xiě)八篇左右,重點(diǎn)介紹中的代碼架構(gòu)鏈?zhǔn)秸{(diào)用內(nèi)部函數(shù)模板引擎等內(nèi)容,旨在幫助大家閱讀源碼,以及寫(xiě)出自己的。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑?qǐng)務(wù)必給予指正,十分感謝。 partial 在《 JavaScript 專(zhuān)題之偏函數(shù)》中,我們寫(xiě)了一個(gè) partial 函數(shù),用來(lái)固定函數(shù)的部分參數(shù),實(shí)現(xiàn)代碼如下: // 這是文章中的第一版 fu...

      zzzmh 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
    <