從閉包和高階函數(shù)初探JS設(shè)計(jì)模式

在前一篇《這些JS設(shè)計(jì)模式中的基礎(chǔ)知識(shí)點(diǎn)你都會(huì)了嗎等舔?》中講到了原型、原型鏈、this指向草雕、call()涩嚣、apply()、bind()以及JS中如何實(shí)現(xiàn)繼承省核,前一篇是必備基礎(chǔ)知識(shí)稿辙,這篇文章將從閉包和高階函數(shù)中初探JavaScript模式。

JavaScript是一門完整的面向?qū)ο蟮木幊陶Z言气忠,JavaScript在設(shè)計(jì)之初參考并引入了Lambda表達(dá)式邻储、閉包和高階函數(shù)等特性赋咽。

而在JavaScript中的一些設(shè)計(jì)模式都依賴閉包和高階函數(shù)來實(shí)現(xiàn),因此非常有必要掌握閉包和高階函數(shù)的知識(shí)點(diǎn)吨娜。

一脓匿、閉包(Closure)

閉包的形成與變量的“作用域(scope)”和“生命周期(lifecycle)”相關(guān),所以先對(duì)這兩個(gè)概念有一個(gè)清晰的認(rèn)識(shí)宦赠。

1.1 變量的作用域

變量的作用域:變量的有效范圍陪毡。

如下示例:

var a = 0;
function func() {
    var a = 1;
    var b = 2;
    console.log(a, b); 
}
func();          // output: 1 2;
console.log(a);  // output: 0
console.log(b);  // output: Uncaught ReferenceError: b is not defined

可以看出在函數(shù)內(nèi)部聲明的變量是局部變量,只在函數(shù)體內(nèi)部執(zhí)行環(huán)境有效勾扭,在函數(shù)外部是無法訪問到的毡琉,并且JS執(zhí)行時(shí)候會(huì)拋出一個(gè)未定義的錯(cuò)誤。

當(dāng)在函數(shù)中聲明一個(gè)變量時(shí)妙色,沒有帶上關(guān)鍵詞 var桅滋,這個(gè)變量就會(huì)變成全局變量,所以推薦大家編程時(shí)候規(guī)范編程(借助TypeScript+Eslint)身辨,變量的聲明盡可能都用 constlet丐谋, 避免不必要的內(nèi)存占用。

在JavaScript中煌珊,函數(shù)可以用來創(chuàng)建函數(shù)作用域号俐,此時(shí)的函數(shù)體內(nèi)部的執(zhí)行環(huán)境可以訪問函數(shù)外部的變量,而外部卻無法訪問函數(shù)體內(nèi)部的變量怪瓶。如果函數(shù)內(nèi)部搜索某個(gè)變量時(shí)萧落,如果該變量不存在,那么就會(huì)在由內(nèi)到外的作用域鏈上尋找該變量是否在對(duì)應(yīng)的作用域上有聲明洗贰,有則返回該變量的值找岖,否則會(huì)返回“Uncaught ReferenceError: variable is not defined

這里大家可以試試在“腦內(nèi)運(yùn)行”下,以加深對(duì)“變量作用域”的理解:

var a = 1;
var func = function() {
    var b = 2;
    var func2 = function() {
        var c = 3;
        console.log(a);
        console.log(b);
        console.log(c);
    }
    func2();
    console.log(c);
}
func();

最后的正確輸出:

1
2
3
Uncaught ReferenceError: c is not defined

1.2 變量的生命周期

如果說變量的作用域是一個(gè)規(guī)則敛滋,那么變量的生命周期就規(guī)則的施行者许布。

變量的生命周期:簡單理解為變量的有效時(shí)間,例如全局變量在程序執(zhí)行的整個(gè)過程中都有效绎晃,函數(shù)中的局部變量在函數(shù)執(zhí)行結(jié)束后被銷毀蜜唾。

function func() {
    var a = 1;      //函數(shù)執(zhí)行完成后將自動(dòng)銷毀
    console.log(a)
}
func();

1.3 閉包改變局部變量的生命周期

首先看個(gè)示例:

function func() {
    var a = 0;      //函數(shù)執(zhí)行完成后將自動(dòng)銷毀
    return function() {
        a = a + 1;
        console.log(a);
    }
}
var f = func();

