JS 适记

JS 适记

try/catch/finally 语句

try/catch/finally 语句用于处理代码中可能出现的错误信息。

try语句允许我们定义在执行时进行错误测试的代码块。(里面代码也是会执行的)

catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。

finally 语句在 try 和 catch 之后无论有无异常都会执行

注意: catch 和 finally 语句都是可选的,但你在使用 try 语句时必须至少使用一个。

提示: 当错误发生时, JavaScript 会停止执行,并生成一个错误信息。使用 throw 语句 来创建自定义消息(抛出异常)。如果你将 throwtrycatch一起使用,就可以控制程序输出的错误信息。

语法: try {
       有可能会出现异常的代码
    }catch(e){
       捕获到异常了之后要执行的处理-代码
       e就是异常信息
    }finally {
       不管有没有发生异常,都要执行的代码
       释放资源的处理
    }
/自己定义抛出异常。  throw
function getRes(num){
 if(isNaN(num)){
   //throw "你为什么不传一个数字捏?";
   throw {
     name:"计算错误异常",
     aaa:"你为什么不传一个数字捏?"
   };
 }
 var res = num * 2;
 return res;
}


try {
 var res1 = getRes("abc")
}catch(e) {
 console.log(e);
}

promise

1、promise是什么?

(1)主要用于异步计算
(2)可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
(3)可以在对象之间传递和操作promise,帮助我们处理队列
(4)Promise的出现让我们告别回调函数,写出更优雅的异步代码

2、为什么会有promise?

为了避免界面冻结(任务)
同步:
假设你去了一家饭店,找个位置,叫来服务员,这个时候服务员对你说,对不起我是“同步”服务员,我要服务完这张桌子才能招呼你。那桌客人明明已经吃上了,你只是想要个菜单,这么小的动作,服务员却要你等到别人的一个大动作完成之后,才能再来招呼你,这个便是同步的问题:也就是“顺序交付的工作1234,必须按照1234的顺序完成”。
异步:
则是将耗时很长的A交付的工作交给系统之后,就去继续做B交付的工作,。等到系统完成了前面的工作之后,再通过回调或者事件,继续做A剩下的工作。
AB工作的完成顺序,和交付他们的时间顺序无关,所以叫“异步”。

3、异步操作的常见语法

(1)事件监听
(2)回调

4、有了nodeJS之后...对异步的依赖进一步加剧了

大家都知道在nodeJS出来之前PHP、Java、python等后台语言已经很成熟了,nodejs要想能够有自己的一片天,那就得拿出点自己的绝活:
无阻塞高并发,是nodeJS的招牌,要达到无阻塞高并发异步是其基本保障
举例:查询数据从数据库,PHP第一个任务查询数据,后面有了新任务,那么后面任务会被挂起排队;而nodeJS是第一个任务挂起交给数据库去跑,然后去接待第二个任务交给对应的系统组件去处理挂起,接着去接待第三个任务...那这样子的处理必然要依赖于异步操作

5、异步回调的问题:

、之前处理异步是通过纯粹的回调函数的形式进行处理
、很容易进入到回调地狱中,剥夺了函数return的能力
、问题可以解决,但是难以读懂,维护困难
、稍有不慎就会踏入回调地狱 - 嵌套层次深,不好维护
、一般情况我们一次性调用API就可以完成请求。
、有些情况需要多次调用服务器API,就会形成一个链式调用,比如为了完成一个功能,我们需要调用API1、API2、API3,依次按照顺序进行调用,这个时候就会出现回调地狱的问题

6、promise的引入

、promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
、并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据
、代码风格,容易理解,便于维护
、多个异步等待合并便于解决

7、promise详解

new Promise(
  function (resolve, reject) {
    setTimeout(function() {
        // 一段耗时的异步操作
        resolve('成功') // 数据处理完成
        // reject('失败') // 数据处理出错
    }, 10000);
  }
).then(
  (res) => {console.log(res)},  // 成功
  (err) => {console.log(err)} // 失败
)
console.log('因为不是异步的,这句会执行比Promise快') //执行顺序1

