前端基礎(chǔ)進(jìn)階(十):深入詳解函數(shù)的柯里化

配圖與本文無(wú)關(guān)

柯里化是函數(shù)的一個(gè)高級(jí)應(yīng)用命斧,想要理解它并不簡(jiǎn)單莽红。因此我一直在思考應(yīng)該如何更加表達(dá)才能讓大家理解起來(lái)更加容易。

通過(guò)上一個(gè)章節(jié)的學(xué)習(xí)我們知道稀余,接收函數(shù)作為參數(shù)的函數(shù)悦冀,都可以叫做高階函數(shù)。我們常常利用高階函數(shù)來(lái)封裝一些公共的邏輯睛琳。

這一章我們要學(xué)習(xí)的柯里化盒蟆,其實(shí)就是高階函數(shù)的一種特殊用法踏烙。

柯里化是指這樣一個(gè)函數(shù)(假設(shè)叫做createCurry),他接收函數(shù)A作為參數(shù)历等,運(yùn)行后能夠返回一個(gè)新的函數(shù)讨惩。并且這個(gè)新的函數(shù)能夠處理函數(shù)A的剩余參數(shù)。

這樣的定義不太好理解寒屯,我們可以通過(guò)下面的例子配合解釋荐捻。

有一個(gè)接收三個(gè)參數(shù)的函數(shù)A。

function A(a, b, c) {
    // do something
}

假如寡夹,我們有一個(gè)已經(jīng)封裝好了的柯里化通用函數(shù)createCurry处面。他接收bar作為參數(shù),能夠?qū)轉(zhuǎn)化為柯里化函數(shù)菩掏,返回結(jié)果就是這個(gè)被轉(zhuǎn)化之后的函數(shù)魂角。

var _A = createCurry(A);

那么_A作為createCurry運(yùn)行的返回函數(shù),他能夠處理A的剩余參數(shù)智绸。因此下面的運(yùn)行結(jié)果都是等價(jià)的野揪。

_A(1, 2, 3);
_A(1, 2)(3);
_A(1)(2, 3);
_A(1)(2)(3);
A(1, 2, 3);

函數(shù)A被createCurry轉(zhuǎn)化之后得到柯里化函數(shù)_A,_A能夠處理A的所有剩余參數(shù)瞧栗。因此柯里化也被稱為部分求值斯稳。

在簡(jiǎn)單的場(chǎng)景下,可以不用借助柯里化通用式來(lái)轉(zhuǎn)化得到柯里化函數(shù)沼溜,我們憑借眼力自己封裝平挑。

例如有一個(gè)簡(jiǎn)單的加法函數(shù),他能夠?qū)⒆陨淼娜齻€(gè)參數(shù)加起來(lái)并返回計(jì)算結(jié)果系草。

function add(a, b, c) {
    return a + b + c;
}

那么add函數(shù)的柯里化函數(shù)_add則可以如下:

function _add(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        }
    }
}

下面的運(yùn)算方式是等價(jià)的通熄。

add(1, 2, 3);
_add(1)(2)(3);

當(dāng)然,靠眼力封裝的柯里化函數(shù)自由度偏低找都,柯里化通用式具備更加強(qiáng)大的能力唇辨。因此我們需要知道如何去封裝這樣一個(gè)柯里化的通用式。

首先通過(guò)_add可以看出能耻,柯里化函數(shù)的運(yùn)行過(guò)程其實(shí)是一個(gè)參數(shù)的收集過(guò)程赏枚,我們將每一次傳入的參數(shù)收集起來(lái),并在最里層里面處理晓猛。在實(shí)現(xiàn)createCurry時(shí)饿幅,可以借助這個(gè)思路來(lái)進(jìn)行封裝。

封裝如下:

// 簡(jiǎn)單實(shí)現(xiàn)戒职,參數(shù)只能從右到左傳遞
function createCurry(func, args) {

    var arity = func.length;
    var args = args || [];

    return function() {
        var _args = [].slice.call(arguments);
        [].push.apply(_args, args);

        // 如果參數(shù)個(gè)數(shù)小于最初的func.length栗恩,則遞歸調(diào)用,繼續(xù)收集參數(shù)
        if (_args.length < arity) {
            return createCurry.call(this, func, _args);
        }

        // 參數(shù)收集完畢洪燥,則執(zhí)行func
        return func.apply(this, _args);
    }
}

