JavaScript 忍者秘籍筆記——函數(shù)是根基

第三章 函數(shù)是根基

函數(shù)的獨(dú)特之處

函數(shù)是第一型(first-class)對(duì)象

對(duì)象在 javascript 中有如下功能:

  • 可以通過字面量進(jìn)行創(chuàng)建汗洒。
  • 可以賦值給變量、數(shù)組或其他對(duì)象的屬性吵瞻。
  • 可以作為參數(shù)傳遞給函數(shù)。
  • 可以作為函數(shù)的返回值進(jìn)行返回甘磨。
  • 可以擁有動(dòng)態(tài)創(chuàng)建并賦值的屬性橡羞。

在 javascript 中,函數(shù)擁有全部這些功能济舆。除了可以像其它對(duì)象類型一樣使用外卿泽,函數(shù)還可以被調(diào)用。這些調(diào)用通常以異步方式進(jìn)行滋觉。

瀏覽器的事件輪詢

桌面應(yīng)用程序(GUI)大多采用如下方式:

  1. 創(chuàng)建用戶界面签夭。
  2. 進(jìn)入輪詢,等待事件觸發(fā)椎侠。
  3. 調(diào)用事件的處理程序(監(jiān)聽器[listener])第租。

瀏覽器編程唯一的不同就是:代碼不負(fù)責(zé)事件輪詢和事件派發(fā),而是瀏覽器處理我纪。

我們的職責(zé)是為瀏覽器中發(fā)生的各種時(shí)間建立事件的處理程序(handler)慎宾。這些事件在觸發(fā)時(shí)被放置在一個(gè)事件隊(duì)列(先進(jìn)先出列表[FIFO])中,然后瀏覽器將調(diào)用已經(jīng)為這些事件建立好的處理程序(handler)浅悉。因?yàn)檫@些事件發(fā)生的時(shí)間和順序都是不可預(yù)知的趟据,所以事件處理函數(shù)的調(diào)用也是異步的。

瀏覽器的事件輪詢是單線程的术健。每個(gè)事件都是按照在隊(duì)列中所放置的順序來處理的汹碱。這就是所謂的FIFO(先進(jìn)先出)列表,或者一個(gè)使用古老定時(shí)器的筒倉(silo)荞估。每個(gè)事件都在自己的生命周期內(nèi)進(jìn)行處理咳促,所有其他事件必須等到這個(gè)事件處理結(jié)束后才能繼續(xù)處理。執(zhí)行過程如圖所示:

瀏覽器在單線程中處理事件輪詢勘伺,并處理每個(gè)事件自身的簡(jiǎn)化視圖

瀏覽器把事件放到隊(duì)列上的機(jī)制是在事件輪詢模型之外跪腹。確定事件何時(shí)發(fā)生并把它們放到事件隊(duì)列上的過程所處的線程,并不參與事件本身的處理娇昙。

回調(diào)的概念

定義一個(gè)函數(shù)尺迂,以便其它一些代碼在適當(dāng)?shù)臅r(shí)候調(diào)用它。

函數(shù)聲明

聲明一個(gè)函數(shù)時(shí),該名稱在整個(gè)函數(shù)聲明范圍內(nèi)時(shí)有效的噪裕。此外蹲盘,如果一個(gè)命名函數(shù)聲明在頂層,window 對(duì)象上的同名屬性則會(huì)引用到該函數(shù)膳音。

所有的函數(shù)都有一個(gè) name 屬性召衔,該屬性保存的是該函數(shù)名稱的字符串。沒有名稱的函數(shù)也仍然有 name 屬性祭陷,只是該屬性值為空字符串苍凛。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>證明函數(shù)聲明相關(guān)內(nèi)容</title>
    <style>
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }

        // 聲明一個(gè)命名函數(shù),該名稱在當(dāng)前作用域有效兵志,并隱式在window上添加一個(gè)同名屬性
        function isNimble(){return true;}
        // 判斷window屬性是確定的
        assert(typeof window.isNimble === "function", "isNimble() defined");
        // 判斷函數(shù)的 name 屬性
        assert(isNimble.name === "isNimble", "isNimble() has a name");

        // 創(chuàng)建一個(gè)匿名函數(shù)醇蝴,并賦值給canFly變量
        var canFly = function(){return true;};
        assert(typeof window.canFly === "function", "canFly() defined");
        assert(canFly.name === "", "canFly() has no name");

        // 創(chuàng)建一個(gè)匿名函數(shù)并引用到window的一個(gè)屬性上
        window.isDeadly = function(){return true;};
        assert(typeof window.isDeadly === "function", "isDeadly() defined");

        // 在outer函數(shù)內(nèi)定義一個(gè)inner函數(shù),測(cè)試該inner()在其定義之前和之后都可以訪問到想罕,并且沒有創(chuàng)建全局的inner()
        function outer(){
            assert(typeof inner === "function", "inner() in scope before declaration");
            function inner(){}
            assert(typeof inner === "function", "inner() in scope after declaration");
            assert(window.inner === undefined, "inner() not in global scope");
        }

        // outer()可以在全局作用域內(nèi)訪問到悠栓,而inner()則不可以
        outer();
        assert(window.inner === undefined, "inner() still not in global scope");

        // 這里聲明的函數(shù)名無效,真正起到控制作用的是變量名
        window.wieldsSword = function swingsSword(){return true;};
        assert(window.wieldsSword.name === 'swingsSword', "wieldsSword's real name is swingsSword");
    </script>