f();    // output: 1
f();    // output: 2
f();    // output: 3
f();    // output: 4
console.log(a);  // output: Uncaught ReferenceError: a is not defined

函數(shù)執(zhí)行后的輸出結(jié)果看起來有些違背“變量的生命周期”規(guī)則,似乎局部變量a并未被銷毀庶艾,并且在最后的 console.log(a) 代碼執(zhí)行時(shí)候報(bào)了變量 a 未定義袁余。那么變量 a 存儲(chǔ)在什么地方吶?

在執(zhí)行 var f = func(); 的時(shí)候咱揍,f 返回了一個(gè)匿名函數(shù)的引用颖榜,它可以訪問到 func() 被調(diào)用時(shí)產(chǎn)生的環(huán)境,而局部變量 a 一直在這個(gè)環(huán)境中。局部變量 a 還能被外界訪問掩完,所以就有了不被銷毀的理由噪漾。在這里產(chǎn)生了一個(gè)閉包結(jié)構(gòu),局部變量的生命周期被延續(xù)了且蓬。

通過查看 f.prototype中的 scopes(作用域):

f.prototype

函數(shù) f 的作用域有兩個(gè)一個(gè)是全局的欣硼,另一個(gè)是 Closure,在 Closure 中可以看到此時(shí)的變量 a 的值是 4恶阴。也就是說诈胜,局部變量 a,實(shí)際上是被存儲(chǔ)在一個(gè)閉包環(huán)境中冯事。

1.4 閉包的更多作用

“閉包”可以改變局部變量的生命周期耘斩,并且不更改局部變量的作用范圍,這一特性使得閉包的運(yùn)用非常廣泛桅咆。

1.4.1 緩存

例如我們要實(shí)現(xiàn)一個(gè)“乘積”函數(shù),乘法需要較大的計(jì)算資源坞笙,如果每次傳入?yún)?shù)都需要重新計(jì)算將是對(duì)計(jì)算資源的浪費(fèi)岩饼,那么就想到了緩存結(jié)果。

如果用一個(gè)全局變量來存儲(chǔ)結(jié)果薛夜,那么就有些“污染”全局變量籍茧,因?yàn)槌朔e僅用于在“乘積”函數(shù)內(nèi)部,我們還是希望能夠?qū)⒆兞拷档婉詈咸堇剑钥梢越柚]包來實(shí)現(xiàn)寞冯。

const multiplication = (function() {
    const cache = {};
    return function() {
        const args = Array.prototype.join.call(arguments, ',');
        if (args in cache) {
            return cache[args];
        }
        let sum = 1;
        for (let i = 0; i < arguments.length; i++) {
            sum = sum * arguments[i];
        }
        return cache[args] = sum;
    }
})();

multiplication(1,2,3,4);

如此我們?cè)谟?jì)算相同乘法時(shí)候就可以直接通過緩存返回乘積結(jié)果,從而節(jié)省計(jì)算資源晚伙,提高程序性能和穩(wěn)定性吮龄。

軟件開發(fā)講究一個(gè)“高內(nèi)聚,低耦合”咆疗,有些通用方法函數(shù)可以獨(dú)立出來漓帚,因此上面的代碼還可以再優(yōu)化。

const multiplication = (function() {
    const cache = {};

    const calculate = function() {
        let sum = 1;
        for (let i = 0; i < arguments.length; i++) {
            sum = sum * arguments[i];
        }
        return sum;
    }

    return function() {
        const args = Array.prototype.join.call(arguments, ',');
        if (args in cache) {
            return cache[args];
        }
        return cache[args] = calculate.apply(null, arguments);
    }
})();

multiplication(1,2,3,4);

1.4.2 面向?qū)ο缶幊蹋?/h3>
/****************** 寫法1 *******************/
var Person = function() {
    var age = 18;
    return {
        addAge: function() {
            age++;
            console.log('age:', age);
        }
    }
}
var person = Person();

person.addAge();    // output: age: 19
person.addAge();    // output: age: 20
person.addAge();    // output: age: 21


/****************** 寫法2 *******************/
var person = {
    age: 18,
    addAge: function() {
        this.age = this.age + 1;
        console.log('age:', this.age);
    }
};
person.addAge();    // output: age: 19
person.addAge();    // output: age: 20
person.addAge();    // output: age: 21


