進擊的 JavaScript(四) 之 閉包與作用域

更改記錄:

19年 11月27日昔穴,修改!

上一節(jié)說了執(zhí)行上下文冬念,這節(jié)咱們就乘勝追擊來搞搞閉包!頭疼的東西讓你不再頭疼牧挣!

名詞解釋:

變量對象

變量對象是根據(jù)(Variable Object) 來翻譯過來的急前,也可以翻譯成可變對象, 就是保存變量的對象,活動對象瀑构,閉包對象都保存著變量裆针,因此也可以稱為變量對象。

注:這里解釋下寺晌,是因為各個書中對這幾個名詞的使用世吨,搞的好遠。

執(zhí)行上下文

是根據(jù)(Execution Context)翻譯過來的呻征,也可譯為執(zhí)行環(huán)境耘婚。
在函數(shù)執(zhí)行時,就會首先創(chuàng)建執(zhí)行上下文來運行代碼陆赋。

活動對象

是根據(jù)(Activation Object)翻譯過來的沐祷,也可譯為激活的對象
在函數(shù)執(zhí)行時攒岛,創(chuàng)建的變量對象赖临,不僅含有變量,還有特殊的this灾锯,arguments兢榨。

閉包

是根據(jù)(closure) 翻譯過來的,也可譯為 閉合顺饮,使結(jié)束等吵聪,我認為可以理解為 封閉的環(huán)境
把當(dāng)前作用域外的環(huán)境封閉起來领突,以備 其他作用域環(huán)境使用暖璧。

總結(jié):好多名詞都是英譯過來的,看原著或根據(jù)上下文來理解才是這些單詞真正的意思君旦,只可意會不可言傳澎办。嘲碱。。

一局蚀、函數(shù)也是引用類型的麦锯。

function f(){ console.log("not change") };

var ff = f;
 
function f(){ console.log("changed") };

ff();

//"changed"
//ff 保存著函數(shù) f 的引用,改變f 的值琅绅, ff也變了

//來個對比劫灶,估計你就明白了。
var f = "not change";

var ff = f;

f = "changed";

console.log(ff);

//"not change"
//ff 保存著跟 f 一樣的值请敦,改變f 的值秃嗜, ff 不會變

其實,就是引用類型 和 基本類型的 區(qū)別澎羞。


二髓绽、函數(shù)創(chuàng)建一個參數(shù),就相當(dāng)于在其內(nèi)部聲明了該變量

function f(arg){
    console.log(arg)
}

f();

//undefined

function f(arg){
    arg = 5;
    console.log(arg);
}
f();

//5


三妆绞、參數(shù)傳遞顺呕,就相當(dāng)于變量復(fù)制(值的傳遞)

基本類型時,變量保存的是數(shù)據(jù)括饶,引用類型時株茶,變量保存的是內(nèi)存地址。參數(shù)傳遞图焰,就是把變量保存的值 復(fù)制給 參數(shù)启盛。

var o = { a: 5 };

function f(arg){
    arg.a = 6;
}

f(o);

console.log(o.a);
//6


四、垃圾收集機制

JavaScript 具有自動垃圾收集機制楞泼,執(zhí)行環(huán)境會負責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存驰徊。函數(shù)中,正常的局部變量和函數(shù)聲明只在函數(shù)執(zhí)行的過程中存在堕阔,當(dāng)函數(shù)執(zhí)行結(jié)束后棍厂,就會釋放它們所占的內(nèi)存(銷毀變量和函數(shù))。

而js 中 主要有兩種收集方式:

  1. 標(biāo)記清除(常見) //給變量標(biāo)記為“進入環(huán)境” 和 “離開環(huán)境”超陆,回收標(biāo)記為“離開環(huán)境”的變量牺弹。
  2. 引用計數(shù) // 一個引用類型值,被賦值給一個變量时呀,引用次數(shù)加1张漂,通過變量取得引用類型值,則減1谨娜,回收為次數(shù)為0 的引用類型值航攒。

知道個大概情況就可以了,《JavaScript高級程序設(shè)計 第三版》 4.3節(jié) 有詳解趴梢,有興趣漠畜,可以看下币他。.


五、作用域

在 JavaScript 中, 作用域(scope憔狞,或譯有效范圍)顧名思義就是變量和函數(shù)的作用范圍(可訪問范圍)蝴悉。

  • 作用域可以實體化為一個 可變對象(Variable Object 變量對象)

  • JavaScript中的作用域有:全局作用域局部作用域(函數(shù)作用域)。ES6 新增了塊級作用域

