對閉包的理解

??在正常情況下仗岖,如果定義了一個函數(shù)慰丛,就會產(chǎn)生一個函數(shù)作用域悲幅,在函數(shù)體中的變量會在這個作用域中使用套鹅。一旦函數(shù)執(zhí)行完成站蝠,函數(shù)所占空間就會被回收,存在于函數(shù)中的局部變量同樣被回收卓鹿,回收后將不能被訪問到菱魔。那么如果我們期望在函數(shù)執(zhí)行完成后,函數(shù)中的局部變量仍然可以被訪問到吟孙,該怎么辦呢澜倦?閉包可以實現(xiàn)這個目標,在學(xué)習(xí)閉包前杰妓,我們需要掌握一個概念:執(zhí)行上下文環(huán)境藻治。

1. 執(zhí)行上下文環(huán)境

??JavaScript每段代碼的執(zhí)行都會存在于一個執(zhí)行上下文環(huán)境中,而任何一個執(zhí)行上下文環(huán)境都會存在于整體的執(zhí)行上下文環(huán)境中巷挥。根據(jù)棧先進后出的特點桩卵,全局環(huán)境產(chǎn)生的執(zhí)行上下文會最先壓入棧中,存在于棧底倍宾。當心的函數(shù)產(chǎn)生調(diào)用時吸占,會產(chǎn)生心的執(zhí)行上下文環(huán)境,也會壓入棧中凿宾。當函數(shù)調(diào)用完成后矾屯,這個上下文環(huán)境及其中的數(shù)據(jù)都會被銷毀,并彈出棧初厚,從而進入之前的執(zhí)行上下文環(huán)境中件蚕。
??需要注意的是,處理活躍狀態(tài)的執(zhí)行上下文環(huán)境只能同時有一個产禾,如下圖深色背景部分排作。


執(zhí)行上下文環(huán)境.png

??我們通過以下代碼了解執(zhí)行上下文環(huán)境的變化過程。

var a = 10;//1.進入全局執(zhí)行上下文環(huán)境
var fn = function (x) {
    var c = 10;
    console.info(c + x);
} 
var bar = function (y) {
    var b = 5;
    fn(y + b)//3.進入fn()函數(shù)執(zhí)行上下文環(huán)境
}
bar(20);//2.進入bar()函數(shù)執(zhí)行上下文環(huán)境

從第一行代碼開始亚情,進入全局執(zhí)行上下文環(huán)境妄痪,此時執(zhí)行上下文環(huán)境中只存在全局執(zhí)行上下文環(huán)境。
當代碼執(zhí)行到第十行時楞件,調(diào)用bar()函數(shù)衫生,進入bar()函數(shù)執(zhí)行上下文環(huán)境中。
執(zhí)行到10行后土浸,進入bar()函數(shù)罪针,執(zhí)行到第八行時,執(zhí)行fn()函數(shù)黄伊,進入fn()函數(shù)執(zhí)行上下文環(huán)境中泪酱。
進入fn()中執(zhí)行第五行代碼后,fn()函數(shù)執(zhí)行上下文環(huán)境會被銷毀,從而彈出棧墓阀。
fn()函數(shù)執(zhí)行上下文環(huán)境被銷毀后毡惜,回到bar()函數(shù)執(zhí)行上下文環(huán)境中,執(zhí)行完成第九行后,bar()函數(shù)執(zhí)行上下文環(huán)境也將被銷毀斯撮,從而彈出棧经伙。
最后全局上下文環(huán)境執(zhí)行完畢,棧被清空吮成,流程執(zhí)行結(jié)束橱乱。
上面的這種代碼執(zhí)行完畢辜梳,執(zhí)行上下文環(huán)境將會被銷毀的場景粱甫,是一種比較理想的情況。
有一種情況作瞄,雖然代碼執(zhí)行完畢茶宵,但執(zhí)行上下文環(huán)境卻無法被感覺地銷毀,這就是講到的閉包宗挥。

2. 閉包的概念

