函數(shù)式編程

現(xiàn)在大公司的編程方式有:

1.oop(面向?qū)ο缶幊?;

2.aop(面向切面編程);

3.函數(shù)式編程(JavaScript Functional Programming);

范疇論Category Theory

  1. 函數(shù)式編程是范疇論的數(shù)學(xué)分支是一門很復(fù)雜的數(shù)學(xué),認(rèn)為世界上所有概念體系都可以抽象出一個(gè)個(gè)范疇
  2. 彼此之間存在某種關(guān)系概念劲赠、事物矩肩、對(duì)象等等,都構(gòu)成范疇愧捕。任何事物只要找出他們之間的關(guān)系奢驯,就能定義
  3. 箭頭表示范疇成員之間的關(guān)系,正式的名稱叫做“態(tài)射”(morphism)次绘。范疇論認(rèn)為瘪阁,同一個(gè)范疇的所有成員,就是不同狀態(tài)的“變形”(transformation)邮偎。通過(guò)“態(tài)射”管跺,一個(gè)成員可以變形成另一個(gè)成員

函數(shù)式編程5大特點(diǎn)

  1. 函數(shù)是第一等公民
  2. 只用表達(dá)式,不用語(yǔ)句
  3. 沒(méi)有副作用
  4. 不修改狀態(tài)
  5. 引用透明(函數(shù)運(yùn)行只靠參數(shù))

專業(yè)術(shù)語(yǔ)

  1. 純函數(shù)
  2. 函數(shù)的柯里化
  3. 函數(shù)組合
  4. Point Free
  5. 聲明式命令式代碼
  6. 惰性求值

純函數(shù)

對(duì)于相同的輸入禾进,永遠(yuǎn)會(huì)得到相同的輸出豁跑,而且沒(méi)有任何可觀察的副作用,也不依賴外部環(huán)境的狀態(tài)泻云。

var xs=[1,2,3,4,5];
//Array.slice是純函數(shù)艇拍,因?yàn)樗鼪](méi)有副作用,對(duì)于固定的輸入宠纯,輸出總是固定的
xs.slice(0,3); //[1,2,3]
xs.slice(0,3);  //[1,2,3]
xs.splice(0,3);   //[1,2,3]
xs.splice(0,3);  //[4,5]
import _ from 'lodash';
var sin=_.memorize(x=>Math.sin(x));
var a=sin(1); //第一次計(jì)算的時(shí)候會(huì)稍慢一點(diǎn)
var b=sin(1); //第二次有了緩存淑倾,速度極快
//純函數(shù)不僅可以有效降低系統(tǒng)的復(fù)雜度,還有很多很棒的特性征椒,比如可緩存性
//惰性函數(shù)

不純

var min=18;
var checkage=function(age){
    return age>min; //依賴于外部的min娇哆,導(dǎo)致不純
}

函數(shù)的柯里化

傳遞給函數(shù)一部分參數(shù)來(lái)調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)。

用柯里化來(lái)改造上面的不純函數(shù)

var checkage=min=>(age=>age>min);
var checkage18=checkage(18);
checkage18(20);

Point Free

  1. 把一些對(duì)象自帶的方法轉(zhuǎn)化成純函數(shù)碍讨,不要命名轉(zhuǎn)瞬即逝的中間變量
  2. 這個(gè)函數(shù)中治力,我們使用了str作為我們的中間變量,但這個(gè)中間變量除了讓代碼變得長(zhǎng)了一點(diǎn)以外是毫無(wú)意義的
    const f=str=>str.toUpperCase().split('')

應(yīng)用

var toUpperCase=word=>word.toUpperCase();
var split=x=>(str=>str.split(x));
var f=compose(split('').toUppercase);
f("abcd efgh");

這種風(fēng)格能夠幫助我們減少不必要的命名勃黍,讓代碼保持簡(jiǎn)潔和通用

聲明式與命令式代碼

命令式代碼的意思就是宵统,我們通過(guò)編寫(xiě)一條又一條指令去讓計(jì)算機(jī)執(zhí)行一些動(dòng)作,這其中一般都會(huì)涉及到很多繁雜的細(xì)節(jié)覆获。而聲明式就要優(yōu)雅很多了马澈,我們通過(guò)寫(xiě)表達(dá)式的方式來(lái)聲明我們想干什么,而不是通過(guò)一步一步的指示弄息。

