JS設(shè)計(jì)模式深入理解—單例争剿、工廠已艰、構(gòu)造函數(shù)、原型蚕苇、組合構(gòu)造原型哩掺、動態(tài)原型

了解并掌握各種JavaScript用于創(chuàng)建自定義類型對象的設(shè)計(jì)模式有利于幫助我們認(rèn)識它們各自的優(yōu)缺點(diǎn)和適用場景,這樣我們在今后的開發(fā)過程中才能夠做到有的放矢涩笤,在正確的場合使用正確的模式創(chuàng)建對象嚼吞。

一盒件、單例模式

var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";

person.sayName = function() {
    alert(this.name);
};

單例模式是指通過創(chuàng)建一個Object對象,并為其設(shè)置各種屬性和方法舱禽,以滿足自定義對象的使用需求炒刁,如上所示;很明顯誊稚,上面的多條語句顯得十分分散翔始,為了更好地將它們組合起來,更好的辦法是使用對象字面量創(chuàng)建:

var person = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",

    sayName: function() {
        alert(this.name);
    }    
};

模式評價:雖然Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢杂脕韯?chuàng)建單個對象里伯,但這些方式有兩個明顯的缺點(diǎn):

  • 沒有做到代碼的復(fù)用城瞎,即要是使用同樣的辦法創(chuàng)建多個對象,會產(chǎn)生大量的重復(fù)代碼疾瓮。
  • 創(chuàng)建出的對象沒有具體的類型脖镀,它們只是Object類型的一個實(shí)例。

二狼电、工廠模式

為了解決單例模式的代碼復(fù)用問題认然,更好的辦法是采用工廠模式:

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name);
    };
    return o;
}

var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

由于JavaScript中無法創(chuàng)建類,所以人們就發(fā)明了一種函數(shù)漫萄,用函數(shù)來封裝以特定接口創(chuàng)建對象的細(xì)節(jié),這就是工廠模式的設(shè)計(jì)原理盈匾。

在上面的例子中腾务,函數(shù)createPerson()能夠根據(jù)接受的參數(shù)來構(gòu)建一個包含所有必要信息的Person對象,并且可以無數(shù)次地調(diào)用這個函數(shù)削饵,而每次調(diào)用都會返回一個包含三個屬性和一個方法的對象岩瘦。

模式評價:工廠模式雖然解決了創(chuàng)建多個相似對象造成的代碼重復(fù)問題,但仍未解決對象類型的區(qū)分問題(怎樣知道一個對象的具體類型)窿撬;通過工廠模式創(chuàng)建出的對象启昧,其類型都是Object,如果能把上例中創(chuàng)建出的對象標(biāo)記為Person類型就好了劈伴。

同時密末,通過工廠模式創(chuàng)建出的對象還存在著一個很嚴(yán)重的問題,那就是內(nèi)存浪費(fèi)跛璧。每當(dāng)調(diào)用一次createPerson()函數(shù)創(chuàng)建一個對象严里,就會在其內(nèi)部創(chuàng)建一個函數(shù)實(shí)例。在前面的例子中追城,person1person2都有一個名為sayName()的方法刹碾,但person1.sayName()person2.sayName()并不是引用的同一個函數(shù)實(shí)例,而是不同的實(shí)例座柱,因?yàn)?/p>

o.sayName = function() {
    alert(this.name);
};

o.sayName = new Function("alert(this.name)");

在邏輯上是完全等價的迷帜。為了證明person1.sayName()person2.sayName()引用的是不同的函數(shù)實(shí)例物舒,有:

alert(person1.sayName == person2.sayName)      //false

因此,若一個自定義對象類型中要是有多個方法戏锹,那么通過工廠模式定義出多個對象造成的內(nèi)存浪費(fèi)就可想而知了冠胯。

三、構(gòu)造函數(shù)模式

為了解決對象的類型問題景用,可以使用構(gòu)造函數(shù)模式涵叮,JavaScript中的構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    };
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