盡管我已經(jīng)做了足夠詳細(xì)的注解磕秤,但是我想理解起來(lái)也并不是那么容易乳乌,因此建議大家用點(diǎn)耐心多閱讀幾遍。這個(gè)createCurry函數(shù)的封裝借助閉包與遞歸市咆,實(shí)現(xiàn)了一個(gè)參數(shù)收集汉操,并在收集完畢之后執(zhí)行所有參數(shù)的一個(gè)過(guò)程。

聰明的讀者可能已經(jīng)發(fā)現(xiàn)蒙兰,把函數(shù)經(jīng)過(guò)createCurry轉(zhuǎn)化為一個(gè)柯里化函數(shù)磷瘤,最后執(zhí)行的結(jié)果,不是正好相當(dāng)于執(zhí)行函數(shù)自身嗎癞己?柯里化是不是把簡(jiǎn)單的問(wèn)題復(fù)雜化了膀斋?

如果你能夠提出這樣的問(wèn)題,那么說(shuō)明你確實(shí)已經(jīng)對(duì)柯里化有了一定的了解痹雅⊙龅#柯里化確實(shí)是把簡(jiǎn)答的問(wèn)題復(fù)雜化了,但是復(fù)雜化的同時(shí)绩社,我們使用函數(shù)擁有了更加多的自由度摔蓝。而這里對(duì)于函數(shù)參數(shù)的自由處理,正是柯里化的核心所在愉耙。

舉一個(gè)非常常見(jiàn)的例子贮尉。

如果我們想要驗(yàn)證一串?dāng)?shù)字是否是正確的手機(jī)號(hào),按照普通的思路來(lái)做朴沿,大家可能是這樣封裝猜谚,如下:

function checkPhone(phoneNumber) {
    return /^1[34578]\d{9}$/.test(phoneNumber);
}

而如果想要驗(yàn)證是否是郵箱呢?這么封裝:

function checkEmail(email) {
    return /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/.test(email);
}

我們還可能會(huì)遇到驗(yàn)證身份證號(hào)赌渣,驗(yàn)證密碼等各種驗(yàn)證信息魏铅,因此在實(shí)踐中,為了統(tǒng)一邏輯坚芜,我們就會(huì)封裝一個(gè)更為通用的函數(shù)览芳,將用于驗(yàn)證的正則與將要被驗(yàn)證的字符串作為參數(shù)傳入。

function check(targetString, reg) {
    return reg.test(targetString);
}

但是這樣封裝之后鸿竖,在使用時(shí)又會(huì)稍微麻煩一點(diǎn)沧竟,因?yàn)闀?huì)總是輸入一串正則,這樣就導(dǎo)致了使用時(shí)的效率低下缚忧。

check(/^1[34578]\d{9}$/, '14900000088');
check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com');

這個(gè)時(shí)候悟泵,我們就可以借助柯里化,在check的基礎(chǔ)上再做一層封裝闪水,以簡(jiǎn)化使用魁袜。

var _check = createCurry(check);

var checkPhone = _check(/^1[34578]\d{9}$/);
var checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);

最后在使用的時(shí)候就會(huì)變得更加直觀與簡(jiǎn)潔了。

checkPhone('183888888');
checkEmail('xxxxx@test.com');

經(jīng)過(guò)這個(gè)過(guò)程我們發(fā)現(xiàn)敦第,柯里化能夠應(yīng)對(duì)更加復(fù)雜的邏輯封裝峰弹。當(dāng)情況變得多變,柯里化依然能夠應(yīng)付自如芜果。

雖然柯里化確實(shí)在一定程度上將問(wèn)題復(fù)雜化了鞠呈,也讓代碼更加不容易理解,但是柯里化在面對(duì)復(fù)雜情況下的靈活性卻讓我們不得不愛(ài)右钾。

當(dāng)然這個(gè)案例本身情況還算簡(jiǎn)單蚁吝,所以還不能夠特別明顯的凸顯柯里化的優(yōu)勢(shì),我們的主要目的在于借助這個(gè)案例幫助大家了解柯里化在實(shí)踐中的用途舀射。