//命令式
let CEOs=[];
for(var i=0;i<companies.length;i++){
    CEOs.push(companies[i].CEO);
}
//聲明式
let CEOs=companies.map(c=>c.CEO);

優(yōu)缺點(diǎn)

函數(shù)式編程的一個(gè)明顯的好處就是這種聲明式的代碼痊班,對(duì)于無(wú)副作用的純函數(shù),我們完全可以不考慮函數(shù)內(nèi)部是如何實(shí)現(xiàn)的摹量,專注于編寫(xiě)業(yè)務(wù)代碼涤伐。優(yōu)化代碼時(shí),目光只需要集中在這些穩(wěn)定堅(jiān)固的函數(shù)內(nèi)部即可缨称。

相反凝果,不純的函數(shù)式的代碼會(huì)產(chǎn)生副作用或者依賴外部系統(tǒng)環(huán)境,使用他們的時(shí)候總是要考慮這些不干凈的副作用睦尽。在復(fù)雜的系統(tǒng)中器净,這對(duì)于程序員的心智來(lái)說(shuō)是極大的負(fù)擔(dān)。

惰性求值

function fn(){
    if(IE){//IE時(shí)
        fn=a;
    }else{//chrome時(shí)
        fn=b;
    }
    return fn;
}

第一次執(zhí)行時(shí)會(huì)走if当凡,然后fn重新賦值山害,第二次執(zhí)行fn時(shí),直接賦值不用判斷宁玫,提高執(zhí)行效率

高階函數(shù)

函數(shù)當(dāng)參數(shù)粗恢,把傳入的函數(shù)做一個(gè)封裝柑晒,然后返回這個(gè)封裝函數(shù)欧瘪,達(dá)到更高程度的抽象

//命令式
var add=function(a,b){
    return a+b;
};
funtion math(func,array){
    return func(array[0],array[1]);
}
math(add,[1,2]); //3

尾調(diào)用優(yōu)化

指函數(shù)內(nèi)部的最后一個(gè)動(dòng)作是函數(shù)調(diào)用。該調(diào)用的返回值匙赞,直接返回給函數(shù)佛掖。函數(shù)調(diào)用自身,稱為遞歸涌庭。如果尾調(diào)用自身芥被,就稱為尾遞歸。遞歸需要保存大量的調(diào)用記錄坐榆,很容易發(fā)生棧溢出錯(cuò)誤拴魄,如果使用尾遞歸優(yōu)化,將遞歸變?yōu)檠h(huán),那么只需要保存一個(gè)調(diào)用記錄匹中,這樣就不會(huì)發(fā)生棧溢出錯(cuò)誤了夏漱。

//不是尾遞歸,無(wú)法優(yōu)化
function factorial(n){
    if(n===1) return 1;
    return n*factorial(n-1);
}
//尾遞歸
function factorial(n,total){
    if(n===1) return total;
    return factorial(n-1,n*total);
}//ES6強(qiáng)制使用尾遞歸

普通遞歸時(shí)顶捷,內(nèi)存需要記錄調(diào)用的堆棧所出的深度和位置信息挂绰。在最低層計(jì)算返回值,再根據(jù)記錄的信息服赎,跳會(huì)上一層級(jí)計(jì)算葵蒂,然后再跳回到更高一層,依次運(yùn)行重虑,直到最外層的調(diào)用函數(shù)践付。在cpu計(jì)算和內(nèi)存會(huì)消耗很多,而且當(dāng)深度過(guò)大時(shí)嚎尤,會(huì)出現(xiàn)堆棧溢出

function sum(x){
    if(x===1) return 1;
    return x+sum(x-1);
}
sum(5);  //15  遞歸
function sum(x,total){
    if(x===1) return x+total;
    return sum(x-1,x+total);
}
sum(5,0); 
sum(4,5);
sum(3,9);
sum(2,12);
sum(1,14);
15 //尾遞歸荔仁,每次執(zhí)行之后,函數(shù)重新傳入?yún)?shù)芽死,直到結(jié)束

整個(gè)計(jì)算過(guò)程是線性的乏梁,調(diào)用一次sum(x,total)后,會(huì)進(jìn)入下一個(gè)棧关贵,相關(guān)的數(shù)據(jù)信息和跟隨進(jìn)入遇骑,不再放在堆棧上保存。當(dāng)計(jì)算完最后的值之后揖曾,直接返回到最上層的sum(5,0).這能有效的防止堆棧溢出落萎。
在ECMAScript6,我們將迎來(lái)尾遞歸優(yōu)化炭剪,通過(guò)尾遞歸優(yōu)化练链,javascript代碼在解釋成機(jī)器碼的時(shí)候,將會(huì)向while看起奴拦,也就是說(shuō)媒鼓,同時(shí)擁有數(shù)學(xué)表達(dá)能力和while的效能。