上面代码console执行顺序:

以为Promise是异步,不影响外面代码执行

因为不是异步的,这句会执行比Promise快
成功

代码解读

resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。
Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。

Async/Await

1、Async/Await简介

async/await是写异步代码的新方式,优于回调函数和Promise。

async/await是基于Promise实现的,它不能用于普通的回调函数。===》(函数前面多了一个 async 关键字。await 关键字只能用在 async 定义的函数内。async 函数会隐式地返回一个 promise,该 promise 的 reosolve 值就是函数 return 的值。)

async/await与Promise一样,是非阻塞的。

async/await使得异步代码看起来像同步代码,再也没有回调函数。但是改变不了JS单线程、异步的本质。

2、Async/Await的用法

使用await,函数必须用async标识

await后面跟的是一个Promise实例

function getTimeout(time) {
        return new Promise(function(yes, no) {
           setTimeout(() => {
              yes('传哥');
           }, time);
        });
    }

    // 上面使用promise解决了异步回调嵌套不好维护的问题,
    // 但是我们还有更好方式书写上面的代码
    // async函数相当于使用同步的方式书写异步代码
    async function fn() {
        // await会等待promise成功,成功后在会向下走,直到遇到下一个await
        let a = await getTimeout(5000);
        console.log(`第一个定时器执行完毕了__${a}`);
        await getTimeout(3000);
        console.log('第二个定时器执行完毕了');
        console.log('加多少代码都可以');
        console.log('加多少代码都可以');
        await getTimeout(1000);
        console.log('第二个定时器执行完毕了');
    }

    fn();

当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

3、为什么 Async/Await 更好?

1. 简洁
由示例可知,使用 Async/Await 明显节约了不少代码。我们不需要写.then,不需要写匿名函数处理 Promise 的 resolve 值,也不需要定义多余的 data 变量,还避免了嵌套代码。这些小的优点会迅速累计起来,这在之后的代码示例中会更加明显。

2. 错误处理
Async/Await 让 try/catch 可以同时处理同步和异步错误。在下面的 promise 示例中,try/catch 不能处理 JSON.parse 的错误,因为它在 Promise 中。我们需要使用.catch,这样错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂。
使用 async/await 的话,catch 能处理 JSON.parse 错误:
const makeRequest = async () => {
    try {
        // this parse may fail
        const data = JSON.parse(await getJSON());
        console.log(data);
    } catch (err) {
        console.log(err);
    }
};

数组与字符串的转换

数组转字符串
(1) var arr = ['a', 'b', 'c']
​           arr.join('-'); // a-b-c
​           arr.join(''); //abc
字符串转数组
(1) var str = 'ab+cd+e'
​           str.split('+') // ['ab', 'cd', 'e']
​           str.split('') // ['a', 'b', '+', 'c', 'd', '+', 'e']

判断语句中转换为false

在js中if条件为null/undefined/0/NaN/""表达式时,统统被解释为false,此外均为true哦

String的常用方法

1》ES6提供三种检索方法(注意不兼容IE)

- **includes()**:返回布尔值,表示是否找到了参数字符串。
- **startsWith()**:返回布尔值,表示参数字符串是否在原字符串的头部
- **endsWith()**:返回布尔值,表示参数字符串是否在原字符串的尾部。
  let s = 'Hello world!';
  s.startsWith('Hello') *// true*
  s.endsWith('!') *// true*
  s.includes('o') *// true

2》indexOf() 检索 返回第一次出现的位置,没有则返回 -1
var str="Hello world!"
str.indexOf("Hello") // 0
str.indexOf("WorldPP") // -1
str.indexOf("world") // 6

3》lastIndexOf()方法返回从右向左出现某个字符或字符串的首个字符索引值(与indexOf相反)
  var src="images/off_1.png";
  src.lastIndexOf('/'); //6
  src.lastIndexOf('g'); //15
  src.lastIndexOf('aaa'); //-1