/****************** 寫法3 *******************/
var Person = function() {
    this.age = 18;
}
Person.prototype.addAge = function() {
    this.age++;
    console.log(this.age);
}
var person = new Person();

person.addAge();    // output: age: 19
person.addAge();    // output: age: 20
person.addAge();    // output: age: 21

閉包特性其實(shí)已經(jīng)在面向?qū)ο蟮木幊田L(fēng)格中得到了體現(xiàn)午磁。

1.5 閉包與內(nèi)存

在面試過程中經(jīng)常被面試官問到:“說說你對(duì)閉包的認(rèn)識(shí)尝抖?”

被面試者經(jīng)常回答道閉包可能會(huì)因?yàn)闆]有被及時(shí)銷毀導(dǎo)致內(nèi)存泄漏迅皇,需要盡量減少閉包的使用昧辽,以及主動(dòng)賦值null及時(shí)釋放內(nèi)存。

因?yàn)閷⒕植孔兞糠诺饺肿兞科溆绊懚际情L期占用了內(nèi)存沒有釋放登颓,所以內(nèi)存泄漏的真正原因并不是因?yàn)槭褂瞄]包搅荞。而內(nèi)存泄漏的關(guān)鍵點(diǎn)在于使用了閉包容易形成“循環(huán)引用”,比如閉包的作用域鏈中保存著一些DOM節(jié)點(diǎn),循環(huán)引用的兩個(gè)對(duì)象都不會(huì)被基于“引用計(jì)數(shù)的垃圾回收機(jī)制”回收內(nèi)存取具。所以其根本原因是對(duì)象的“循環(huán)引用”導(dǎo)致的內(nèi)存泄漏脖隶。

二、高階函數(shù)(HOF)

高階函數(shù)(Higher-Order Function)是至少滿足如下條件之一的函數(shù):

  1. 函數(shù)可以作為參數(shù)被傳遞
  2. 函數(shù)可以作為返回值輸出

在JavaScript中常見于回調(diào)函數(shù)則是作為了參數(shù)被傳遞暇检,閉包則是返回了函數(shù)

2.1 簡單示例

例如一個(gè)單例模式的例子产阱,既將函數(shù)作為參數(shù),也將函數(shù)作為返回值:

const getSingleBuider = function(fn) {
    let instance;
    return function() {
        return instance || (instance = fn.apply(this, arguments));
    }
}

2.2 高階函數(shù)與AOP

AOP(面向切面編程)的主要作用是把一些跟核心業(yè)務(wù)邏輯無關(guān)的功能抽離出來块仆,例如日志統(tǒng)計(jì)构蹬、異常處理、安全控制等悔据。將這些功能抽離后庄敛,再通過“動(dòng)態(tài)織入”的方式摻入業(yè)務(wù)邏輯模塊中。能夠保證業(yè)務(wù)邏輯模塊的高內(nèi)聚科汗,以及抽離的功能能夠很好的復(fù)用藻烤。

在JavaScript中實(shí)現(xiàn)AOP,一般是將一個(gè)函數(shù)“動(dòng)態(tài)織入”另一個(gè)函數(shù)內(nèi)头滔,那么就可以通過咱在前一篇基礎(chǔ)文章《這些JS設(shè)計(jì)模式中的基礎(chǔ)知識(shí)點(diǎn)你都會(huì)了嗎怖亭?》中講到的原型鏈來實(shí)現(xiàn)。

來看一個(gè)簡單的示例來更好理解高階函數(shù)以及AOP:

Function.prototype.before = function(beforeFn) {
    var _self = this;                           // 存儲(chǔ)原函數(shù)的引用
    // 返回原函數(shù)與新函數(shù)的“代理”函數(shù)
    return function() {
        beforeFn.apply(this, arguments);        // 執(zhí)行新函數(shù)
        return _self.apply(this, arguments);    // 執(zhí)行原函數(shù)返回執(zhí)行結(jié)果
    }
}
Function.prototype.after = function(afterFn) {
    var _self = this;
    return function() {
        const result = _self.apply(this, arguments);
        afterFn.apply(this, arguments);
        return result;
    }
}

let func = function() {
    console.log('run');
}

func = func.before(function(){
    console.log('berfore run');
}).after(function() {
    console.log('after run');
});

func();
運(yùn)行結(jié)果