首先需要明確什么樣的函數(shù)稱為構(gòu)造函數(shù),即通過new操作符+函數(shù)名的方式來創(chuàng)建對象的函數(shù)伞插,就叫做構(gòu)造函數(shù)割粮。

構(gòu)造函數(shù)創(chuàng)建對象實(shí)例的過程:

  1. 創(chuàng)建一個新對象;
  2. 把當(dāng)前構(gòu)造函數(shù)內(nèi)的作用域賦給新創(chuàng)建的這個對象(此時構(gòu)造函數(shù)內(nèi)部的this指針就指向了這個新對象)媚污;
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼舀瓢;
  4. 返回新對象

同時構(gòu)造函數(shù)還有一個極其重要的特性:默認(rèn)情況下,若構(gòu)造函數(shù)內(nèi)部沒有通過return語句返回其它類型的變量或?qū)ο蠛拿溃瑒t通過構(gòu)造函數(shù)創(chuàng)建并返回的對象其類型由構(gòu)造函數(shù)名指定京髓。

這個特性的意思就是,在上例中商架,通過名為Person的構(gòu)造函數(shù)創(chuàng)建出的person1person2對象實(shí)例堰怨,其對象類型都是Person;也就是說蛇摸,它們都是Person類型的對象實(shí)例(可以使用instanceof檢驗(yàn)):

alert(person1 instanceof Person);      //true
alert(person2 instanceof Person);      //true

正因?yàn)檫@個特性备图,所以才為什么說構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象

這樣一來赶袄,構(gòu)造函數(shù)模式就很好理解了:

  1. 通過new Person()調(diào)用Person構(gòu)造函數(shù)揽涮,創(chuàng)建出一個Person類型的對象。
  2. Person構(gòu)造函數(shù)內(nèi)部的作用域賦給該對象饿肺,即此時函數(shù)內(nèi)部的this指向了該對象蒋困。
  3. 執(zhí)行構(gòu)造函數(shù)代碼,通過this為該對象創(chuàng)建屬性和方法敬辣。
  4. 由于Person構(gòu)造函數(shù)內(nèi)部沒有用return語句返回其它變量和對象雪标,所以Person構(gòu)造函數(shù)返回該對象。

進(jìn)一步優(yōu)化
為了解決工廠模式提到的內(nèi)存浪費(fèi)問題溉跃,我們發(fā)現(xiàn)創(chuàng)建兩個完成同樣任務(wù)的Function實(shí)例的確沒有必要汰聋;況且有this對象在,根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對象上喊积。因此烹困,可以把公共函數(shù)的定義轉(zhuǎn)移到構(gòu)造函數(shù)外部:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName() {
    alert(this.name);
};

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

模式評價
構(gòu)造函數(shù)模式有效地解決了對象的類型問題,是比工廠模式更佳的解決方案乾吻。同時為了解決工廠模式中遇到的內(nèi)存浪費(fèi)問題髓梅,選擇將公共函數(shù)的定義轉(zhuǎn)移到構(gòu)造函數(shù)的外部拟蜻,看樣子解決了目前遇到的所有問題。然而新的問題又來了:把公共函數(shù)定義在全局作用域中枯饿,而僅僅只是為了供對象調(diào)用酝锅,看起來似乎有些小題大做了。而更讓人感到違和的是奢方,如果一個對象有很多公共函數(shù)搔扁,或者把所有對象的公共函數(shù)定義都放在全局作用域中,暫且不說我們這個自定義的引用類型毫無封裝性可言蟋字,更有可能會與全局函數(shù)的定義混在一起難以區(qū)分稿蹲,從而增加了代碼的維護(hù)成本。因此我們?nèi)孕枰乙粋€具有更好封裝性的解決方案鹊奖。

四苛聘、原型模式

我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象(原型對象)忠聚,而這個對象的用途是可以包含由特定類型的所有實(shí)例共享的屬性和方法设哗。因此產(chǎn)生了原型模式:

function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};

var person1 = new Person();
person1.sayName();        //Nicholas