4》substring(start,end)表示从start到end之间的字符串,包括start位置的字符但是不包括end位置的字符。
var src="images/off_1.png";
src.substring(7,10)//off

5》substr(start,length)表示从start位置开始,截取length长度的字符串。
var src="images/off_1.png";
src.substr(7,3)//off

Array的常用方法

1》filter() 过滤筛选。filter把传入的函数依次作用于每个元素,然后根据返回值是 true 还是false决定保留还是丢弃该元素。三个参数 (当前元素, 索引,  当前数组)
​   var arr = [1, 5, 6]
​   var r = arr.filter((item,index, self) => {return item >= 5 ?  false :  true;}) 
    // r = [5, 6]
    
2》map() 遍历。不改变源数组,返回一个新的被改变过值之后的数组(map需return)。三个参数 (当前元素, 索引,  当前数组)实际上对数组的每个元素都遍历一次,同时返回一个新的值。记住一点是返回的这个数据的长度和原始数组长度是一致的。
​   var arr1 = [1, 2, 3]
​   var arr2 = arr1.map((item,index, self) => {return  item * item}) 
    // arr2 = [1, 4, 9]   ;  arr1 =  [1, 2, 3]
    
3》some()方法会依次执行数组的每个元素:
如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
    var arr1 = [1, 2, 3]
​   arr1.some((item,index, self) => item = == 2) //true

4》every()方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
如果所有元素都满足条件,则返回 true。
var arr = [1, 2, 3]
​   arr.every((item,index, self) => item > 0) //true
    arr.every((item,index, self) => item > 2) //false
    
5》unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
var arr= ['AA','BB']
arr.unshift('MM')//arr= ['MM','AA','BB']
arr.unshift('KK','HH')//arr= ['KK','HH','MM','AA','BB']

6》push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度
var arr= ['AA','BB']
arr.push('MM')//arr= ['AA','BB','MM']

7》find() 方法,用于找出第一个符合条件的数组成员值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined。(注意返回 undefined情況)
[1, 4, -5, 10].find((value, index, arr) => value < 0)// -5
[1, 4, -5, 10].find((n) => n < -6)// undefined

8》findIndex () 方法,find 方法非常类似,返回第一个符合条件的数组成员的 位置 ,如果所有成员都不符合条件,则返回-1。 两个方法都可以发现 NaN,弥补了数组的 IndexOf 方法的不足
[1, 5, 10, 15].findIndex((value, index, arr) => value > 9)//2
[1, 5, 10, 15].findIndex((value, index, arr) => value > 16)//-1

遍历

1》for循环,需要知道数组的长度,才能遍历(使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显)
​   for (j = 0,len=arr.length; j < len; j++) {
​   }

2》遍历对象, for (属性) in (对象)
​   let obj ={a:'2', b:3, c:true};
​       for (var i in obj) {
​       console.log(obj[i],i)  // 2, a
​   }

for--in 也可遍历数组 (i)为下标, 缺陷1. index索引为字符串型数字,不能直接进行几何运算。2. 遍历顺序有可能不是按照实际数组的内部顺序。 3.使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗的原型方法method和name属性
所以for in更适合遍历对象,不要使用for in遍历数组。(for -- of --可以解决这样的缺陷)
​   var arr = [7, 8, 9]
​   for (var a in arr) {
​       console.log(arr[a])  // 7, 8, 9
​   }

3》for --of --  可以与break、continue和return配合使用。(for 当前元素 of 需要遍历的数组)
​   var arr = [‘b’, 8, 9]
​   for (var a of arr) {
​       console.log(a)  // b, 8, 9
​   }

小总结: 
1、map()速度比foreach()快 
2、map():返回一个新的Array,不对原数组产生影响,每个元素为调用func的结果。 
3、foreach()不会产生新数组,没有返回值,只是针对每个元素调用func ,不能使用break语句中断循环,也不能使用      return语句返回到外层函数
3、map()因为返回数组所以可以链式操作,foreach不能 
4、map()要比foreach()执行速度更快 
5、filter()返回一个符合func条件的元素数组 
6、some()返回一个boolean,判断是否有元素是否符合func条件 
7、every()返回一个boolean,判断每个元素是否符合func条件