</body>
</html>

作用域和函數(shù)

在 JavaScript 中按价,作用域是由 function 進(jìn)行聲明的惭适,而不是代碼塊。聲明的作用域創(chuàng)建于代碼塊楼镐,但不是終結(jié)于代碼塊癞志。

  • 變量聲明的作用域開始于聲明的地方,結(jié)束于所在函數(shù)的結(jié)尾框产,于代碼嵌套無關(guān)凄杯。
  • 命名函數(shù)的作用域是指聲明該函數(shù)的整個(gè)函數(shù)范圍,于代碼嵌套無關(guān)茅信。(機(jī)制提升)
  • 對(duì)于作用域聲明盾舌,全局上下文就像一個(gè)包含頁面所有代碼的超大型函數(shù)。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>作用域斷言測(cè)試</title>
    <style>
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }

        assert(true,"|------BEFORE OUTER ------|")
        assert(typeof outer == 'function', "outer() is in scope");
        assert(typeof inner === 'function', "inner() is scope");
        assert(typeof a === 'number', "a is in scope");
        assert(typeof b === 'number', "b is in scope");
        assert(typeof c === 'number', "c is in scope");
        assert(typeof d === 'number', "d is not in scope");
        assert(typeof e === 'number', "e is not in scope");

        function outer(){
            assert(true,"|------ INSIDE OUTER,BEFORE a ------|")
            assert(typeof outer == 'function', "outer() is in scope");
            assert(typeof inner === 'function', "inner() is scope");
            assert(typeof a === 'number', "a is in scope");
            assert(typeof b === 'number', "b is in scope");
            assert(typeof c === 'number', "c is in scope");
            assert(typeof d === 'number', "d is not in scope");
            assert(typeof e === 'number', "e is not in scope");

            var a = 1;

            assert(true,"|------ INSIDE OUTER,AFTER a ------|")
            assert(typeof outer == 'function', "outer() is in scope");
            assert(typeof inner === 'function', "inner() is scope");
            assert(typeof a === 'number', "a is in scope");
            assert(typeof b === 'number', "b is in scope");
            assert(typeof c === 'number', "c is in scope");
            assert(typeof d === 'number', "d is not in scope");
            assert(typeof e === 'number', "e is not in scope");

            function inner(){/******/}
            var b = 2;

            assert(true,"|------ INSIDE OUTER,AFTER inner() AND b ------|")
            assert(typeof outer == 'function', "outer() is in scope");
            assert(typeof inner === 'function', "inner() is scope");
            assert(typeof a === 'number', "a is in scope");
            assert(typeof b === 'number', "b is in scope");
            assert(typeof c === 'number', "c is in scope");
            assert(typeof d === 'number', "d is not in scope");
            assert(typeof e === 'number', "e is not in scope");

            if(a == 1){
                assert(true,"|------ INSIDE OUTER,INSIDE if AND INSIDE if ------|")
                assert(typeof outer == 'function', "outer() is in scope");
                assert(typeof inner === 'function', "inner() is scope");
                assert(typeof a === 'number', "a is in scope");
                assert(typeof b === 'number', "b is in scope");
                assert(typeof c === 'number', "c is in scope"); // 這里c 被初始化為undefined蘸鲸,所有斷言會(huì)失敗
                var c = 3;
                // 不能再let const 聲明之前調(diào)用 assert 會(huì)報(bào)錯(cuò)
                let d=4; // 添加 ES6 的let
                const e=5; // 添加 ES6 的const

                assert(true,"|------ INSIDE OUTER,INSIDE if AND AFTER let const ------|")
                assert(typeof outer == 'function', "outer() is in scope");
                assert(typeof inner === 'function', "inner() is scope");
                assert(typeof a === 'number', "a is in scope");
                assert(typeof b === 'number', "b is in scope");
                assert(typeof c === 'number', "c is in scope");
                assert(typeof d === 'number', "d is not in scope");
                assert(typeof e === 'number', "e is not in scope");
            }

            assert(true,"|------ INSIDE OUTER,OUTSIDE if ------|")
            assert(typeof outer == 'function', "outer() is in scope");
            assert(typeof inner === 'function', "inner() is scope");
            assert(typeof a === 'number', "a is in scope");
            assert(typeof b === 'number', "b is in scope");
            assert(typeof c === 'number', "c is in scope");
            assert(typeof d === 'number', "d is not in scope");
            assert(typeof e === 'number', "e is not in scope");
        }
        outer();
        assert(true,"|------ INSIDE OUTER,OUTSIDE if ------|")
        assert(typeof outer == 'function', "outer() is in scope");
        assert(typeof inner === 'function', "inner() is scope");
        assert(typeof a === 'number', "a is in scope");
        assert(typeof b === 'number', "b is in scope");
        assert(typeof c === 'number', "c is in scope");
        assert(typeof d === 'number', "d is not in scope");
        assert(typeof e === 'number', "e is not in scope");
    </script>
