你不知道的 JSON.stringify() 的威力

前言

其實有很多有用的東西刨沦,當時學習了倍权,也記住了,但是時間久了就是記不住囱修,所以導致在日常開發(fā)中總是想不起來原來這個東西可以這么用赎瑰,而去選擇了更加復雜和麻煩的方式。其實我們?nèi)粘W習的知識就是拿來用的破镰,即使你今天把知識點背下來了餐曼,沒有去思考這個知識點我們可以用來干嘛,不需要幾天就會慢慢地忘掉鲜漩。所以今天我們來了解一下在日常學習時你遺漏掉或者忘掉或者沒有思考過的你不知道的 JSON.stringify() 的威力源譬。

通過需求學習JSON.stringify()

首先我們在開發(fā)的過程當中遇到這樣一個處理數(shù)據(jù)的需求

const todayILearn = {
  _id: 1,
  content: '今天學習 JSON.stringify(),我很開心孕似!',
  created_at: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
  updated_at: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)'
}

我們需要將上面這個對象處理成下面這個對象

const todayILearn = {
  id: 1,
  content: '今天學習 JSON.stringify()瓶佳,我很開心!',
  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)'
}

也就是在不改變屬性的值的前提下鳞青,將對象屬性修改一下霸饲。 把_id 改成 id,把 updated_at 改成 updatedAt臂拓,把 created_at 改成 createdAt厚脉。我們現(xiàn)在通過這個小小的需求來見識一下 JSON.stringify() 的強大吧。

首先要解決這個問題我們有很多的解決方式胶惰,我們先提供兩種不優(yōu)雅的解決方案:

  • 方案一:一次遍歷+多聲明一個變量
// 多一個變量存儲
const todayILearnTemp = {};
for (const [key, value] of Object.entries(todayILearn)) {
  if (key === "_id") todayILearnTemp["id"] = value;
  else if (key === "created_at") todayILearnTemp["createdAt"] = value;
  else if (key === "updatedAt") todayILearnTemp["updatedAt"] = value;
  else todayILearnTemp[key] = value;
}
console.log(todayILearnTemp);
// 結(jié)果:
// { id: 1,
//  content: '今天學習 JSON.stringify()傻工,我很開心!',
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
//  updated_at: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)' 
// }

方案一完全沒有問題可以實現(xiàn)孵滞。但是多聲明了一個變量又加上一層循環(huán)并且還有很多的 if else 語句中捆,怎么都顯得不太優(yōu)雅。

  • 方案二:暴力 delete 屬性和增加屬性
// 極致的暴力美學
todayILearn.id = todayILearn._id;
todayILearn.createdAt = todayILearn.created_at;
todayILearn.updatedAt = todayILearn.updated_at;
delete todayILearn._id;
delete todayILearn.created_at;
delete todayILearn.updated_at;
console.log(todayILearn);
//  太暴力??
//{ 
//  content: '今天學習 JSON.stringify()坊饶,我很開心泄伪!',
//  id: 1,
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
//  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)' 
//}

直接 delete 暴力解決太粗魯了,而且有一個缺點匿级,屬性的順序變了蟋滴。

  • 方案三:序列化+ replace 美學典范
const mapObj = {
  _id: "id",
  created_at: "createdAt",
  updated_at: "updatedAt"
};
JSON.parse(
  JSON.stringify(todayILearn).replace(
    /_id|created_at|updated_at/gi,
    matched => mapObj[matched])
    )
// { 
// id: 1,
//  content: '今天學習 JSON.stringify(),我很開心痘绎!',
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
//  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)' 
// }

瞬間感覺非常優(yōu)雅和舒服津函,有木有!如果你這樣寫孤页,你導師給你 review 代碼的時候尔苦,你導師肯定會夸贊你的??。

接下來行施,正片開始允坚,我們今天將系統(tǒng)的學習或者說是復習一遍 JSON.stringify 的基礎(chǔ)知識,讓我們在日常開發(fā)中更加的游刃有余悲龟。


溫故知新之非常簡單的 JSON.stringify() 九大特性

JSON.stringify()第一大特性