构造函数

1.构造函数介绍
    a.构造函数命名使用一个名字,首字母大写。
    b.用new关键字配合使用。
    c.构造函数首先也是一个函数,只不过这个函数用来创建对象并且初始化对象。
    d.自动返回值。
  
2.构造函数执行过程。
    a.创建了一个对象
    b.让this指向new创建出来的对象。
    c.给这个对象的属性赋值、初始化
    d.自动返回那个创建出来的对象。

function Person(name,age){
    this.name = name;
    this.age = age;
    console.log(this);
}
var p1 = new Person("某敏",18);
console.log(p1); //{name: "某敏", age: 18}

3.注意
    a.构造函数不需要返回值,会自动返回创建的那个对象,但是如果真的给构造函数设置了返回值,会怎么样?
    如果是返回的简单的数据类型,会忽略;  如果返回的是复杂的数据类型,会把创建出来的那个对象给覆盖掉。

    b.构造函数首先也是一个函数,既然是函数,就可以不使用new关键配合着调用,而是直接调用。
    如果把构造函数当成普通的函数来调用,那这个构造函数中的this就是window。
    如果把构造函数当成普通的函数来调用,那他的返回值就是默认的返回值undefined。
    var pp = Person("琳琳",20);//没有使用new 关键字
    console.log(pp); //undefined
    console.log(window);

引入原型

1.原因:假如构造函数里有个sayHi方法,而这个方法是一样的,代码冗余。
    function Person(name,age){
        this.name = name;
        this.age = age;
        this.sayHi = function () {
            console.log("你好,我的名字是"+this.name);
        }
    }
    var p1 = new Person("某敏",18);
    var p2 = new Person("某婷",19);
    p1.sayHi();
    p2.sayHi();
    console.log(p1.sayHi === p2.sayHi); //false 验证了sayhi p1/p2不是同一个  所以代码冗余

2.如何将sayHi方法变成公共的? 存构造函数的原型- prototype里。
  原型其实就是一个对象,创建任意的构造函数的时候,系统都会帮我们自动的创建一个和这个构造函数关联的对象,这个对   象就是原型。
  function Person(name,age){
    this.name = name;
    this.age = age;
  }
  Person.prototype.sayHi = function () {
    console.log("你好,我的名字是"+this.name);
  }
  Person.prototype.type = "人类";
  //2.通过构造函数创建出来的对象,都可以访问这个构造函数对应的原型中的成员。
  var p1 = new Person("某敏",18);
  var p2 = new Person("某林林",38);
  console.log(p1.type +":"+ p2.type); //人类:人类
  console.log(p1.sayHi === p2.sayHi); //true
  console.log(p1.sayHi === Person.prototype.sayHi); //true
  