全局作用域(Global Scope)

(1)不在任何函數(shù)內(nèi)定義的變量就具有全局作用域瘾敢。(非嚴(yán)格模式下)

(2)實際上拍冠,JavaScript默認有一個全局對象window,全局作用域的變量實際上被綁定到window的一個屬性簇抵。

局部作用域(Local Scope)

(1)JavaScript的作用域是通過函數(shù)來定義的庆杜,在一個函數(shù)中定義的變量只對這個函數(shù)內(nèi)部可見,稱為函數(shù)(局部)作用域正压。

塊級作用域
塊級作用域指在If語句欣福,switch語句,循環(huán)語句等語句塊中定義變量焦履,這意味著變量不能在語句塊之外被訪問。

六雏逾、函數(shù)跟作用域鏈間的關(guān)系

每個函數(shù)都有一個[[scope]] 的內(nèi)部屬性(可以通過console.dir(fn)嘉裤,來查看),它保存著作用域鏈(一個對象數(shù)組)栖博,而作用域鏈中是一個個可變對象(Variable Object 變量對象)(一個保存當(dāng)前作用域中用到的變量屑宠,函數(shù)等的對象)。當(dāng)函數(shù)創(chuàng)建時仇让,一個代表全局環(huán)境的可變對象會被插入到作用域的第一個位置典奉。該全局可變對象保存著window,navigator丧叽,document 等卫玖。

例如如下 聲明一個全局函數(shù)

function add(num1, num2) {
    return num1 + num2
}
UTOOLS1575003062214.png

當(dāng)函數(shù)執(zhí)行時,會創(chuàng)建執(zhí)行上下文(執(zhí)行環(huán)境)踊淳,隨后創(chuàng)建一個執(zhí)行上下文對象假瞬,它有自己的作用鏈。剛開始迂尝,它會用函數(shù)自身的 [[scope]] 中的作用域鏈初始化自己(也就是復(fù)制)脱茉。

隨后一個活動對象被創(chuàng)建(也可以說是變量對象,可變對象)垄开,它保存著當(dāng)前函數(shù)作用域里的變量琴许,arguments,this 等溉躲。最后榜田,該活動對象會被推到執(zhí)行上下文的作用域鏈的最前端寸认。

_Blank UML (1).png

注:執(zhí)行上下文(執(zhí)行環(huán)境)在函數(shù)執(zhí)行完畢后就會被銷毀,里面的作用域鏈串慰,變量偏塞,函數(shù),活動對象邦鲫,this 等也會一同銷毀灸叼。

七、作用域鏈查找

在函數(shù)執(zhí)行過程中庆捺,每遇到一個變量古今,都會經(jīng)歷一次標(biāo)識符解析過程以決定從那里獲取存儲數(shù)據(jù)。該過程搜索執(zhí)行環(huán)境的作用域鏈滔以,查找同名的標(biāo)識符捉腥。搜索過程從作用域鏈頭部開始也就是當(dāng)前運行的作用域。如果找到你画,就使用這個標(biāo)識符對應(yīng)的變量抵碟;如果沒找到,繼續(xù)搜索作用域鏈中的下一個對象坏匪。搜索過程會持續(xù)進行拟逮,直到找到標(biāo)識符,若無法搜索到匹配的對象适滓,那么標(biāo)識符將被視為未定義的敦迄。

八、閉包函數(shù) 與 閉包對象

當(dāng)函數(shù)嵌套時凭迹,例如有一個A函數(shù)罚屋,內(nèi)部有個v1 的變量,有一個B函數(shù),B 中使用了v1 變量嗅绸。這時脾猛,為了讓 B 執(zhí)行時,能訪問 v1(其實就是為了形成作用域鏈)朽砰,會有以下兩個變化:

  • 形成一個閉包函數(shù)尖滚,生成一個閉包對象 A,包含了 B 中用到 v1 變量
  • 在B 閉包函數(shù)的[[scope]] 屬性中 推入 閉包對象A瞧柔。
function A(){
    var v1 = 666
    
    function B() {
        return v1
    }
    
    console.dir(B)
    
    B()
}

A()

執(zhí)行結(jié)果漆弄,看函數(shù)的[[scope]] 屬性:


UTOOLS1575253977522.png

注:可以通過 debugger 來在谷歌瀏覽器控制臺里看。具體怎么用造锅,可以自行百度撼唾。

現(xiàn)在來分析一下過程:

1、首先哥蔚,A 函數(shù)執(zhí)行倒谷,一開始 它的[[scope]] 內(nèi)的作用域鏈中只有全局的可變對象蛛蒙,然后 創(chuàng)建一個執(zhí)行上下文對象,有一個作用域鏈渤愁,根據(jù) [[scope]] 復(fù)制來 來初始化自己牵祟。

_Blank UML.png

2、創(chuàng)建 A 函數(shù)的活動對象抖格,并推到 執(zhí)行上下文對象的作用域鏈中诺苹。

UTOOLS1575253109483.png

3、當(dāng)發(fā)現(xiàn) B 中用到 v1 時雹拄,B 就會變成一個封閉的函數(shù)(閉包函數(shù))收奔,然后,生成一個關(guān)于A 函數(shù)的封閉對象(閉包對象)滓玖,保存著 v1(因為它存在于A坪哄,在B中使用)。隨后势篡,把這個封閉的對象推到 B 函數(shù) 的[[scope]] 作用域鏈中翩肌。

UTOOLS1575250075918.png

注:這時,B 函數(shù)還沒有執(zhí)行殊霞。至于什么機制導(dǎo)致js 能夠發(fā)現(xiàn)未執(zhí)行的函數(shù)內(nèi)使用了 A 函數(shù)內(nèi)的變量摧阅,目前的知識還得不到答案。

4绷蹲、當(dāng)B 函數(shù)執(zhí)行時,創(chuàng)建執(zhí)行上下文顾孽,創(chuàng)建執(zhí)行上下文對象祝钢,初始化執(zhí)行上下文對象的作用域鏈(復(fù)制B 函數(shù)的[[scope]] 屬性)。

_Blank UML.png

5若厚、隨后創(chuàng)建一個活動對象拦英,并推到 執(zhí)行上下文對象的作用域鏈中。

_Blank UML.png

這樣测秸,B 在執(zhí)行時疤估,就可以訪問 v1 了,因為在一個作用域鏈中霎冯。

下面來總結(jié)下作用鏈的變化過程:

  1. 全局下的 A函數(shù)執(zhí)行時铃拇,內(nèi)部的[[scope]] 保存的作用域鏈只有一個全局的變量對象。創(chuàng)建A 函數(shù)的執(zhí)行上下文對象沈撞,根據(jù) [[scope]] 復(fù)制初始化 A函數(shù)的 執(zhí)行上下文對象的作用域鏈慷荔。
  2. 創(chuàng)建 A 函數(shù)的活動對象,推到 A函數(shù)執(zhí)行上下文對象的作用域鏈前端缠俺。
  3. 當(dāng)發(fā)現(xiàn) A 函數(shù)內(nèi)部(不管層級多深)有 一個函數(shù)使用了 A函數(shù)內(nèi)變量或函數(shù)显晶。
  4. A 內(nèi)(不管層級多深)所有函數(shù) 都會形成 閉包函數(shù)贷岸。
  5. 然后創(chuàng)建一個關(guān)于 A 的閉包對象,對象內(nèi)含有被使用的變量或函數(shù)(通過復(fù)制)磷雇。
  6. 最后把該閉包對象 推到 所有閉包函數(shù)的 [[scope]] 內(nèi)偿警。

可以得出以下結(jié)論:

  • 有兩個作用域鏈,一個存與函數(shù)的[[scope]] 中唯笙,用來保存作用域螟蒸,以備執(zhí)行上下文對象初始化自身作用域鏈。
  • 執(zhí)行上下文對象中的作用域鏈睁本,會添加活動函數(shù)尿庐,作用域鏈的查找,查的就是這條作用域鏈呢堰。(一般我們說的作用域鏈就是指這條)
  • 活動函數(shù)只會存在于執(zhí)行上下文對象的作用域鏈中抄瑟。
  • 有閉包函數(shù)和閉包對象,閉包函數(shù)的[[scope]] 保存閉包對象枉疼,而閉包對象皮假,封閉的是 父或祖級函數(shù)作用域中的變量或?qū)ο?/strong>。
  • 閉包函數(shù)的存在是因為 執(zhí)行上下文環(huán)境 會在執(zhí)行完后銷毀骂维,而其中的作用域鏈惹资,活動對象,變量等等就丟失了航闺,通過閉包函數(shù) 就可以保存著作用域鏈褪测,而鏈中的變量對象又保存著變量,函數(shù)等潦刃。

來一個難一點的例子侮措,大家可以先自己分析分析。

function A(){
  var va = 'aaa'
  
  function B() {
    var vb = 'bbb'

    function C() {
      var vc1 = 'ccc'

      return va
    }

    function D() {
      var vd = 'ddd'
      
      return vb
    }

    console.dir(C)
    console.dir(D)

    C()

  }
  
  console.dir(B)

  function E () {
    var vd = 'eee'
  }

  console.dir(E)

  B()
}