對于 undefined屋讶、任意的函數(shù)以及 symbol 三個特殊的值分別作為對象屬性的值、數(shù)組元素须教、單獨的值時 JSON.stringify()將返回不同的結(jié)果皿渗。

首先,我們來復習一下知識點轻腺,看一道非常簡單的面試題目:請問下面代碼會輸出什么乐疆?

const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  }
};
JSON.stringify(data); // 輸出:?

// "{"a":"aaa"}"

很簡單這道題目面試官主要考察的知識點是:

  • undefined贬养、任意的函數(shù)以及 symbol 作為對象屬性值時 JSON.stringify() 跳過(忽略)對它們進行序列化

面試官追問:假設(shè) undefined挤土、任意的函數(shù)以及 symbol 值作為數(shù)組元素會是怎樣呢?

JSON.stringify(["aaa", undefined, function aa() {
    return true
  }, Symbol('dd')])  // 輸出:误算?

// "["aaa",null,null,null]"

知識點是:

  • undefined仰美、任意的函數(shù)以及 symbol 作為數(shù)組元素值時迷殿,JSON.stringify() 將會將它們序列化為 null

我們再發(fā)揮動下腦筋,如果單獨序列化這些值會是什么樣的結(jié)果呢咖杂?

JSON.stringify(function a (){console.log('a')})
// undefined
JSON.stringify(undefined)
// undefined
JSON.stringify(Symbol('dd'))
// undefined

單獨轉(zhuǎn)換的結(jié)果就是:

  • undefined庆寺、任意的函數(shù)以及 symbolJSON.stringify() 作為單獨的值進行序列化時都會返回 undefined

JSON.stringify() 第一大特性總結(jié)

  • undefined、任意的函數(shù)以及 symbol 作為對象屬性值時 JSON.stringify() 對跳過(忽略)它們進行序列化

  • undefined诉字、任意的函數(shù)以及 symbol 作為數(shù)組元素值時懦尝,JSON.stringify() 將會將它們序列化為 null

  • undefined、任意的函數(shù)以及 symbolJSON.stringify() 作為單獨的值進行序列化時壤圃,都會返回 undefined

JSON.stringify() 第二大特性

也是在使用過程中必須要非常注意的一個點:

  • 非數(shù)組對象的屬性不能保證以特定的順序出現(xiàn)在序列化后的字符串中陵霉。
const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  },
  d: "ddd"
};
JSON.stringify(data); // 輸出:?
// "{"a":"aaa","d":"ddd"}"

JSON.stringify(["aaa", undefined, function aa() {
    return true
  }, Symbol('dd'),"eee"])  // 輸出:伍绳?

// "["aaa",null,null,null,"eee"]"

正如我們在第一特性所說踊挠,JSON.stringify() 序列化時會忽略一些特殊的值,所以不能保證序列化后的字符串還是以特定的順序出現(xiàn)(數(shù)組除外)墨叛。