這樣我們就可以在完全不影響原函數(shù)原有邏輯的情況下給加入了新的中間件坤检,類似于Koa的“洋蔥模型”兴猩。

Koa洋蔥模型

使用AOP來給函數(shù)動(dòng)態(tài)添加職責(zé)(功能),這與設(shè)計(jì)模式之一的“裝飾者模式”的思想一致早歇。

2.3 柯里化(Curring)

柯里化又稱“部分求值”倾芝,一個(gè)curring函數(shù)首先會(huì)接受一些參數(shù),接受了這些參數(shù)后箭跳,該函數(shù)不會(huì)立即求值晨另,而是繼續(xù)返回另一個(gè)函數(shù),剛才傳入的參數(shù)在函數(shù)的閉包環(huán)境中存儲(chǔ)起來衅码,待到函數(shù)真正需要被求值的時(shí)候拯刁,之前傳入的參數(shù)都會(huì)被一次性用于求值。

例如面試中會(huì)通過讓大家實(shí)現(xiàn)一個(gè)求和函數(shù)逝段,使用的方法如下:

sum(1)(2)(3); // output: 6

看到這個(gè)我們首先會(huì)想到用高階函數(shù)不斷返回函數(shù)垛玻,讓參數(shù)在閉包中存起來,也就是上述的柯里化奶躯,我們的第一版代碼可能長這樣:

function sum(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        }
    }
}
sum(1)(2)(3);  // output: 6

第一版的代碼看起來不太優(yōu)雅帚桩?如果想要 sum(1)(2)...(1000) 咋辦,不可能去寫一千遍return函數(shù)吧嘹黔,因此想到了遞歸账嚎。

遞歸的次數(shù)依賴于函數(shù)行參的長度莫瞬,所以再來一個(gè)通用的curring,我們實(shí)際上遞歸的是“兩數(shù)求和”這一行為郭蕉,思考也就是可以將函數(shù)柯里化疼邀,那么就可以鏈?zhǔn)浇邮軈?shù)執(zhí)行。

我們先針對(duì)兩數(shù)求和來實(shí)現(xiàn)柯里化

// 柯里化函數(shù)第一版
function curry(fn) {
    // 將傳入的函數(shù)fn從實(shí)參數(shù)組中移除
    const args = Array.prototype.slice.call(arguments, 1);
    // 返回函數(shù)用于接受下一個(gè)參數(shù)
    return function() {
        // 將返回函數(shù)需要接受的下一次入?yún)⒈4娴絥ewArgs中召锈,slice淺拷貝
        const newArgs = args.concat(Array.prototype.slice.call(arguments));
        // 將newArgs參數(shù)放到被柯里化函數(shù)中執(zhí)行
        return fn.apply(this, newArgs);
    };
}

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

const addCurry = curry(add, 1, 2);
addCurry() // 3
// 或者
const addCurry = curry(add, 1);
addCurry(2) // 3
// 或者
const addCurry = curry(add);
addCurry(1, 2) // 3

第一版代碼我們可以發(fā)現(xiàn)有一個(gè)確定就是沒有實(shí)現(xiàn)我們想要的鏈?zhǔn)筋愃朴?code>sum(1)(2)(3)這樣形式旁振,其實(shí)現(xiàn)思路就是將返回的函數(shù)也柯里化。

已聲明的函數(shù)涨岁,可以通過原型里length屬性獲取到函數(shù)行參的長度拐袜。

所以改造第二版:

const curry = function(fn) {
  return function inner() {
    // 淺拷貝入?yún)?    const args = Array.prototype.slice.call(arguments);
    // 如果下一個(gè)參數(shù)的長度大于了函數(shù)的行參個(gè)數(shù),則跳出遞歸
    if (arguments.length >= fn.length) {
      return fn.apply(undefined, args);
    } else {
      // 否則繼續(xù)處理后續(xù)參數(shù)梢薪,返回curring函數(shù)
      return function() {
        // 獲取合并上一次和下一次的入?yún)?        const allArgs = args.concat(Array.prototype.slice.call(arguments));
        return inner.apply(undefined, allArgs);
      };
    }
  };
}

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

const currySum = curry(sum);
柯里化

如果利用ES6蹬铺,那么可以有更簡潔的寫法:

const curry = fn =>
  judge = (...args) =>
    args.length >= fn.length 
      ? fn(...args) 
      : arg => judge(...args, arg);

2.4 防抖(debounce)和節(jié)流(throttle)

一般我們都是將這兩個(gè)概念放在一起來講,兩者都是防止用戶頻繁觸發(fā)函數(shù)調(diào)用秉撇,只是兩者的處理策略不同甜攀,筆者總結(jié)了一句幫助大家記憶區(qū)分的口訣:
“防抖多次觸發(fā),最后一次生效琐馆;節(jié)流多次觸發(fā)赴邻,周期性生效”。

對(duì)于防抖節(jié)流的示例分析這里便不展開了啡捶,相信大家也在學(xué)習(xí)或工作中都已經(jīng)運(yùn)用過,例如lodash中的debounce和throttle奸焙,或者單獨(dú)的防抖或節(jié)流的三方庫瞎暑,對(duì)于這倆的認(rèn)知都已經(jīng)比較清晰。

推薦閱讀:《debounce(防抖)和throttle(節(jié)流)

2.5 分時(shí)函數(shù)

分時(shí)函數(shù)是一個(gè)用于程序性能優(yōu)化上的一個(gè)運(yùn)用与帆,最近在做程序性能優(yōu)化的過程中接觸到了了赌,筆者覺得非常有必要一說。

一個(gè)常見的案例是大量DOM節(jié)點(diǎn)插入玄糟,那么就會(huì)導(dǎo)致頁面初始化load的時(shí)候非澄鹚卡頓(假死現(xiàn)象)

一次性插入:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分時(shí)函數(shù)</title>
</head>
<body>
    <div>分時(shí)函數(shù)性能優(yōu)化驗(yàn)證</div>

    <script>
        // 一次性添加到頁面
        const dataSource = new Array(10000).fill('DYBOY');
        // 創(chuàng)建DOM
        const createDiv = (text = 'DYBOY') => {
            const div = document.createElement('div');
            div.innerHTML = text;
            document.body.appendChild(div);
        }
        // 批量添加
        for (data of dataSource) {
            createDiv(data);
        }
    </script>
</body>
</html>
一次性插入的性能表現(xiàn)

分時(shí)函數(shù)的思想就是將一次性執(zhí)行大量重復(fù)操作時(shí),分批次時(shí)間周期的進(jìn)行阵翎,這樣就可以不阻塞頁面首屏的渲染,避免出現(xiàn)假死現(xiàn)象郭卫。

改造后的代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分時(shí)函數(shù)</title>
</head>
<body>
    <div>分時(shí)函數(shù)性能優(yōu)化驗(yàn)證</div>

    <script>
        // 一次性添加到頁面
        const dataSource = new Array(10000).fill('DYBOY');
        // 創(chuàng)建DOM
        const createDiv = (text = 'DYBOY') => {
            const div = document.createElement('div');
            div.innerHTML = text;
            document.body.appendChild(div);
        }
        // 批量添加
        // for (data of dataSource) {
        //     createDiv(data);
        // }

        /**
         * 分時(shí)函數(shù)
         * @param dataSource - 數(shù)據(jù)數(shù)組
         * @param fn - 分時(shí)執(zhí)行的函數(shù)
         * @param count - 每分段時(shí)間內(nèi)執(zhí)行函數(shù)的次數(shù)
         * @param duration - 分段時(shí)長砍聊,單位ms
         **/
        const timeChunk = (dataSource, fn, count = 1, duration = 200) => {
            let timer;
            const start = () => {
                const minCount = Math.min(count, dataSource.length);
                for(let i = 0; i < minCount; i++) fn(dataSource.shift());
            }
            return () => {
                timer = setInterval(() => {
                    if (dataSource.length === 0) return clearInterval(timer);
                    start();
                }, duration);
            }
        }

       const newRender = timeChunk(dataSource, createDiv, 100, 300);

       newRender();
    </script>
</body>
</html>
image.png

通過對(duì)比可以看到后者經(jīng)過分時(shí)函數(shù)的首屏里scripting的時(shí)間只有425ms,前者是2410ms贰军,通過分時(shí)函數(shù)使得首屏性能提節(jié)省了500%的時(shí)間玻蝌,非常可觀。