3.使用原型要注意的地方
    a.对象访问成员的规则: 是先看自己有没有这个成员,如果没有才去看原型中有没有。
        function Dog(type,name){
          this.type = type;
          this.name = name;
          this.bark = function () {
           console.log("嘿嘿嘿");
          }
        }
        //不管是那个狗,都可以叫,所以这种公共的方法,就可以写在原型中。
        Dog.prototype.bark = function () {
          console.log("汪汪汪...我的名字是"+this.name);
        }
        var d1 = new Dog("二哈","小2");
        d1.bark(); // 嘿嘿嘿
    
    b.原型中添加成员,就必须使用  构造函数.prototype  这种方式。
        function Dog(pinZhong,name){
          this.pinZhong = pinZhong;
          this.name = name;
        }
        //不管是那个狗,都可以叫,所以这种公共的方法,就可以写在原型中。
        Dog.prototype.bark = function () {
          console.log("汪汪汪...我的名字是"+this.name);
        }
        var d1 = new Dog("二哈","小2");
        var d2 = new Dog("泰迪","泰日天");
        d1.type = "狗"; //这是在给d1对象添加属性。
        console.log(d2.type); //undefined
        
    c.修改原型中的成员,也必须使用  构造函数.prototype  这种方式。
    
    d.如果修改原型(把原型指向另外的一个对象);
      根据构造函数创建出来的对象,访问原型中的成员,要注意区分这个对象是 修改原型之前创建的,还是修改原型之后       创建的。(结果会不同)
          function Dog(pinZhong,name){
            this.pinZhong = pinZhong;
            this.name = name;
          }
          //不管是那个狗,都可以叫,所以这种公共的方法,就可以写在原型中。
          Dog.prototype.bark = function () {
            console.log("汪汪汪...我的名字是"+this.name);
          }
          var d1 = new Dog("二哈","小2");
          d1.bark(); //汪汪汪...我的名字是小2

          //修改Dog对相应的原型
          Dog.prototype = {
            bark: function () {
              console.log("哈哈哈");
            }
          }
          var d3 = new Dog("中华田园犬","旺财");
          d3.bark(); //哈哈哈

静态成员和实例成员

//静态成员:就是构造函数点出来的东西。
//实例成员:就是实例化对象点出来的东西。
  function Person(name){
    this.name = name;
  }
  Person.prototype.sayHi = function () {
    console.log("哈哈哈哈....");
  }

  //实例化对象
  var p1 = new Person("张三");
  p1.sayHi(); //这里的sayHi是实例化对象p1点出来的,所以这里的sayHi就是实例成员。

  //把Person构造函数当成是一个对象的话,往这个对象中添加方法。
  Person.sayHi = function () {
    console.log("嘿嘿额黑...构造函数当成了对象,添加的方法");
  }

  Person.sayHi(); //这里的sayHi是构造函数作为对象点出来的,所以这里的sayHi就是静态成员。

递归

递归 ----》函数自己调用自己 (也有结束的时候)
    var i = 0;
    function test1(){
        i++;
        console.log(i);
        console.log('哈哈');
        if(i < 5){
            test1();
        }
    }
    test1();

回调函数

回调函数----》 一个函数是参数是个函数
作用----》简化代码

例子:2个参数,一个参数做了平方,一个参数加了10, 此时就有2个结果
通常我们要这样做
   function getSum(n1,n2){
     var res1 = n1 * n1;
     var res2 = n2 + 10;
     return res1 + res2;//不同的地方
   }

   function getSub(n1,n2){
     var res1 = n1 * n1;
     var res2 = n2 + 10;
     return  res1 - res2;//不同的地方
   }
  -----》发现代码冗余
  
  ---以下用回调函数方法
    function sum(num1,num2){
      return num1 + num2;
    }

    function sub(num1,num2){
      return num1 - num2;
    }

    function getRes(n1,n2, fun){
      var res1 = n1 * n1; //100
      var res2 = n2 + 10; //60
      if(f1 instanceof  Function){
        return fun(res1,res2);
      }
    }
    console.log(getRes(10, 50,  )); // 160 做加法
    console.log(getRes(10, 50, sub)); // 40 做减法
    

匿名函数 --》自调用函数

    //1.匿名函数 - 没有名字的函数
    //  function (){
    //    console.log("哈哈");
    //  }

    //2.匿名函数没有名字,也就意味着 ,匿名声明出来之后,没有办法用名字的方式来调用。
    //如何来调用匿名函数呢?  自己调用自己


    //3.如果一个匿名函数自己调用自己,就是自调用函数。
    //3.1
     (function (){
       console.log("哈哈");
     })();

    //3.2 推荐使用
    (function (){
      console.log("嘿嘿");
    }());


    //4.自调用函数有什么用?
    //a.如果一个函数声明出来仅仅只会被调用一次,就可以写一个自调用函数。
    //b.js中只有函数才能分割作用域, 可以用自调用函数分割作用域。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容