var person2 = new Person();
person2.sayName();        //Nicholas

alert(person1.sayName == person2.sayName)    //true

由于原型對象的存在,我們可以在構(gòu)造函數(shù)中什么都不寫两蟀,只要是通過構(gòu)造函數(shù)創(chuàng)建的對象實(shí)例(new操作符)网梢,都能夠與函數(shù)的原型對象相關(guān)聯(lián),從而訪問原型對象中的屬性和方法赂毯。這也是為什么person1.sayName == person2.sayName战虏,因?yàn)樗鼈冊L問的屬性和方法都是位于原型對象中的同一份。

每當(dāng)創(chuàng)建一個函數(shù)時欢瞪,原型對象會自動獲得一個屬性:constructor,該屬性指向prototype所在函數(shù)徐裸,即在本例中遣鼓,Person.prototype.constructor == Person當(dāng)利用構(gòu)造函數(shù)創(chuàng)建一個對象實(shí)例時重贺,創(chuàng)建出的對象會自動獲得一個指向當(dāng)前構(gòu)造函數(shù)原型對象的內(nèi)部指針[[prototype]](雖然無法訪問但卻是真實(shí)存在的)骑祟,這也解釋了為什么所有通過構(gòu)造函數(shù)創(chuàng)建的實(shí)例能夠訪問同一個原型對象中的屬性和方法。

再看JavaScript引擎是如何搜索某個對象的某個屬性的:每當(dāng)代碼讀取某個對象的某個屬性時气笙,都會執(zhí)行一次搜索次企,目標(biāo)是具有給定名字的屬性。搜索先從對象實(shí)例本身開始潜圃。如果在實(shí)例中找到了具有給定名字的屬性缸棵,則返回該屬性的值;否則繼續(xù)搜索
[[prototype]]內(nèi)部指針指向的原型對象谭期,在原型對象中查找具有給定名字的屬性堵第。如果在原型對象中找到了該屬性吧凉,則返回原型對象中的該屬性值。

原型模式利用了所有對象實(shí)例訪問同一個原型對象的機(jī)制來解決內(nèi)存浪費(fèi)問題踏志,不過在前面的例子中阀捅,每添加一個屬性和方法都要敲一遍Person.prototype。為減少冗余代碼针余,也為視覺上更好的封裝性饲鄙,更常見的做法是用一個包含所有屬性和方法的對象字面量來重寫整個原型對象:

function Person() {
}

Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

然而這樣又會帶來一個問題,那就是利用對象字面量來重寫Person.prototype后圆雁,Person.prototype.constructor已經(jīng)不再指向Person了忍级;這是因?yàn)槔脤ο笞置媪縿?chuàng)建出的對象是Object()構(gòu)造函數(shù)創(chuàng)建出的實(shí)例,其prototype.constructor指向的是Object()構(gòu)造函數(shù)摸柄;因此颤练,如果constructor十分重要的話,還需要將其顯式設(shè)置回原來的值:

function Person() {
}

Person.prototype = {
    construct: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

模式評價:基于原型對象的特點(diǎn)驱负,原型模式可以將自定義類型的方法定義在原型對象內(nèi)部嗦玖,而不用再將其放到全局作用域中,因此從這一點(diǎn)考慮跃脊,它是比構(gòu)造函數(shù)模式更優(yōu)的解決方案宇挫;然而原型模式也并非沒有缺點(diǎn),那就是它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)的這一環(huán)節(jié)酪术,結(jié)果使得所有實(shí)例在默認(rèn)情況下都取得相同的屬性值器瘪,每個對象無法擁有自己獨(dú)特的屬性內(nèi)容,這顯然是與“利用相同模板創(chuàng)建不同對象”的理念背道而馳的绘雁。因此這也正是很少看到有人單獨(dú)使用原型模式創(chuàng)建對象的原因所在橡疼。

五、組合使用構(gòu)造函數(shù)模式和原型模式