JSON.stringify()` 第三大特性

  • 轉(zhuǎn)換值如果有 toJSON() 函數(shù)止毕,該函數(shù)返回什么值,序列化結(jié)果就是什么值漠趁,并且忽略其他屬性的值扁凛。
JSON.stringify({
    say: "hello JSON.stringify",
    toJSON: function() {
      return "today i learn";
    }
  })
// "today i learn"

JSON.stringify()第四大特性

  • JSON.stringify() 將會正常序列化 Date 的值。
JSON.stringify({ now: new Date() });
// "{"now":"2019-12-08T07:42:11.973Z"}"

實際上 Date 對象自己部署了 toJSON() 方法(同Date.toISOString())闯传,因此 Date 對象會被當做字符串處理谨朝。

JSON.stringify() 第五大特性

  • NaNInfinity 格式的數(shù)值及 null 都會被當做 null

直接上代碼:

JSON.stringify(NaN)
// "null"
JSON.stringify(null)
// "null"
JSON.stringify(Infinity)
// "null"

JSON.stringify() 第六大特性

關(guān)于基本類型的序列化:

  • 布爾值甥绿、數(shù)字字币、字符串的包裝對象在序列化過程中會自動轉(zhuǎn)換成對應的原始值。
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// "[1,"false",false]"

JSON.stringify() 第七大特性

關(guān)于對象屬性的是否可枚舉:

  • 其他類型的對象共缕,包括 Map/Set/WeakMap/WeakSet洗出,僅會序列化可枚舉的屬性。
// 不可枚舉的屬性默認會被忽略:
JSON.stringify( 
    Object.create(
        null, 
        { 
            x: { value: 'json', enumerable: false }, 
            y: { value: 'stringify', enumerable: true } 
        }
    )
);
// "{"y","stringify"}"

JSON.stringify() 第八大特性

我們都知道實現(xiàn)深拷貝最簡單粗暴的方式就是序列化:JSON.parse(JSON.stringify())图谷,這個方式實現(xiàn)深拷貝會因為序列化的諸多特性導致諸多的坑點:比如現(xiàn)在我們要說的循環(huán)引用問題翩活。

// 對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法便贵,會拋出錯誤菠镇。 
const obj = {
  name: "loopObj"
};
const loopObj = {
  obj
};
// 對象之間形成循環(huán)引用,形成閉環(huán)
obj.loopObj = loopObj;
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}
deepClone(obj)
/**
 VM44:9 Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'loopObj' -> object with constructor 'Object'
    --- property 'obj' closes the circle
    at JSON.stringify (<anonymous>)
    at deepClone (<anonymous>:9:26)
    at <anonymous>:11:13
 */

  • 對包含循環(huán)引用的對象(對象之間相互引用承璃,形成無限循環(huán))執(zhí)行此方法利耍,會拋出錯誤。

這也就是為什么用序列化去實現(xiàn)深拷貝時,遇到循環(huán)引用的對象會拋出錯誤的原因隘梨。

JSON.stringify() 第九大特性

最后程癌,關(guān)于 symbol 屬性還有一點要說的就是:

  • 所有以 symbol 為屬性鍵的屬性都會被完全忽略掉,即便 replacer 參數(shù)中強制指定包含了它們出嘹。
JSON.stringify({ [Symbol.for("json")]: "stringify" }, function(k, v) {
    if (typeof k === "symbol") {
      return v;
    }
  })

// undefined

關(guān)于 replacer 是什么呢席楚,它是 JSON.stringify() 的第二個參數(shù),我們比較少的會用到税稼,所以很多時候我們會忘記 JSON.stringify() 第二個、第三個參數(shù)垮斯,場景不多郎仆,但是用的好的話會非常的方便,關(guān)于 JSON.stringify() 第九大特性的例子中對 replacer 參數(shù)不明白的同學先別急兜蠕,其實很簡單扰肌,我們馬上就會在下面的學習中弄懂。

枕典席文之 JSON.stringify() 第二個參數(shù)和第三個參數(shù)

強大的第二個參數(shù) replacer

replacer 參數(shù)有兩種形式熊杨,可以是一個函數(shù)或者一個數(shù)組曙旭。作為函數(shù)時,它有兩個參數(shù)晶府,鍵(key)和值(value)桂躏,函數(shù)類似就是數(shù)組方法 mapfilter 等方法的回調(diào)函數(shù)川陆,對每一個屬性值都會執(zhí)行一次該函數(shù)剂习。如果 replacer 是一個數(shù)組,數(shù)組的值代表將被序列化成 JSON 字符串的屬性名较沪。

replacer 作為函數(shù)時

可以打破九大特性的大多數(shù)特性

第二個參數(shù)replacer 非常強大鳞绕, replacer 作為函數(shù)時,我們可以打破九大特性的大多數(shù)特性尸曼,我們直接來看代碼吧们何。

const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  }
};
// 不用 replacer 參數(shù)時
JSON.stringify(data); 

// "{"a":"aaa"}"
// 使用 replacer 參數(shù)作為函數(shù)時
JSON.stringify(data, (key, value) => {
  switch (true) {
    case typeof value === "undefined":
      return "undefined";
    case typeof value === "symbol":
      return value.toString();
    case typeof value === "function":
      return value.toString();
    default:
      break;
  }
  return value;
})
// "{"a":"aaa","b":"undefined","c":"Symbol(dd)","fn":"function() {\n    return true;\n  }"}"

雖然使用 toString() 方法有點耍流氓的意思但是不得不說第二個參數(shù)很強大。

傳入 replacer 函數(shù)的第一個參數(shù)

需要注意的是控轿,replacer 被傳入的函數(shù)時冤竹,第一個參數(shù)不是對象的第一個鍵值對镀迂,而是空字符串作為 key 值废岂,value 值是整個對象的鍵值對:

const data = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
JSON.stringify(data, (key, value) => {
  console.log(value);
  return value;
})
// 第一個被傳入 replacer 函數(shù)的是 {"":{a: 2, b: 3, c: 4, d: 5}}
// {a: 2, b: 3, c: 4, d: 5}   
// 2
// 3
// 4
// 5


實現(xiàn) map 函數(shù)

我們還可以用它來手寫實現(xiàn)一個對象的類似 map 的函數(shù)。

// 實現(xiàn)一個 map 函數(shù)
const data = {
  a: 2,
  b: 3,
  c: 4,
  d: 5
};
const objMap = (obj, fn) => {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function !`);
  }
  return JSON.parse(JSON.stringify(obj, fn));
};
objMap(data, (key, value) => {
  if (value % 2 === 0) {
    return value / 2;
  }
  return value;
});
// {a: 1, b: 3, c: 2, d: 5}