除了分時(shí)函數(shù)俯树,性能優(yōu)化過程中帘腹,如果某個(gè)函數(shù)計(jì)算任務(wù)的時(shí)間非常長,那么就會(huì)導(dǎo)致“長時(shí)間頁面白屏”的現(xiàn)象许饿,這里我們可著手該長時(shí)間計(jì)算任務(wù)阳欲,看看該任務(wù)里有啥耗時(shí)的操作,看看針對(duì)耗時(shí)操作能不能做緩存米辐,時(shí)間切片胸完,以及宏任務(wù)微任務(wù)插隊(duì),在后續(xù)的文章中將整理并分享給大家翘贮。

2.6 惰性加載函數(shù)

后續(xù)將梳理專項(xiàng)的關(guān)于性能優(yōu)化方法赊窥,這里僅僅提一下概念,惰性加載屬于程序性能優(yōu)化中的一種方法狸页,其目的是使得函數(shù)的執(zhí)行分支僅發(fā)生一次锨能。

類似于我們將某個(gè)耗時(shí)操作的函數(shù)結(jié)果保存到一個(gè)變量中,而不是在每次for循環(huán)中都去重新執(zhí)行函數(shù)拿到計(jì)算結(jié)果芍耘。

惰性加載函數(shù)的方式有兩種:

  1. 在函數(shù)調(diào)用時(shí)處理:函數(shù)內(nèi)部復(fù)寫函數(shù)址遇,直接返回值;
  2. 在函數(shù)聲明時(shí)處理:函數(shù)聲明時(shí)斋竞,確定返回值倔约。

三、總結(jié)

這篇文章是承接前一篇《這些JS設(shè)計(jì)模式中的基礎(chǔ)知識(shí)點(diǎn)你都會(huì)了嗎坝初?》內(nèi)容浸剩,從Javascript中的this指向、原型鳄袍、原型鏈绢要、JS繼承實(shí)現(xiàn)到閉包(Closure)和高階函數(shù)(HOF),這些都是學(xué)習(xí)設(shè)計(jì)模式的必要基礎(chǔ)拗小,因?yàn)樵贘avaScript中的設(shè)計(jì)模式很多地方都需要依賴于閉包和高階函數(shù)來實(shí)現(xiàn)重罪,所以能夠掌握并熟練運(yùn)用閉包和高階函數(shù),有助于大家能夠快速理解并在JS中實(shí)現(xiàn)程序設(shè)計(jì)哀九。

對(duì)于設(shè)計(jì)模式和前端進(jìn)階的同學(xué)不妨關(guān)注微信公眾號(hào):DYBOY剿配,添加筆者微信,交流學(xué)習(xí)阅束,內(nèi)推大廠惨篱!

Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末围俘,一起剝皮案震驚了整個(gè)濱河市砸讳,隨后出現(xiàn)的幾起案子琢融,更是在濱河造成了極大的恐慌,老刑警劉巖簿寂,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漾抬,死亡現(xiàn)場離奇詭異,居然都是意外死亡常遂,警方通過查閱死者的電腦和手機(jī)纳令,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來克胳,“玉大人平绩,你說我怎么就攤上這事∧恚” “怎么了捏雌?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笆搓。 經(jīng)常有香客問我性湿,道長,這世上最難降的妖魔是什么满败? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任肤频,我火速辦了婚禮,結(jié)果婚禮上算墨,老公的妹妹穿的比我還像新娘宵荒。我一直安慰自己,他們只是感情好净嘀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布骇扇。 她就那樣靜靜地躺著,像睡著了一般面粮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上继低,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天熬苍,我揣著相機(jī)與錄音,去河邊找鬼袁翁。 笑死柴底,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粱胜。 我是一名探鬼主播柄驻,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼焙压!你這毒婦竟也來了鸿脓?” 一聲冷哼從身側(cè)響起抑钟,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎野哭,沒想到半個(gè)月后在塔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拨黔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蛔溃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篱蝇。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贺待,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出零截,到底是詐尸還是另有隱情麸塞,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布瞻润,位于F島的核電站喘垂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍撞。R本人自食惡果不足惜正勒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望傻铣。 院中可真熱鬧章贞,春花似錦樱衷、人聲如沸节槐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽两踏。三九已至败京,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梦染,已是汗流浹背赡麦。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帕识,地道東北人泛粹。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像肮疗,于是被迫代替她去往敵國和親晶姊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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