閉包

自己領(lǐng)會(huì)

函數(shù)式編程比較火熱的庫(kù)

  • Rxjs //截流與仿抖
  • cyclejs
  • lodashjs
  • underscorejs //開(kāi)始學(xué)最佳的庫(kù)
  • ramadajs

需要學(xué)習(xí)

范疇與容器

  1. 我們可以把“范疇”想象成是一個(gè)容器错妖,里面包含兩樣?xùn)|西绿鸣。值(value)、值的變形關(guān)系暂氯,也就是函數(shù)潮模。
  2. 范疇論使用函數(shù),表達(dá)范疇之間的關(guān)系痴施。
  3. 伴隨著范疇論的發(fā)展擎厢,就發(fā)展出一整套函數(shù)的運(yùn)算方法究流。這套方法起初只用于數(shù)學(xué)運(yùn)算,后來(lái)有人將它在計(jì)算機(jī)上實(shí)現(xiàn)了动遭,就變成了今天的“函數(shù)式編程”梯嗽。
  4. 本質(zhì)上,函數(shù)式編程只是范疇論的運(yùn)算方法沽损,跟數(shù)理邏輯灯节、微積分、行列式式同一類東西绵估,都是數(shù)學(xué)方法炎疆,只是碰巧他能用來(lái)寫(xiě)程序。為什么函數(shù)式編程要求函數(shù)必須是純的国裳,不能有副作用形入?因?yàn)樗且环N數(shù)學(xué)運(yùn)算,原始目的就是求值缝左,不做其他事情亿遂,否則就無(wú)法滿足函數(shù)運(yùn)算法則了。

函子是函數(shù)式編程里面最重要的數(shù)據(jù)類型渺杉,也是基本的運(yùn)算單位和功能單位蛇数。它首先是一種范疇,也就是說(shuō)是越,是一個(gè)容器耳舅,包含了值和變形關(guān)系。比較特殊的是倚评,它的變形關(guān)系可以依次作用于每一個(gè)值浦徊,將當(dāng)前容器變形成另一個(gè)容器。

容器天梧、Functor(函子)

  1. $(...)返回的對(duì)象并不是一個(gè)原生的DOM對(duì)象盔性,而是對(duì)于原生對(duì)象的一種封裝,這在某種意義上就是一個(gè)"容器"(但它并不函數(shù)式)
  2. Functor(函子)遵守一些特定規(guī)則的容器類型
  3. Functor是一個(gè)對(duì)于函數(shù)調(diào)用的抽象呢岗,我們賦予容器自己去調(diào)用函數(shù)的能力冕香。把東西裝進(jìn)一個(gè)容器,只留出一個(gè)接口map給容器外的函數(shù)敷燎,map一個(gè)函數(shù)時(shí)暂筝,我們讓容器自己來(lái)運(yùn)行這個(gè)函數(shù)箩言,這樣容器就可以自由地選擇何時(shí)何地如何操作這個(gè)函數(shù)硬贯,以致于擁有惰性求值、錯(cuò)誤處理陨收、一步調(diào)用等非常牛掰的特性
var Container=function(x){
    this.__value=x;
}
//函數(shù)式編程一般約定饭豹,函子有一個(gè)of方法
Container.of=x=>new Container(x);
//Container.of('abcd);
//一般約定鸵赖,函子的標(biāo)志就是容器具有map方法。該方法將容器里面的每一個(gè)值拄衰,映射到另一個(gè)容器它褪。
Container.prototype.map=function(f){
    return Container.of(f(this.__value));
}
Container.of(3)
    .map(x=>x+1)                //Container(4)
    .map(x=>'Result is '+x);        //Container('Result is 4')

Maybe 函子

函子接受各種函數(shù),處理容器內(nèi)部的值翘悉,這里就有一個(gè)問(wèn)題茫打,容器內(nèi)部的值可能是一個(gè)空值(比如null),而外部函數(shù)未必有處理空值的機(jī)制妖混,如果傳入空值老赤,很可能就會(huì)出錯(cuò)。