??對于閉包的概念乌庶,官方有一個通用的解釋:一個擁有許多變量和綁定了這些變量的執(zhí)行上下文環(huán)境的表達式,通常是函數(shù)契耿。
閉包有兩個明顯特點:

  • 函數(shù)擁有外邊變量的引用瞒大,在函數(shù)返回時,該變量仍處于活躍狀態(tài)搪桂。
  • 閉包作為一個函數(shù)返回時透敌,其執(zhí)行上下文環(huán)境不會被銷毀,仍處于執(zhí)行上下文環(huán)境中踢械。

在JavaScript中存在一種內(nèi)部函數(shù)酗电,即函數(shù)聲明和函數(shù)表達式可以處于另一個函數(shù)的函數(shù)體內(nèi),在內(nèi)部函數(shù)中可以訪問外部函數(shù)聲明的變量内列,在這個內(nèi)部函數(shù)在包含他們的外部函數(shù)之外被調(diào)用時撵术,機會形成閉包。
?? 我們來看下以下代碼话瞧。

    function fn() {
        var max = 10;
        return function bar(x) {
            if (x > max) {
                console.info(x)
            }
        }
    }
    var f1 = fn();
    f1(11);//11

代碼執(zhí)行后嫩与,生成全局上下文環(huán)境,并壓入棧中交排。
代碼執(zhí)行到第九行時蕴纳,進入fn()函數(shù)中,生成fn()函數(shù)執(zhí)行上下文環(huán)境个粱,并將其壓入棧中古毛。
fn()函數(shù)返回一個bar()函數(shù),并將其賦給變量f1。
當代碼執(zhí)行到第10行時稻薇,調(diào)用f1()函數(shù)嫂冻,注意此時是一個關(guān)鍵節(jié)點,f1()函數(shù)包含了對max變量的引用塞椎,而max變量存在于外部函數(shù)fn()中的桨仿,此時fn()函數(shù)執(zhí)行上下文環(huán)境并不會被直接銷毀,依然存在于執(zhí)行上下文環(huán)境中案狠。


閉包執(zhí)行上下文環(huán)境

等到第10行代碼執(zhí)行結(jié)束后服傍,bar()函數(shù)執(zhí)行完畢,bar()函數(shù)執(zhí)行上下文環(huán)境也被銷毀骂铁,同時因為max變量引用會被釋放吹零,fn()函數(shù)執(zhí)行上下文環(huán)境也一同被銷毀。
最后全局執(zhí)行上下文環(huán)境執(zhí)行完畢拉庵,棧被清空灿椅,流程執(zhí)行結(jié)束。
閉包所存在最大的問題就是消耗內(nèi)存钞支,如果閉包使用越來越多茫蛹,內(nèi)存消耗將越來越大。

3. 閉包的用途

??在了解閉包之后烁挟,我們可以結(jié)合閉包的特點婴洼,寫出一些更加簡潔優(yōu)雅的代碼,并且能在某些方面提升代碼的執(zhí)行效率撼嗓。

  • 結(jié)果緩存

在開發(fā)過程中柬采,我們可能會遇到這樣的場景,假如有一個處理很耗時的函數(shù)對象静稻,每次調(diào)用都會消耗很長時間警没。
我們可以將其處理結(jié)果在內(nèi)存中緩存起來。這樣在代碼執(zhí)行時振湾,如果內(nèi)存中有杀迹,則直接返回;如果內(nèi)存中沒有押搪,則調(diào)用函數(shù)進行計算树酪,更新緩存并返回結(jié)果。
因為閉包不會釋放外部變量的引用大州,所以能將外部變量值緩存在內(nèi)存中续语。

    var checkedBox = (function (){
        //緩存的容器
        var cache = {};
        return {
            searchBox: function (id){
                // 如果再內(nèi)存中,則直接返回
                if(id in cache){
                    return `查找的緩存結(jié)果為:${cache[id]}`
                }
                //經(jīng)過一段很耗時的dealFn()函數(shù)處理
                var result = dealFn(id);
                //更新緩存結(jié)果
                cache[id] = result;
                //返回計算的結(jié)果
                return `查找的結(jié)果為:${result}`
            }
        }
    })()
    //處理很耗時的函數(shù)
    function dealFn(id) {
        console.info('這是很耗時的操作')
        return id;
    }
    //兩次調(diào)用searchBox函數(shù)
    console.info(checkedBox.searchBox(1))
    console.info(checkedBox.searchBox(1))