為了解決原型模式所遇到的困境庐舟,自然而然會想到利用原型對象定義公共方法欣除,而把共享的屬性放到構(gòu)造函數(shù)中的方式來創(chuàng)建對象,而這正是組合使用構(gòu)造函數(shù)模式和原型模式的思路:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}

Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
};

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

這樣一來挪略,每個對象都有自己的屬性历帚,但同時又能共享一份相同的方法,最大限度地節(jié)約了內(nèi)存杠娱。
模式評價:這種混成模式集構(gòu)造函數(shù)模式和原型模式各自之長挽牢,是目前JavaScript中使用最廣泛、認(rèn)同度最高的一種創(chuàng)建自定義類型的方法摊求∏莅危可以說,這是用來定義引用類型的一種默認(rèn)模式

六奏赘、動態(tài)原型模式

即便是已經(jīng)擁有了如此好的用來定義引用類型的設(shè)計(jì)模式寥闪,但習(xí)慣于使用Java/C++等語言的開發(fā)人員可能仍會認(rèn)為:組合使用構(gòu)造函數(shù)模式和原型模式的設(shè)計(jì)模式感覺還是和單獨(dú)使用構(gòu)造函數(shù)模式差不多,前者雖然比后者好那么一些磨淌,但還是沒法做到徹底地封裝疲憋,畢竟構(gòu)造函數(shù)和原型對象依然是分開定義的,從這一點(diǎn)來說梁只,兩者并沒有多大差別缚柳。為了做到將兩者結(jié)合到一起,實(shí)現(xiàn)徹底的封裝搪锣,于是就有了動態(tài)原型模式:

function Person(name, age, job) {
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
    
    //方法
    if(typeof this.sayName != "function") {
        //所有的公有方法都在這里定義
        Person.prototype.sayName = function() {
            alert(this.name);
        }秋忙;

        Person.prototype.sayJob = function() {
            alert(this.job);
        };


        Person.prototype.sayAge = function() {
            alert(this.age);
        };
    }
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();        //Nicholas
person2.sayName();        //Greg

動態(tài)原型模式把所有信息都封裝到了構(gòu)造函數(shù)中,既通過在構(gòu)造函數(shù)中初始化原型(只會在第一次調(diào)用構(gòu)造函數(shù)創(chuàng)建對象時進(jìn)行)构舟,也保持了同時使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn)灰追。

我們可以分析一下該模式創(chuàng)建對象實(shí)例的過程:

  1. 首先通過new Person()調(diào)用構(gòu)造函數(shù),此時立刻創(chuàng)建了一個Person類型的對象狗超。
  2. 新創(chuàng)建的對象獲得了指向構(gòu)造函數(shù)原型對象的指針弹澎,此時的原型對象中只有constructor屬性,且指向Person函數(shù)努咐。
  3. Person構(gòu)造函數(shù)內(nèi)部的作用域賦給該對象苦蒿,即此時函數(shù)內(nèi)部的this指向了該對象。
  4. 進(jìn)入構(gòu)造函數(shù)執(zhí)行內(nèi)部語句渗稍,首先設(shè)置新對象的name,age,job屬性
  5. 隨后搜索并判斷新對象中是否已存在有效的sayName()函數(shù)佩迟,如果不存在,說明原型對象中還沒有添加該方法竿屹,此時創(chuàng)建原型對象中的對應(yīng)方法报强。
  6. 由于新對象中保存的是指向原型對象的指針,所以在原型對象中添加的方法能被新對象動態(tài)訪問到
  7. 返回新對象并退出構(gòu)造函數(shù)

構(gòu)造函數(shù)中通過檢查某個應(yīng)該存在的方法是否有效拱燃,來決定是否需要初始化原型秉溉;這是一個非常巧妙的策略。若把所有需要初始化的公共方法和公共屬性都放在一起扼雏,僅僅只需要檢查其中一個即可知道是否已執(zhí)行過這段代碼坚嗜。因此在上例中夯膀,只有第一次創(chuàng)建對象person1時才會初始化原型诗充,而當(dāng)創(chuàng)建對象person2時,由于原型已經(jīng)初始化了诱建,所以將不再重復(fù)此過程蝴蜓。