繼續(xù)來(lái)思考一個(gè)例子窘茁。這個(gè)例子與map有關(guān)。在高階函數(shù)的章節(jié)中脆烟,我們分析了封裝map方法的思考過(guò)程山林。由于我們沒(méi)有辦法確認(rèn)一個(gè)數(shù)組在遍歷時(shí)會(huì)執(zhí)行什么操作,因此我們只能將調(diào)用for循環(huán)的這個(gè)統(tǒng)一邏輯封裝起來(lái)邢羔,而具體的操作則通過(guò)參數(shù)傳入的形式讓使用者自定義驼抹。這就是map函數(shù)。

但是拜鹤,這是針對(duì)了所有的情況我們才會(huì)這樣想框冀。

實(shí)踐中我們常常會(huì)發(fā)現(xiàn),在我們的某個(gè)項(xiàng)目中敏簿,針對(duì)于某一個(gè)數(shù)組的操作其實(shí)是固定的明也,也就是說(shuō),同樣的操作惯裕,可能會(huì)在項(xiàng)目的不同地方調(diào)用很多次温数。

于是,這個(gè)時(shí)候轻猖,我們就可以在map函數(shù)的基礎(chǔ)上帆吻,進(jìn)行二次封裝,以簡(jiǎn)化我們?cè)陧?xiàng)目中的使用咙边。假如這個(gè)在我們項(xiàng)目中會(huì)調(diào)用多次的操作是將數(shù)組的每一項(xiàng)都轉(zhuǎn)化為百分比 1 --> 100%猜煮。

普通思維下我們可以這樣來(lái)封裝。

function getNewArray(array) {
    return array.map(function(item) {
        return item * 100 + '%'
    })
}

getNewArray([1, 2, 3, 0.12]);   // ['100%', '200%', '300%', '12%'];

而如果借助柯里化來(lái)二次封裝這樣的邏輯败许,則會(huì)如下實(shí)現(xiàn):

function _map(func, array) {
    return array.map(func);
}

var _getNewArray = createCurry(_map);

var getNewArray = _getNewArray(function(item) {
    return item * 100 + '%'
})

getNewArray([1, 2, 3, 0.12]);   // ['100%', '200%', '300%', '12%'];
getNewArray([0.01, 1]); // ['1%', '100%']

如果我們的項(xiàng)目中的固定操作是希望對(duì)數(shù)組進(jìn)行一個(gè)過(guò)濾王带,找出數(shù)組中的所有Number類型的數(shù)據(jù)。借助柯里化思維我們可以這樣做市殷。

function _filter(func, array) {
    return array.filter(func);
}

var _find = createCurry(_filter);

var findNumber = _find(function(item) {
    if (typeof item == 'number') {
        return item;
    }
})

findNumber([1, 2, 3, '2', '3', 4]); // [1, 2, 3, 4]

// 當(dāng)我們繼續(xù)封裝另外的過(guò)濾操作時(shí)就會(huì)變得非常簡(jiǎn)單
// 找出數(shù)字為20的子項(xiàng)
var find20 = _find(function(item, i) {
    if (typeof item === 20) {
        return i;
    }
})
find20([1, 2, 3, 30, 20, 100]);  // 4

// 找出數(shù)組中大于100的所有數(shù)據(jù)
var findGreater100 = _find(function(item) {
    if (item > 100) {
        return item;
    }
})
findGreater100([1, 2, 101, 300, 2, 122]); // [101, 300, 122]

我采用了與check例子不一樣的思維方向來(lái)想大家展示我們?cè)谑褂每吕锘瘯r(shí)的想法愕撰。目的是想告訴大家,柯里化能夠幫助我們應(yīng)對(duì)更多更復(fù)雜的場(chǎng)景。

當(dāng)然不得不承認(rèn)搞挣,這些例子都太簡(jiǎn)單了带迟,簡(jiǎn)單到如果使用柯里化的思維來(lái)處理他們顯得有一點(diǎn)多此一舉,而且變得難以理解囱桨。因此我想讀者朋友們也很難從這些例子中感受到柯里化的魅力仓犬。不過(guò)沒(méi)關(guān)系,如果我們能夠通過(guò)這些例子掌握到柯里化的思維舍肠,那就是最好的結(jié)果了搀继。在未來(lái)你的實(shí)踐中,如果你發(fā)現(xiàn)用普通的思維封裝一些邏輯慢慢變得困難翠语,不妨想一想在這里學(xué)到的柯里化思維叽躯,應(yīng)用起來(lái),柯里化足夠強(qiáng)大的自由度一定能給你一個(gè)驚喜肌括。