</body>
</html>

函數(shù)調(diào)用

有四種不同的方式進(jìn)行函數(shù)調(diào)用,每種方式都有細(xì)微的差別(主要區(qū)別在于如何定義每種調(diào)用類型的this):

  • 作為一個(gè)函數(shù)進(jìn)行調(diào)用窿锉,是最簡(jiǎn)單的形式酌摇。
  • 作為一個(gè)方法進(jìn)行調(diào)用,在對(duì)象上進(jìn)行調(diào)用嗡载,支持面向?qū)ο缶幊獭?/li>
  • 作為構(gòu)造器進(jìn)行調(diào)用窑多,創(chuàng)建一個(gè)新對(duì)象。
  • 通過 apply()call()方法進(jìn)行調(diào)用洼滚。

從參數(shù)到函數(shù)形參

  • 如果實(shí)際傳遞的參數(shù)數(shù)量大于函數(shù)聲明的形參數(shù)量埂息,超出的參數(shù)不會(huì)配給形參。
  • 如果聲明的形參數(shù)量大于實(shí)際傳遞的參數(shù)數(shù)量,沒有對(duì)應(yīng)參數(shù)的形參會(huì)賦值為undefined千康。

所有的函數(shù)調(diào)用都會(huì)傳遞兩個(gè)隱式參數(shù):argumentsthis享幽。

  • arguments:是傳遞給函數(shù)的所有參數(shù)的一個(gè)集合,該集合有一個(gè) length 屬性拾弃。arguments 不是JS的數(shù)組值桩,不能使用數(shù)組方法。
  • thisthis參數(shù)引用了與該函數(shù)調(diào)用進(jìn)行隱式關(guān)聯(lián)的一個(gè)對(duì)象豪椿,被稱為函數(shù)上下文奔坟。它依賴于函數(shù)的調(diào)用方式,因此搭盾,this又稱為調(diào)用上下文咳秉。

下面的代碼中展示了作為函數(shù)調(diào)用和作為方法調(diào)用的區(qū)別:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函數(shù)調(diào)用和方法調(diào)用的區(qū)別</title>
    <style>
        /*定義結(jié)果樣式*/
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <!--顯示測(cè)試結(jié)果-->
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }
        
        function creep(){return this;}
        assert(creep() === window, "Creeping in the window");

        var sneak = creep; // 創(chuàng)建變量引用creep
        assert(sneak() === window, "Sneaking in the window");

        var ninja1 = {
            skulk: creep // 創(chuàng)建屬性引用creep
        };
        assert(ninja1.skulk() === ninja1, "The 1st ninja is skulking");

        var ninja2 = {
            skulk: creep
        };
        assert(ninja2.skulk() === ninja2, "The 2nd ninja is skulking");
    </script>
</body>
</html>

將函數(shù)作為構(gòu)造器(constructor)進(jìn)行調(diào)用,要在函數(shù)調(diào)用前使用 new 關(guān)鍵字鸯隅。構(gòu)造器調(diào)用時(shí)澜建,會(huì)發(fā)生如下特殊行為:

  • 創(chuàng)建一個(gè)新的空對(duì)象。
  • 傳遞給構(gòu)造器的對(duì)象時(shí) this 參數(shù)滋迈,從而成為構(gòu)造器的函數(shù)上下文霎奢。
  • 如果沒有顯示的返回值,新創(chuàng)建的對(duì)象則作為構(gòu)造器的返回值進(jìn)行返回饼灿。