在上面的代碼中厦画,末尾兩次調(diào)用searchBox(1)()函數(shù)疮茄,在第一次調(diào)用時滥朱,id為1的值并未在緩存對象cache中,因為會執(zhí)行很耗時的函數(shù)力试,輸出的結(jié)果為“1”徙邻。
這是很耗時的操作
查找的結(jié)果為:1
而第二次執(zhí)行searchBox(1)函數(shù)時,由于第一次已經(jīng)將結(jié)果更新到cache對象中畸裳,并且該對象引用并未被回收缰犁,因此會直接從內(nèi)存的cache對象中讀取,直接返回“1”怖糊,最后輸出的結(jié)果為“1”帅容。
查找的緩存結(jié)果為:1
這樣并沒有執(zhí)行很耗時的函數(shù),還間接提高了執(zhí)行效率伍伤。

  • 封裝

??在JavaScript中提倡的模塊化思想是希望將具有一定特征的屬性封裝到一起并徘,只需要對外暴露對應(yīng)的函數(shù),并不關(guān)心內(nèi)部邏輯的實現(xiàn)嚷缭。
例如饮亏,我們可以借助數(shù)組實現(xiàn)一個棧耍贾,只對外暴露出表示入棧和出棧的push()函數(shù)和pop()函數(shù)阅爽,以及表示棧長度的size()函數(shù)。

    var stack = (function () {
       //使用數(shù)組模仿棧的實現(xiàn)
        var arr = [];
        //棧
        return{
            push:function (value){
                arr.push(value)
            },
            pop:function () {
                return arr.pop()
            },
            size:function () {
                return arr.length
            }
        }
    })()
    stack.push('abc');
    stack.push('def');
    console.info(stack.size())//2
    stack.pop();
    console.info(stack.size())//1

上面的代碼中存在一個立即執(zhí)行函數(shù)荐开,在函數(shù)內(nèi)部會產(chǎn)生一個執(zhí)行上下文環(huán)境付翁,最后返回一個表示棧的對象并賦給stack變量。在匿名函數(shù)執(zhí)行完畢后晃听,其執(zhí)行上下文環(huán)境并不會被銷毀百侧,因為在對象的push()、pop()能扒、size()等函數(shù)中包含了對arr變量的引用佣渴,arr變量會繼續(xù)存在于內(nèi)存中,所以后面幾次對stack變量的操作會使stack變量的長度產(chǎn)生變化初斑。
接下來我們將通過幾道練習(xí)題加深大家對閉包的理解辛润。

1. ul中有若干個li,每次但擊li,輸出li的索引值
<ul>
   <li>1</li>
   <li>2</li>
   <li>3</li>
   <li>4</li>
   <li>5</li>
</ul>
<script>
   var lis = document.getElementsByTagName('ul')[0].children;
   for (var i = 0; i < lis.length; i++) {
       lis[i].onclick = function () {
           console.log(i);
       };
   }
</script>

但是真正運行后卻發(fā)現(xiàn)见秤,結(jié)果并不如自己所想砂竖,每次單擊后輸出的并不是索引值,而一直都是“5”鹃答。
這是為什么呢乎澄?因為在我們單擊li,觸發(fā)li的click事件之前测摔,for循環(huán)已經(jīng)執(zhí)行結(jié)束了置济,而for循環(huán)結(jié)束的條件就是最后一次i++執(zhí)行完畢,此時i的值為5,所以每次單擊li后返回的都是“5”浙于。
采取使用閉包的方法可以很好地解決這個問題修噪。

    var lis = document.getElementsByTagName('ul')[0].children;
    for (let i = 0; i < lis.length; i++) {
        (function (index) {
            lis[i].onclick = function () {
                console.info(index)
            }
        })(i)
    }

在每一輪的for循環(huán)中,我們將索引值i傳入一個匿名立即執(zhí)行函數(shù)中路媚,在該匿名函數(shù)中存在對外部變量lis的引用黄琼,因此會形成一個閉包。而閉包中的變量index整慎,即外部傳入的i值會繼續(xù)存在于內(nèi)存中脏款,所以當單擊li時,就會輸出對應(yīng)的索引index值裤园。