當(dāng)然也并不建議在任何情況下以炫技為目的的去使用柯里化点骑,在柯里化的實(shí)現(xiàn)中,我們知道柯里化雖然具有了更多的自由度们童,但同時(shí)柯里化通用式里調(diào)用了arguments對(duì)象畔况,使用了遞歸與閉包,因此柯里化的自由度是以犧牲了一定的性能為代價(jià)換來(lái)的慧库。只有在情況變得復(fù)雜時(shí)跷跪,才是柯里化大顯身手的時(shí)候。

額外知識(shí)補(bǔ)充

無(wú)限參數(shù)的柯里化齐板。

該部分內(nèi)容可忽略

在前端面試中吵瞻,你可能會(huì)遇到這樣一個(gè)涉及到柯里化的題目。

// 實(shí)現(xiàn)一個(gè)add方法甘磨,使計(jì)算結(jié)果能夠滿足如下預(yù)期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

這個(gè)題目的目的是想讓add執(zhí)行之后返回一個(gè)函數(shù)能夠繼續(xù)執(zhí)行橡羞,最終運(yùn)算的結(jié)果是所有出現(xiàn)過(guò)的參數(shù)之和。而這個(gè)題目的難點(diǎn)則在于參數(shù)的不固定济舆。我們不知道函數(shù)會(huì)執(zhí)行幾次卿泽。因此我們不能使用上面我們封裝的createCurry的通用公式來(lái)轉(zhuǎn)換一個(gè)柯里化函數(shù)。只能自己封裝滋觉,那么怎么辦呢签夭?在此之前,補(bǔ)充2個(gè)非常重要的知識(shí)點(diǎn)椎侠。

一個(gè)是ES6函數(shù)的不定參數(shù)第租。假如我們有一個(gè)數(shù)組,希望把這個(gè)數(shù)組中所有的子項(xiàng)展開(kāi)傳遞給一個(gè)函數(shù)作為參數(shù)我纪。那么我們應(yīng)該怎么做慎宾?

// 大家可以思考一下丐吓,如果將args數(shù)組的子項(xiàng)展開(kāi)作為add的參數(shù)傳入
function add(a, b, c, d) {
    return a + b + c + d;
}
var args = [1, 3, 100, 1];

在ES5中,我們可以借助之前學(xué)過(guò)的apply來(lái)達(dá)到我們的目的趟据。

add.apply(null, args);  // 105

而在ES6中券犁,提供了一種新的語(yǔ)法來(lái)解決這個(gè)問(wèn)題,那就是不定參之宿。寫法如下:

add(...args);  // 105

這兩種寫法是等效的族操。OK,先記在這里比被。在接下的實(shí)現(xiàn)中,我們會(huì)用到不定參數(shù)的特性泼舱。

第二個(gè)要補(bǔ)充的知識(shí)點(diǎn)是函數(shù)的隱式轉(zhuǎn)換等缀。當(dāng)我們直接將函數(shù)參與其他的計(jì)算時(shí),函數(shù)會(huì)默認(rèn)調(diào)用toString方法娇昙,直接將函數(shù)體轉(zhuǎn)換為字符串參與計(jì)算尺迂。

function fn() { return 20 }
console.log(fn + 10);     // 輸出結(jié)果 function fn() { return 20 }10

我們可以重寫函數(shù)的toString方法,讓函數(shù)參與計(jì)算時(shí)冒掌,輸出我們想要的結(jié)果噪裕。

function fn() { return 20; }
fn.toString = function() { return 30 }

console.log(fn + 10); // 40

除此之外,當(dāng)我們重寫函數(shù)的valueOf方法也能夠改變函數(shù)的隱式轉(zhuǎn)換結(jié)果股毫。

function fn() { return 20; }
fn.valueOf = function() { return 60 }

console.log(fn + 10); // 70

當(dāng)我們同時(shí)重寫函數(shù)的toString方法與valueOf方法時(shí)膳音,最終的結(jié)果會(huì)取valueOf方法的返回結(jié)果。

function fn() { return 20; }
fn.valueOf = function() { return 50 }
fn.toString = function() { return 30 }

console.log(fn + 10); // 60