Functor.of(null).map(function(s){
    return s.toUpperCase();
});
//TypeError
class Maybe extends Functor{
    map(f){
        return this.val?Maybe.of(f(this.val)):Maybe.of(null);
    }
}
Maybe.of(null).map(function(s){
    return s.toUpperCase();
});
//Maybe(null) //報(bào)錯(cuò)制市,未定義
var Maybe=function(x){
    this.__value=x;
}
Maybe.of=function(x){
    return new Maybe(x);
}
Maybe.prototype.map=function(f){
    return this.isNothing()?Maybe.of(null):Maybe.of(f(this.__value));
}
Maybe.prototype.isNothing=function(){
    return (this.__value===null||this.__value===undefined);
}
Maybe(null) //不會(huì)報(bào)錯(cuò)了
//新的容器我們稱為Maybe

Either 函子

條件運(yùn)算if...else 是常見(jiàn)的運(yùn)算之一抬旺,函數(shù)式編程里面,使用Either函子表達(dá)祥楣。Either函子內(nèi)部有兩個(gè)值:左值(left)和右值(right)开财。右值是正常情況下使用的值,左值是右值不存在時(shí)使用的默認(rèn)值误褪。

class Either extends Functor{
    constructor(left,right){
        this.left=left;
        this.right=right;
    }
    map(f){
        //右值存在變右值责鳍,否則變左值
        return this.right?Either.of(this.left,f(this.right)):Either.of(f(this.left),this.right);
    }
}
Either.of=function(left,right){
    return new Either(left,right);
}

var addOne=function(x){
    return x+1;
}
Either.of(5,6).map(addOne); //Either(5,7);
Either.of(1,null).map(addOne);  //Either(2);
Either
    //右值中有address這個(gè)屬性,則覆蓋原來(lái)的xxx兽间,否則使用默認(rèn)的xxx
    .of({address:'xxx'},currentUser.address)    .map(updateField);

es5寫(xiě)法

錯(cuò)誤處理薇搁、Either

var Left=function(x){
    this.__value=x;
}
var Rigth=function(x){
    this.__value=x;
}
Left.of=function(x){
    return new Left(x);
}
Right.of=function(x){
    return new Right(x);
}
Left.prototype.map=functin(f){
    return this;
}
Right.prototype.map=function(f){
    return Right.of(f(this.__value));
}

Left和Right唯一的區(qū)別就在于map方法的實(shí)現(xiàn),Right.map的行為和我們之前提到的map函數(shù)一樣渡八。但是Left.map就很不同了:它不會(huì)對(duì)容器做任何事情啃洋,只是很簡(jiǎn)單地把這個(gè)容器拿進(jìn)來(lái)又扔出去。這個(gè)特性意味著屎鳍,Left可以用來(lái)傳遞一個(gè)錯(cuò)誤消息宏娄。

var getAge = user => user.age ? Right.of(user.age):Left.of("Error");;
getAge({name:'stark',age:'21'}).map(age=>'Age is '+age);
//Right('Age is 21');
getAge({name:'stark'}).map({age=>'Age is '+age});
//Left('Error');

Left 可以讓調(diào)用鏈中任意一環(huán)的錯(cuò)誤立即返回到調(diào)用鏈的尾部,這給我們錯(cuò)誤處理帶來(lái)了很大的方便逮壁,再也不用一層又一層的Try/catch

AP因子

函子里面包含的值孵坚,完全可能是函數(shù)。我們可以想象這樣一種情況窥淆,一個(gè)函子的值是數(shù)值卖宠,另一個(gè)函子的值是函子。

class Ap extends Functor{
    ap(F){
        return Ap.of(this.val(F.val));
    }
}
Ap.of(addTwo).ap(Functor.of(2));

實(shí)例

function Functor(val){
    this.__val=val;
}
Functor.of=function(val){
    return new Functor(val);
}
Functor.prototype.map=function(fn){
    return Functor.of(fn(this.__val));
}
function addTwo(x){
    return x+2;
}
function Ap(val){
    Functor.call(this,val);
}
Ap.of=function(val){
    return new Ap(val);
}
var __proto=Object.create(Functor.prototype);
__proto.constructor=Ap.prototype.constructor;
Ap.prototype=__proto;
Ap.prototype.ap=function(F){
    return Ap.of(this.__val(F.__val));
}