console.dir(A)

A()

根據(jù)上面分析乖杠,可以得出各個函數(shù)的[[scope]]:

  • A 只有一個全局變量對象
  • B 和 E 有兩個變量對象分扎,關(guān)于 A 的閉包對象,全局變量對象胧洒。
  • C 和 D 有三個變量對象畏吓,關(guān)于 B 的閉包對象,關(guān)于 A 的閉包對象卫漫,全局變量對象菲饼。

控制臺:


未標(biāo)題-1.png

通過 debugger 來單步調(diào)試,無非就是能看到每個執(zhí)行環(huán)境內(nèi)的作用域鏈中 含有 活動對象汛兜。

有興趣的可以試試巴粪。

至于閉包的內(nèi)存泄漏,這里面牽扯到 js 的垃圾回收機制。不過可以看到肛根,[[scope]] 中保存著 變量辫塌,如果 該變量 占的內(nèi)存不被釋放,一旦這樣的情況過多派哲,內(nèi)存占用過大臼氨,就會造成內(nèi)存泄漏 和 性能問題。

九芭届、閉包的概念

一般說的閉包指的都是閉包函數(shù)储矩。

引用高程(《JavaScript高級程序設(shè)計》)中關(guān)于閉包說法:

閉包是指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù)

通過上面說的那么多,你品褂乍,你細品持隧。。逃片。


十屡拨、閉包的本質(zhì)

我認為就是為了形成作用域鏈。你品褥实,你細品呀狼。。损离。

再來個有趣經(jīng)典的例子:

function timer () {
  for (var i=1; i<=5; i++) {
    setTimeout(function(){
      console.log(i);
    },i*1000);
  }
}

timer()

//每隔一秒輸出一個6,共5個哥艇。

是不是跟你想的不一樣?其實僻澎,這個例子重點就在setTimeout函數(shù)上貌踏,這個函數(shù)的第一個參數(shù)接受一個函數(shù)作為回調(diào)函數(shù),這個回調(diào)函數(shù)并不會立即執(zhí)行窟勃,它會在當(dāng)前代碼執(zhí)行完哩俭,并在給定的時間后執(zhí)行。這樣就導(dǎo)致了上面情況的發(fā)生拳恋。

注:這里用一個函數(shù)包裹起來了,這樣砸捏,你可以通過 debugger谬运,會發(fā)現(xiàn),這里也形成閉包了垦藏。閉包函數(shù)是每一個匿名函數(shù)梆暖,閉包對象是是關(guān)于timer 的,保存著變量 i掂骏。

可以下面對這個例子進行變形轰驳,可以有助于你的理解把:

function timer () {
  var i = 1;
  while(i <= 5){
    setTimeout(function(){
      console.log(i);
    },i*1000)

    i = i+1;
  }
}

timer()

正因為,setTimeout里的第一個函數(shù)不會立即執(zhí)行,當(dāng)這段代碼執(zhí)行完之后级解,i 已經(jīng) 被賦值為6了(等于5時冒黑,進入循環(huán),最后又加了1)勤哗,所以 這時再執(zhí)行setTimeout 的回調(diào)函數(shù)抡爹,讀取 i 的值,回調(diào)函數(shù)作用域內(nèi)沒有i芒划,向上讀取冬竟,上級作用域內(nèi)i的值就是6了。但是 i * 1000民逼,是立即執(zhí)行的泵殴,所以,每次讀的 i 值 都是對的拼苍。

這時候笑诅,就需要再用個閉包函數(shù)來保存每個循環(huán)時 i 不同的值。

function makeClosures(i){     // 這個函數(shù)使用了 上級作用域中的 `i`映屋,形成閉包函數(shù)苟鸯。
    var i = i;    //這步是不需要的,為了讓看客們看的輕松點
    return function(){
        console.log(i);     //匿名沒有執(zhí)行棚点,它可以訪問i 的值早处,保存著這個i 的值。
    }
}