補(bǔ)充了這兩個(gè)知識(shí)點(diǎn)之后铃诬,我們可以來(lái)嘗試完成之前的題目了祭陷。add方法的實(shí)現(xiàn)仍然會(huì)是一個(gè)參數(shù)的收集過(guò)程。當(dāng)add函數(shù)執(zhí)行到最后時(shí)趣席,仍然返回的是一個(gè)函數(shù)兵志,但是我們可以通過(guò)定義toString/valueOf的方式,讓這個(gè)函數(shù)可以直接參與計(jì)算宣肚,并且轉(zhuǎn)換的結(jié)果是我們想要的想罕。而且它本身也仍然可以繼續(xù)執(zhí)行接收新的參數(shù)。實(shí)現(xiàn)方式如下霉涨。

function add() {
    // 第一次執(zhí)行時(shí)按价,定義一個(gè)數(shù)組專門用來(lái)存儲(chǔ)所有的參數(shù)
    var _args = [].slice.call(arguments);

    // 在內(nèi)部聲明一個(gè)函數(shù),利用閉包的特性保存_args并收集所有的參數(shù)值
    var adder = function () {
        var _adder = function() {
            // [].push.apply(_args, [].slice.call(arguments));
            _args.push(...arguments);
            return _adder;
        };

        // 利用隱式轉(zhuǎn)換的特性嵌纲,當(dāng)最后執(zhí)行時(shí)隱式轉(zhuǎn)換俘枫,并計(jì)算最終的值返回
        _adder.toString = function () {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }

        return _adder;
    }
    // return adder.apply(null, _args);
    return adder(..._args);
}

var a = add(1)(2)(3)(4);   // f 10
var b = add(1, 2, 3, 4);   // f 10
var c = add(1, 2)(3, 4);   // f 10
var d = add(1, 2, 3)(4);   // f 10

// 可以利用隱式轉(zhuǎn)換的特性參與計(jì)算
console.log(a + 10); // 20
console.log(b + 20); // 30
console.log(c + 30); // 40
console.log(d + 40); // 50

// 也可以繼續(xù)傳入?yún)?shù),得到的結(jié)果再次利用隱式轉(zhuǎn)換參與計(jì)算
console.log(a(10) + 100);  // 120
console.log(b(10) + 100);  // 120
console.log(c(10) + 100);  // 120
console.log(d(10) + 100);  // 120
// 其實(shí)上栗中的add方法逮走,就是下面這個(gè)函數(shù)的柯里化函數(shù)鸠蚪,只不過(guò)我們并沒(méi)有使用通用式來(lái)轉(zhuǎn)化,而是自己封裝
function add(...args) {
    return args.reduce((a, b) => a + b);
}

下一篇:前端基礎(chǔ)進(jìn)階(十一):詳解面向?qū)ο蟆?gòu)造函數(shù)茅信、原型與原型鏈
上一篇:前端基礎(chǔ)進(jìn)階(九):函數(shù)與函數(shù)式編程
前端基礎(chǔ)進(jìn)階目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盾舌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蘸鲸,更是在濱河造成了極大的恐慌妖谴,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酌摇,死亡現(xiàn)場(chǎng)離奇詭異膝舅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)窑多,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門仍稀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人埂息,你說(shuō)我怎么就攤上這事技潘。” “怎么了千康?”我有些...
    開(kāi)封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵享幽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拾弃,道長(zhǎng)值桩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任砸彬,我火速辦了婚禮颠毙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砂碉。我一直安慰自己蛀蜜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布增蹭。 她就那樣靜靜地躺著滴某,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滋迈。 梳的紋絲不亂的頭發(fā)上霎奢,一...
    開(kāi)封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音饼灿,去河邊找鬼幕侠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碍彭,可吹牛的內(nèi)容都是我干的晤硕。 我是一名探鬼主播悼潭,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舞箍!你這毒婦竟也來(lái)了舰褪?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疏橄,失蹤者是張志新(化名)和其女友劉穎占拍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捎迫,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晃酒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了立砸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掖疮。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖颗祝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恼布,我是刑警寧澤螺戳,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站折汞,受9級(jí)特大地震影響倔幼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爽待,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一损同、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸟款,春花似錦膏燃、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至处渣,卻和暖如春伶贰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罐栈。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工黍衙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荠诬。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓琅翻,卻偏偏與公主長(zhǎng)得像位仁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子望迎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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