構(gòu)造器的目的是創(chuàng)建一個(gè)新對(duì)象并對(duì)其進(jìn)行設(shè)置幕侠,然后將其作為構(gòu)造器的返回值進(jìn)行返回。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用構(gòu)造器設(shè)置通用對(duì)象</title>
    <style>
        /*定義結(jié)果樣式*/
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <!--顯示測(cè)試結(jié)果-->
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }

        // 聲明一個(gè)構(gòu)造器碍彭,在函數(shù)上下文對(duì)象上創(chuàng)建一個(gè)skulk屬性晤硕。該屬性方法又返回了上下文自身
        function Ninja(){
            this.skulk = function(){return this;};
        }

        var ninja1 = new Ninja();
        var ninja2 = new Ninja();

        assert(ninja1.skulk() === ninja1, "The 1st ninja is skulking");
        assert(ninja2.skulk() === ninja2, "The 1st ninja is skulking");
    </script>
</body>
</html>

函數(shù)和方法的命名通常以動(dòng)詞開頭,來描述它們所做的事情庇忌,并以小寫字母開頭舞箍。而構(gòu)造器的命名通常是由一個(gè)描述所構(gòu)造對(duì)象的名詞來命名,并以大寫字母開頭皆疹。

JavaScript 的每個(gè)函數(shù)都有 apply()call()方法疏橄,使用任何一個(gè)方法,都可以顯式指定任何一個(gè)對(duì)象作為其函數(shù)上下文略就。

函數(shù)作為第一型對(duì)象捎迫,可以向其它任何類型的對(duì)象一樣,擁有屬性和方法表牢。

  • apply():接收兩個(gè)參數(shù)窄绒,一個(gè)是作為函數(shù)上下文的對(duì)象,另一個(gè)是作為函數(shù)參數(shù)所組成的數(shù)組崔兴。例如:f.apply(o,[1,2,3]);
  • call():接收的參數(shù)包括作為函數(shù)上下文的對(duì)象和一個(gè)參數(shù)列表而不是單個(gè)數(shù)組彰导。例如:f.call(o,1,2,3);
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用apply()和call()指定函數(shù)上下文</title>
    <style>
        /*定義結(jié)果樣式*/
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <!--顯示測(cè)試結(jié)果-->
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }
        
      function juggle(){
          var result = 0;
          for(var n=0; n<arguments.length; n++){
              result += arguments[n];
          }
          this.result = result; // 在上下文保存結(jié)果
      }

          var ninja1 = {};
          var ninja2 = {};
          
          juggle.apply(ninja1,[1,2,3,4]);
          juggle.call(ninja2,5,6,7,8);
          
          assert(ninja1.result === 10, "juggled via apply");
          assert(ninja2.result === 26, "juggled via call");
    </script>
</body>
</html>

函數(shù)式編程和命令式編程的區(qū)別在于思維層面:函數(shù)式程序的構(gòu)建塊而不是命令式語句蛔翅。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>構(gòu)建for-each函數(shù)演示函數(shù)上下文功能</title>
    <style>
        /*定義結(jié)果樣式*/
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <!--顯示測(cè)試結(jié)果-->
    <ul id="results"></ul>

    <script>
        // 定義assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }
        
      function forEach(list,callback){
          for(var n=0; n<list.length; n++){
              callback.call(list[n],n);
          }
      }

      // 創(chuàng)建測(cè)試對(duì)象
      var weapons = ['shuriken','katana','nunchuncks'];

      forEach(weapons,function(index){
          assert(this == weapons[index], "Got the expected value of " + weapons[index]);
      });
    </script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市位谋,隨后出現(xiàn)的幾起案子山析,更是在濱河造成了極大的恐慌,老刑警劉巖倔幼,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盖腿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡损同,警方通過查閱死者的電腦和手機(jī)翩腐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膏燃,“玉大人茂卦,你說我怎么就攤上這事∽榱ǎ” “怎么了等龙?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伶贰。 經(jīng)常有香客問我蛛砰,道長(zhǎng),這世上最難降的妖魔是什么黍衙? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任泥畅,我火速辦了婚禮,結(jié)果婚禮上琅翻,老公的妹妹穿的比我還像新娘位仁。我一直安慰自己,他們只是感情好方椎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布聂抢。 她就那樣靜靜地躺著,像睡著了一般棠众。 火紅的嫁衣襯著肌膚如雪琳疏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天闸拿,我揣著相機(jī)與錄音轿亮,去河邊找鬼。 笑死胸墙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的按咒。 我是一名探鬼主播迟隅,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼但骨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了智袭?” 一聲冷哼從身側(cè)響起奔缠,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吼野,沒想到半個(gè)月后校哎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞳步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年闷哆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单起。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抱怔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘀倒,到底是詐尸還是另有隱情屈留,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布测蘑,位于F島的核電站灌危,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏碳胳。R本人自食惡果不足惜勇蝙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望固逗。 院中可真熱鬧浅蚪,春花似錦、人聲如沸烫罩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贝攒。三九已至盗誊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隘弊,已是汗流浹背哈踱。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梨熙,地道東北人开镣。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像咽扇,于是被迫代替她去往敵國(guó)和親邪财。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陕壹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容