2. 定時器問題

定時器setTimeout()函數(shù)和for循環(huán)在一起使用撤师,總會出現(xiàn)一些意想不到的結(jié)果,我們看看下面的代碼拧揽。

var arr = ['one', 'two', 'three'];
for(var i = 0; i < arr.length; i++) {
   setTimeout(function () {
       console.log(arr[i]);
   }, i * 1000);
}

在這道題目中剃盾,我們期望通過定時器從第一個元素開始往后,每隔一秒輸出arr數(shù)組中的一個元素。
但是運行過后碌识,我們卻會發(fā)現(xiàn)結(jié)果是每隔一秒輸出一個“undefined”是整,這是為什么呢?
setTimeout()函數(shù)與for循環(huán)在調(diào)用時會產(chǎn)生兩個獨立執(zhí)行上下文環(huán)境积蔚,當setTimeout()函數(shù)內(nèi)部的函數(shù)執(zhí)行時,for循環(huán)已經(jīng)執(zhí)行結(jié)束烦周,而for循環(huán)結(jié)束的條件是最后一次i++執(zhí)行完畢尽爆,此時i的值為3,所以實際上setTimeout()函數(shù)每次執(zhí)行時读慎,都會輸出arr[3]的值漱贱。而因為arr數(shù)組最大索引值為2,所以會間隔一秒輸出“undefined”夭委。
通過閉包可以解決這個問題幅狮,代碼如下所示。

var arr = ['one', 'two', 'three'];
for(var i = 0; i < arr.length; i++) {
   (function (time) {
       setTimeout(function () {
           console.log(arr[time]);
       }, time * 1000);
   })(i);
}

通過立即執(zhí)行函數(shù)將索引i作為參數(shù)傳入闰靴,在立即函數(shù)執(zhí)行完成后彪笼,由于setTimeout()函數(shù)中有對arr變量的引用,其執(zhí)行上下文環(huán)境不會被銷毀蚂且,因此對應(yīng)的i值都會存在內(nèi)存中配猫。所以每次執(zhí)行setTimeout()函數(shù)時,i都會是數(shù)組對應(yīng)的索引值0杏死、1泵肄、2捆交,從而間隔一秒輸出“one”“two”“three”。

3. 作用域鏈問題

閉包往往會涉及作用域鏈問題腐巢,尤其是包含this屬性時品追。

var name = 'outer';
var obj = {
   name: 'inner',
   method: function () {
       return function () {
           return this.name;
       }
   }
};
console.log(obj.method()());  // outer

在調(diào)用obj.method()函數(shù)時,會返回一個匿名函數(shù)冯丙,而該匿名函數(shù)中返回的是this.name肉瓦,因為引用到了this屬性,在匿名函數(shù)中胃惜,this相當于一個外部變量泞莉,所以會形成一個閉包。
在JavaScript中船殉,this指向的永遠是函數(shù)的調(diào)用實體鲫趁,而匿名函數(shù)的實體是全局對象window,因此會輸出全局變量name的值“outer”利虫。
如果想要輸出obj對象自身的name屬性挨厚,應(yīng)該如何修改呢?簡單來說就是改變this的指向糠惫,將其指向obj對象本身疫剃。

var name = 'outer';
var obj = {
   name: 'inner',
   method: function () {
       // 用_this保存obj中的this
       var _this = this;
       return function () {
           return _this.name;
       }
   }
};
console.log(obj.method()());  // inner

在method()函數(shù)中利用_this變量保存obj對象中的this,在匿名函數(shù)的返回值中再去調(diào)用_this.name寞钥,此時_this就指向obj對象了慌申,因此會輸出“inner”陌选。

4. 多個相同函數(shù)名問題
// 第一個foo()函數(shù)
function foo(a, b) {
   console.log(b);
   return {
      // 第二個foo()函數(shù)
       foo: function (c) {
          // 第三個foo()函數(shù)
          return foo(c, a);
       }
   }
}
var x = foo(0); x.foo(1); x.foo(2); x.foo(3);
var y = foo(0).foo(1).foo(2).foo(3);
var z = foo(0).foo(1); z.foo(2); z.foo(3);