replacer 作為數(shù)組時

replacer 作為數(shù)組時红且,結(jié)果非常簡單躲株,數(shù)組的值就代表了將被序列化成 JSON 字符串的屬性名片部。

const jsonObj = {
  name: "JSON.stringify",
  params: "obj,replacer,space"
};

// 只保留 params 屬性的值
JSON.stringify(jsonObj, ["params"]);
// "{"params":"obj,replacer,space"}" 

有意思卻沒啥用的第三個參數(shù) space

space 參數(shù)用來控制結(jié)果字符串里面的間距。首先看一個例子就是到這東西到底是干啥用的:

const tiedan = {
  name: "彈鐵蛋同學",
  describe: "今天在學 JSON.stringify()",
  emotion: "like shit"
};
JSON.stringify(tiedan, null, "??");
// 接下來是輸出結(jié)果
// "{
// ??"name": "彈鐵蛋同學",
// ??"describe": "今天在學 JSON.stringify()",
// ??"emotion": "like shit"
// }"
JSON.stringify(tiedan, null, 2);
// "{
//   "name": "彈鐵蛋同學",
//   "describe": "今天在學 JSON.stringify()",
//   "emotion": "like shit"
// }"

上面代碼一眼就能看出第三個參數(shù)的作用了,花里胡哨的档悠,其實這個參數(shù)還是比較雞肋的廊鸥,除了好看沒啥特別的用處。我們用 \t辖所、 \n 等縮進能讓輸出更加格式化惰说,更適于觀看。

  • 如果是一個數(shù)字, 則在字符串化時每一級別會比上一級別縮進多這個數(shù)字值的空格(最多10個空格)缘回;

  • 如果是一個字符串吆视,則每一級別會比上一級別多縮進該字符串(或該字符串的前10個字符)。

總結(jié)

JSON.stringify() 九大特性:

一酥宴、對于 undefined啦吧、任意的函數(shù)以及 symbol 三個特殊的值分別作為對象屬性的值、數(shù)組元素拙寡、單獨的值時的不同返回結(jié)果授滓。

  • undefined、任意的函數(shù)以及 symbol 作為對象屬性值時 JSON.stringify() 對跳過(忽略)它們進行序列化

  • undefined肆糕、任意的函數(shù)以及 symbol 作為數(shù)組元素值時般堆,JSON.stringify() 將會將它們序列化為 null

  • undefined、任意的函數(shù)以及 symbolJSON.stringify() 作為單獨的值進行序列化時都會返回 undefined

二诚啃、非數(shù)組對象的屬性不能保證以特定的順序出現(xiàn)在序列化后的字符串中淮摔。