function timer() {
  for (var i=1; i<=5; i++) {
    setTimeout(makeClosures(i),i*1000);  
    
    //這里簡單說下瘫析,這里makeClosures(i)砌梆, 是函數(shù)執(zhí)行,并不是傳參贬循,不是一個概念
    //每次循環(huán)時咸包,都執(zhí)行了makeClosures函數(shù),形成一個閉包函數(shù)杖虾,保存含有 `i` 的閉包對象(這個例子就是 5個 閉包函數(shù)保存各自的閉包對象)烂瘫。
    //然后每次都返回了一個沒有被執(zhí)行的匿名函數(shù),(這里就是返回了5個匿名函數(shù))。
    //每個匿名函數(shù)都是一個局部作用域奇适,它的上級作用域就是 makeClosures 閉包函數(shù)坟比。
    //因此,每個匿名函數(shù)執(zhí)行時嚷往,讀取`i`值葛账,都是上級作用域內(nèi)保存的值,是不一樣的皮仁。所以籍琳,就得到了想要的結(jié)果
  }
}

timer()

//1
//2
//3
//4
//5

你可能在別處菲宴,或者自己想到了下面這種解法:

for (var i=1; i<=5; i++) {
    (function(i){
        setTimeout(function(){
            console.log(i);
        },i*1000);
    })(i);
}

這個例子不僅利用了閉包,而且還利用了立即執(zhí)行函數(shù) 來模擬 函數(shù)作用域 來解決的趋急。

做下變形喝峦,你再看看:

for (var i=1; i<=5; i++) {
    function f(i){
        setTimeout(function(){
            console.log(i);
        },i*1000);
    };
    
    f(i);
}




附錄:

其實這道題,知道ES6let 關(guān)鍵詞宣谈,估計也想到了另一個解法:

for (let i=1; i<=5; i++) {   //這里的關(guān)鍵就是使用的let 關(guān)鍵詞愈犹,來形成塊級作用域
    setTimeout(function(){
        console.log(i);
    },i*1000);
}

我不知道,大家有沒有疑惑啊闻丑,為啥使用了塊級作用域就可以了呢漩怎。反正我當(dāng)初就糾結(jié)了半天。

18年 11月 2日修正:

這個答案的關(guān)鍵就在于 塊級作用域的規(guī)則了嗦嗡。它讓let聲明的變量只在{}內(nèi)有效勋锤,外部是訪問不了的。

做下變形侥祭,這個是為了方便理解的叁执,事實并非如此:

for (var i=1; i<=5; i++) {
    let j = i;
    setTimeout(function(){
        console.log(j);
    },j*1000);
}

當(dāng)for 的() 內(nèi)使用 let時,for 循環(huán)就存在兩個作用域矮冬,() 括號里的父作用域谈宛,和 {} 中括號里的 子作用域。

每次循環(huán)都會創(chuàng)建一個 子作用域胎署。保存著父作用域傳來的值吆录,這樣,每個子作用域內(nèi)的值都是不同的琼牧。當(dāng)setTimeout 的匿名函數(shù)執(zhí)行時恢筝,自己的作用域沒有i 的值,向上讀取到了該 子作用域i 值巨坊。因此每次的值才會不一樣撬槽。

你要是喜歡折騰,你會發(fā)現(xiàn)趾撵,塊級作用域的表現(xiàn)跟函數(shù)作用域一樣侄柔,子作用域中使用它的變量,它也會形成一個塊級對象占调,被寫入到 函數(shù)的 [[scope]] 中勋拟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妈候,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挂滓,老刑警劉巖苦银,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡幔虏,警方通過查閱死者的電腦和手機纺念,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來想括,“玉大人陷谱,你說我怎么就攤上這事∩冢” “怎么了烟逊?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铺根。 經(jīng)常有香客問我宪躯,道長,這世上最難降的妖魔是什么位迂? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任访雪,我火速辦了婚禮,結(jié)果婚禮上掂林,老公的妹妹穿的比我還像新娘臣缀。我一直安慰自己,他們只是感情好泻帮,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布精置。 她就那樣靜靜地躺著,像睡著了一般刑顺。 火紅的嫁衣襯著肌膚如雪氯窍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天蹲堂,我揣著相機與錄音狼讨,去河邊找鬼。 笑死柒竞,一個胖子當(dāng)著我的面吹牛政供,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朽基,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼布隔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了稼虎?” 一聲冷哼從身側(cè)響起衅檀,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霎俩,沒想到半個月后哀军,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沉眶,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年杉适,在試婚紗的時候發(fā)現(xiàn)自己被綠了谎倔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡猿推,死狀恐怖片习,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蹬叭,我是刑警寧澤藕咏,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站具垫,受9級特大地震影響侈离,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筝蚕,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一卦碾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧起宽,春花似錦洲胖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腐晾,卻和暖如春叉弦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藻糖。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工淹冰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巨柒。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓樱拴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親洋满。 傳聞我的和親對象是個殘疾皇子晶乔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

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