在上面的代碼中理郑,出現(xiàn)了3個具有相同函數(shù)名的foo()函數(shù),返回的第三個foo()函數(shù)中包含了對第一個foo()函數(shù)參數(shù)a的引用咨油,因此會形成一個閉包您炉。
在完成這道題目之前,我們需要搞清楚這3個foo()函數(shù)的指向役电。
首先最外層的foo()函數(shù)是一個具名函數(shù)赚爵,返回的是一個具體的對象。
第二個foo()函數(shù)是最外層foo()函數(shù)返回對象的一個屬性法瑟,該屬性指向一個匿名函數(shù)冀膝。
第三個foo()函數(shù)是一個被返回的函數(shù),該foo()函數(shù)會沿著原型鏈向上查找霎挟,而foo()函數(shù)在局部環(huán)境中并未定義窝剖,最終會指向最外層的第一個foo()函數(shù),因此第三個和第一個foo()函數(shù)實際是指向同一個函數(shù)酥夭。
理清3個foo()函數(shù)的指向后赐纱,我們再來看看具體的執(zhí)行過程脊奋。
var x = foo(0); x.foo(1); x.foo(2); x.foo(3);
(1)在執(zhí)行foo(0)時,未傳遞b值疙描,所以輸出“undefined”诚隙,并返回一個對象,將其賦給變量x起胰。
在執(zhí)行x.foo(1)時久又,foo()函數(shù)閉包了外層的a值,就是第一次調(diào)用的0效五,此時c=1籽孙,因為第三層和第一層為同一個函數(shù),所以實際調(diào)用為第一層的的foo(1, 0)火俄,此時a為1犯建,b為0,輸出“0”瓜客。
執(zhí)行x.foo(2)和x.foo(3)時适瓦,和x.foo(1)是相同的原理,因此都會輸出“0”谱仪。
第一行輸出結(jié)果為“undefined玻熙,0,0疯攒,0”嗦随。
var y = foo(0).foo(1).foo(2).foo(3);
(2)在執(zhí)行foo(0)時,未傳遞b值敬尺,所以輸出“undefined”枚尼,緊接著進行鏈式調(diào)用foo(1),其實這部分與(1)中的第二部分分析一樣砂吞,實際調(diào)用為foo(1, 0)署恍,此時a為1,b為0蜻直,會輸出“0”盯质。
foo(1)執(zhí)行后返回的是一個對象,其中閉包了變量a的值為1概而,當foo(2)執(zhí)行時呼巷,實際是返回foo(2, 1),此時的foo()函數(shù)指向第一個函數(shù)赎瑰,因此會執(zhí)行一次foo(2, 1)王悍,此時a為2,b為1乡范,輸出“1”配名。
foo(2)執(zhí)行后返回一個對象啤咽,其中閉包了變量a的值為2,當foo(3)執(zhí)行時渠脉,實際是返回foo(3, 2)宇整,因此會執(zhí)行一次foo(3, 2),此時a為3芋膘,b為2鳞青,輸出“2”。
第二行輸出結(jié)果為“undefined为朋,0臂拓,1,2”习寸。
var z = foo(0).foo(1); z.foo(2); z.foo(3);
(3)前兩步foo(0).foo(1)的執(zhí)行結(jié)果與(1)胶惰、(2)的分析相同,輸出“undefined”和“0”霞溪。
foo(0).foo(1)執(zhí)行完畢后孵滞,返回的是一個對象,其中閉包了變量a的值為1鸯匹,當調(diào)用z.foo(2)時坊饶,實際是返回foo(2, 1),因此會執(zhí)行foo(2, 1)殴蓬,此時a為2匿级,b為1,輸出“1”染厅。
執(zhí)行z.foo(3)時痘绎,與z.foo(2)一樣,實際是返回foo(3, 1)糟秘,因此會執(zhí)行foo(3, 1)简逮,此時a為3,b為1尿赚,輸出“1”。
第三行輸出結(jié)果為“undefined蕉堰,0凌净,1,1”屋讶。