不過需要特別注意的是,該模式下不能使用對象字面量構(gòu)造原型,如下面這樣:

function Person(name, age, job) {
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
    
    //方法
    if(typeof this.sayName != "function") {
        //不能用這樣的方法初始化原型
        Person.prototype = {
            sayName: function() {
                alert(this.name);
            },

            sayJob: function() {
                alert(this.job);
            },

            sayAge: function() {
                alert(this.age);
            }
        };
    }
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();        //出錯
person2.sayName();        //出錯

這是因?yàn)椋舭言蛯ο蟮某跏蓟^程放到構(gòu)造函數(shù)中茎匠,那么當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建對象時格仲,對象的創(chuàng)建總是先于原型對象的初始化的(在執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼前對象就已經(jīng)完成了創(chuàng)建),由于對象獲得指向原型對象的指針發(fā)生在對象創(chuàng)建時刻诵冒,所以在這種情況下凯肋,創(chuàng)建的對象早已指向了原型對象,對于之前的情況汽馋,由于Person.prototype和新對象的[[prototype]]指向同一個對象侮东,所以通過Person.prototype修改原型對象能被新對象的[[prototype]]訪問到;然而使用對象字面量時豹芯,Person.prototype通過賦值的方式指向了另一個對象悄雅,但此時[[prototype]]仍指向初始的原型對象,這也是為什么person1.sayName()出錯的原因铁蹈。

總結(jié):

通過對各個模式的分析發(fā)現(xiàn)宽闲,組合使用構(gòu)造函數(shù)模式和原型模式動態(tài)原型模式是所有介紹過的模式中最適合用來創(chuàng)建自定義類型對象的兩個模式。這兩者雖然從本質(zhì)上看是相同的握牧,但是由于實(shí)現(xiàn)細(xì)節(jié)的不同使得它們互有優(yōu)勢容诬。對于前者來說,它能夠使用對象字面量的方式構(gòu)造原型對象我碟,適用于定義具有較多公共方法的對象類型放案,這樣可以簡化代碼,但其構(gòu)造函數(shù)與原型對象是分開定義的矫俺,封裝性一般吱殉。而后者正好與之相反,后者雖不能使用對象字面量創(chuàng)建原型對象厘托,但卻做到了將原型對象的初始化封裝到了構(gòu)造函數(shù)中從而形成一個整體友雳,這樣更加符合OO的思想。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铅匹,一起剝皮案震驚了整個濱河市押赊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌包斑,老刑警劉巖流礁,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罗丰,居然都是意外死亡神帅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門萌抵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來找御,“玉大人元镀,你說我怎么就攤上這事■Γ” “怎么了栖疑?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滔驶。 經(jīng)常有香客問我遇革,道長,這世上最難降的妖魔是什么揭糕? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任澳淑,我火速辦了婚禮,結(jié)果婚禮上插佛,老公的妹妹穿的比我還像新娘杠巡。我一直安慰自己,他們只是感情好雇寇,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布氢拥。 她就那樣靜靜地躺著,像睡著了一般锨侯。 火紅的嫁衣襯著肌膚如雪嫩海。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天囚痴,我揣著相機(jī)與錄音叁怪,去河邊找鬼。 笑死深滚,一個胖子當(dāng)著我的面吹牛奕谭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痴荐,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼血柳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了生兆?” 一聲冷哼從身側(cè)響起难捌,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸦难,沒想到半個月后根吁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡合蔽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年击敌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辈末。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡愚争,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挤聘,到底是詐尸還是另有隱情轰枝,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布组去,位于F島的核電站鞍陨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏从隆。R本人自食惡果不足惜诚撵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望键闺。 院中可真熱鬧寿烟,春花似錦、人聲如沸辛燥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挎塌。三九已至徘六,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榴都,已是汗流浹背待锈。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘴高,地道東北人竿音。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像拴驮,于是被迫代替她去往敵國和親谍失。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345