const A=Functor.of(2);
const B=Ap.of(addTwo);
console.log(B.ap(A));  //4
//console.log(B.ap(A).ap(A));此時(shí)會(huì)報(bào)錯(cuò)忧饭,B.ap(A)其中A不是個(gè)函數(shù)

IO

真正的程序總要去接觸骯臟的世界

function readLoaclStorage(){
    return window.localStorage;
}

Io跟前面那幾個(gè)Functor不同的地方在于扛伍,他的__value是一個(gè)函數(shù)。它把不純的操作(比如IO词裤、網(wǎng)絡(luò)請(qǐng)求刺洒、DOM)包裹到一個(gè)函數(shù)內(nèi)鳖宾,從而延遲這個(gè)操作的執(zhí)行。所以我們認(rèn)為逆航,IO包含的是被包裹的操作的返回值鼎文。

IO其實(shí)也算是惰性求值

IO負(fù)責(zé)了調(diào)用鏈積累了很多很多不純的操作,帶來(lái)的復(fù)雜性和不可維護(hù)性因俐。

import _ from 'lodash';
var compose=_.flowRight;
var IO=function(f){
    this._value=f;
}
IO.of=x=>new IO(_=>x);
IO.prototype.map=function(f){
    return new IO(compose(f,this.__value));
}

Monad

Monad就是一種設(shè)計(jì)模式拇惋,表示將一個(gè)運(yùn)算過(guò)程,通過(guò)函數(shù)拆解成互相連接的多個(gè)步驟抹剩。你只要提供下一步運(yùn)算所需的函數(shù)蚤假,整個(gè)運(yùn)算就會(huì)自動(dòng)進(jìn)行下去。

Promise就是一種Monad

Monad糖我們避開(kāi)了嵌套地獄吧兔,可以輕松地進(jìn)行深度嵌套的函數(shù)式編程磷仰,比如IO和其它異步任務(wù)

Maybe.of(
    Maybe.of(
        Maybe.of({name:'Mulburry',number:99})
    )
)
class Monad extends Functor{
    join(){
        return this.val;
    }
    flatMap(f){
        return this.map(f).join();
    }
}

Monad 函子的作用是,總是返回一個(gè)單層的函子境蔼。它有一個(gè)flatMap方法灶平,與map方法作用相同,唯一的區(qū)別是如果生成了一個(gè)嵌套函子箍土,他會(huì)取出后這內(nèi)部的值逢享,保證返回的永遠(yuǎn)是一個(gè)單層的容器,不會(huì)出現(xiàn)嵌套的情況吴藻。

如果函數(shù)f返回的是一個(gè)函子瞒爬,那么this.map(f)就會(huì)生成一個(gè)嵌套的函子。所以沟堡,join方法保證了flatMap方法總是返回一個(gè)單層的函子侧但。這意味著嵌套的漢子會(huì)被鋪平(flatMap)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市航罗,隨后出現(xiàn)的幾起案子禀横,更是在濱河造成了極大的恐慌,老刑警劉巖粥血,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柏锄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡复亏,警方通過(guò)查閱死者的電腦和手機(jī)趾娃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缔御,“玉大人抬闷,你說(shuō)我怎么就攤上這事∩蔡剩” “怎么了饶氏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)有勾。 經(jīng)常有香客問(wèn)我疹启,道長(zhǎng),這世上最難降的妖魔是什么蔼卡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任喊崖,我火速辦了婚禮,結(jié)果婚禮上雇逞,老公的妹妹穿的比我還像新娘荤懂。我一直安慰自己,他們只是感情好塘砸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布节仿。 她就那樣靜靜地躺著,像睡著了一般掉蔬。 火紅的嫁衣襯著肌膚如雪廊宪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天女轿,我揣著相機(jī)與錄音箭启,去河邊找鬼。 笑死蛉迹,一個(gè)胖子當(dāng)著我的面吹牛傅寡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播北救,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荐操,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了珍策?” 一聲冷哼從身側(cè)響起淀零,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎膛壹,沒(méi)想到半個(gè)月后驾中,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡模聋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年肩民,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片链方。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡持痰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祟蚀,到底是詐尸還是另有隱情工窍,我是刑警寧澤割卖,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站患雏,受9級(jí)特大地震影響鹏溯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜淹仑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一丙挽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匀借,春花似錦颜阐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至是鬼,卻和暖如春猿棉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屑咳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工萨赁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兆龙。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓杖爽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親紫皇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慰安,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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