4. 小結(jié)

閉包如果使用合理冰寻,在一定程度上能提高代碼執(zhí)行效率;如果使用不合理皿渗,則會造成內(nèi)存浪費斩芭,性能下降轻腺。接下來總結(jié)閉包的優(yōu)點和缺點。

1. 閉包的優(yōu)點
  • 保護函數(shù)內(nèi)變量的安全划乖,實現(xiàn)封裝贬养,防止變量流入其他環(huán)境發(fā)生命名沖突,造成環(huán)境污染琴庵。
  • 在適當?shù)臅r候误算,可以在內(nèi)存中維護變量并緩存,提高執(zhí)行效率迷殿。
2. 閉包的缺點
  • 消耗內(nèi)存:通常來說儿礼,函數(shù)的活動對象會隨著執(zhí)行上下文環(huán)境一起被銷毀,但是庆寺,由于閉包引用的是外部函數(shù)的活動對象蚊夫,因此這個活動對象無法被銷毀,這意味著懦尝,閉包比一般的函數(shù)需要消耗更多的內(nèi)存这橙。
  • 泄漏內(nèi)存: 在IE9之前,如果閉包的作用域鏈中存在DOM對象导披,則意味著該DOM對象無法被銷毀屈扎,造成內(nèi)存泄漏。
function closure() {
   var element = document.getElementById("elementID");
   element.onclick = function () {
       console.log(element.id);
   };
}

??在closure()函數(shù)中撩匕,給一個element元素綁定了click事件鹰晨,而在這個click事件中,輸出了element元素的id屬性止毕,即在onclick()函數(shù)的閉包中存在了對外部元素element的引用模蜡,那么該element元素在網(wǎng)頁關(guān)閉之前會一直存在于內(nèi)存之中,不會被釋放扁凛。
??如果這樣的事件處理的函數(shù)很多忍疾,將會導(dǎo)致大量內(nèi)存被占用,進而嚴重影響性能谨朝。
??對應(yīng)的解決辦法是:先將需要使用的屬性使用臨時變量進行存儲卤妒,然后在事件處理函數(shù)時使用臨時變量進行操作;此時閉包中雖然不直接引用element元素字币,但是對id值的調(diào)用仍然會導(dǎo)致element元素的引用被保存则披,此時應(yīng)該手動將element元素設(shè)置為null。

function closure() {
   var element = document.getElementById("elementID");
   // 使用臨時變量存儲
   var id = element.id;
   element.onclick = function () {
       console.log(id);
   };
   // 手動將元素設(shè)置為null
   element = null;
}

閉包既有好處洗出,也有壞處士复。我們應(yīng)該合理評估,適當使用翩活,盡可能地發(fā)揮出閉包的最大用處阱洪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末便贵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子冗荸,更是在濱河造成了極大的恐慌承璃,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俏竞,死亡現(xiàn)場離奇詭異绸硕,居然都是意外死亡,警方通過查閱死者的電腦和手機魂毁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門玻佩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人席楚,你說我怎么就攤上這事咬崔。” “怎么了烦秩?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵垮斯,是天一觀的道長。 經(jīng)常有香客問我只祠,道長兜蠕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任抛寝,我火速辦了婚禮熊杨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盗舰。我一直安慰自己晶府,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布钻趋。 她就那樣靜靜地躺著川陆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛮位。 梳的紋絲不亂的頭發(fā)上较沪,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音土至,去河邊找鬼购对。 笑死,一個胖子當著我的面吹牛陶因,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垂蜗,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼楷扬,長吁一口氣:“原來是場噩夢啊……” “哼解幽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烘苹,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤躲株,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后镣衡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霜定,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年廊鸥,在試婚紗的時候發(fā)現(xiàn)自己被綠了望浩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡惰说,死狀恐怖磨德,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吆视,我是刑警寧澤典挑,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站啦吧,受9級特大地震影響您觉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜授滓,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一琳水、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧褒墨,春花似錦炫刷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至噩咪,卻和暖如春顾彰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胃碾。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工涨享, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仆百。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓厕隧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吁讨,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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