三、轉(zhuǎn)換值如果有 toJSON() 函數(shù)绍申,該函數(shù)返回什么值噩咪,序列化結(jié)果就是什么值,并且忽略其他屬性的值极阅。

四胃碾、JSON.stringify() 將會正常序列化 Date 的值。

五筋搏、NaNInfinity 格式的數(shù)值及 null 都會被當做 null仆百。

六、布爾值奔脐、數(shù)字俄周、字符串的包裝對象在序列化過程中會自動轉(zhuǎn)換成對應的原始值。

七髓迎、其他類型的對象峦朗,包括 Map/Set/WeakMap/WeakSet,僅會序列化可枚舉的屬性排龄。

八波势、對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會拋出錯誤尺铣。

九拴曲、所有以 symbol 為屬性鍵的屬性都會被完全忽略掉,即便 replacer 參數(shù)中強制指定包含了它們凛忿。

JSON.stringify() 第二個參數(shù)和第三個參數(shù)

強大的第二個參數(shù):

  • 作為函數(shù)時澈灼,它有兩個參數(shù),鍵(key)和值(value)店溢,函數(shù)類似就是數(shù)組方法 map叁熔、filter 等方法的回調(diào)函數(shù),對每一個屬性值都會執(zhí)行一次該函數(shù)(期間我們還簡單實現(xiàn)過一個 map 函數(shù))床牧。
  • 如果 replacer 是一個數(shù)組者疤,數(shù)組的值代表將被序列化成 JSON 字符串的屬性名。

華麗的第三個參數(shù):

  • 如果是一個數(shù)字, 則在字符串化時每一級別會比上一級別縮進多這個數(shù)字值的空格(最多10個空格)叠赦。

  • 如果是一個字符串,則每一級別會比上一級別多縮進該字符串(或該字符串的前10個字符)革砸。

注意:

第一個例子的方案三除秀,有小伙伴提示說這個方案會有風險,確實是這樣的(可能會把對象的值給替換掉)算利。大家慎用吧册踩,大部分情況下這樣使用是 ok 的。小伙伴們提供的第四種方案還是很不錯的:

const todayILearn = {
  _id: 1,
  content: '今天學習 JSON.stringify()效拭,我很開心暂吉!',
  created_at: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
  updated_at: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)'
}
const mapObj = {
  _id: "id",
  created_at: "createdAt",
  updated_at: "updatedAt"
};
Object.fromEntries(Object.entries(todayILearn).map(([k, v]) => [mapObj[k]||k, v]))
// { 
// id: 1,
//  content: '今天學習 JSON.stringify(),我很開心缎患!',
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中國標準時間)',
//  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中國標準時間)' 
// }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慕的,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挤渔,更是在濱河造成了極大的恐慌肮街,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件判导,死亡現(xiàn)場離奇詭異嫉父,居然都是意外死亡,警方通過查閱死者的電腦和手機眼刃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門绕辖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人擂红,你說我怎么就攤上這事仪际。” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵弟头,是天一觀的道長吩抓。 經(jīng)常有香客問我,道長赴恨,這世上最難降的妖魔是什么疹娶? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮伦连,結(jié)果婚禮上雨饺,老公的妹妹穿的比我還像新娘。我一直安慰自己惑淳,他們只是感情好额港,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歧焦,像睡著了一般移斩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绢馍,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天向瓷,我揣著相機與錄音,去河邊找鬼舰涌。 笑死猖任,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瓷耙。 我是一名探鬼主播朱躺,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼搁痛!你這毒婦竟也來了长搀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤落追,失蹤者是張志新(化名)和其女友劉穎盈滴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轿钠,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巢钓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疗垛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片症汹。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贷腕,靈堂內(nèi)的尸體忽然破棺而出背镇,到底是詐尸還是另有隱情咬展,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布瞒斩,位于F島的核電站破婆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏胸囱。R本人自食惡果不足惜祷舀,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烹笔。 院中可真熱鬧裳扯,春花似錦、人聲如沸谤职。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽允蜈。三九已至冤吨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饶套,已是汗流浹背锅很。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凤跑,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓叛复,卻偏偏與公主長得像仔引,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子褐奥,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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