JavaScript 函數(shù)式編程初窺

編程范式

編程范式是:解決編程中的問(wèn)題的過(guò)程中使用到的一種模式,體現(xiàn)在思考問(wèn)題的方式和代碼風(fēng)格上。這點(diǎn)很像語(yǔ)言吗垮,語(yǔ)言本身會(huì)體現(xiàn)出不同國(guó)家的人的思考方式和行為模式。

常見的編程范式有下面幾種:

  • 命令式編程
  • 面向?qū)ο缶幊?/li>
  • 函數(shù)式編程

除了這三個(gè)之外凹髓,我們還會(huì)接觸到其他的編程范式烁登,如:聲明式。

編程范式之間不是互斥關(guān)系蔚舀,而是可以結(jié)合在一起使用的饵沧。我們往往需要結(jié)合各種編程范式來(lái)完成一個(gè)程序功能。

在學(xué)習(xí)寫代碼的過(guò)程中赌躺,我們一般先接觸命令式編程狼牺,然后學(xué)習(xí)面向?qū)ο缶幊蹋嫦驅(qū)ο缶幊炭梢宰屛覀兒芊奖愕靥幚砀鼜?fù)雜的問(wèn)題礼患。這篇文章里是钥,我們會(huì)介紹函數(shù)式編程。

不同的編程范式有不同的代碼表現(xiàn)

image.png

比如從來(lái)沒(méi)有坐過(guò)電梯的人缅叠,第一次坐電梯悄泥,電梯在 10 樓,人在 1 樓肤粱,他會(huì)按下弹囚,讓電梯下來(lái)。按錯(cuò)按鈕是因?yàn)樗昧似硎拐Z(yǔ)领曼,而不是把自己的想法提交出去鸥鹉。

相似地,你寫的代碼就像電梯的按鈕界面悯森,是讓自己或者他人閱讀的宋舷。只有達(dá)成了相同的共識(shí)才能更好地理解绪撵。通過(guò)這次文章可以讓大家更好地理解函數(shù)式編程瓢姻。

下面是幾種編程范式的代碼片段:

const app = 'github';
const greeting = 'Hi, this is ';
console.log(greeting + app);

這是命令式編程,通過(guò)調(diào)用 constconsole.log 進(jìn)行賦值和輸出音诈。

const Program = function() {
    this.app = 'github';
    this.greeting = function() {
        console.log('Hi, this is ' + this.app);
    };
};
const program = new Program();
program.greeting();

這是面向?qū)ο缶幊袒眉睿覀儼颜麄€(gè)程序抽象成現(xiàn)實(shí)生活中的一個(gè)對(duì)象绎狭,這個(gè)對(duì)象會(huì)包含屬性和方法。通過(guò)類的概念褥傍,我們有了生成對(duì)象的一個(gè)工廠儡嘶。使用 new 關(guān)鍵字創(chuàng)建一個(gè)對(duì)象,最后調(diào)用對(duì)象的方法恍风,也能完成剛才我們用命令式編程完成的程序功能蹦狂。

const greet = function(app) {
    return 'Hi, this is ' + app;
};
console.log(greet('github'));

這是簡(jiǎn)單的函數(shù)式編程,通過(guò)函數(shù)的調(diào)用完成程序的功能朋贬。但是一般情況下的函數(shù)式編程會(huì)更復(fù)雜一些凯楔,會(huì)包含函數(shù)的組合。

不同的編程范式適用的場(chǎng)景不同

  • 命令式編程:流程順序
  • 面向?qū)ο缶幊蹋何矬w
  • 函數(shù)式語(yǔ)言:數(shù)據(jù)處理

我們往往會(huì)在不同場(chǎng)景下使用不同的編程范式锦募,通過(guò)編程范式的結(jié)合來(lái)實(shí)現(xiàn)一個(gè)程序摆屯。

我們通過(guò)命令式編程去讓程序按步驟地執(zhí)行操作。

面向?qū)ο缶幊虅t是把程序抽象成和現(xiàn)實(shí)生活中的物體一樣的對(duì)象糠亩,對(duì)象上有屬性和方法虐骑,通過(guò)對(duì)象之間的修改屬性和調(diào)用方法來(lái)完成程序設(shè)計(jì)。

而函數(shù)式編程則適用于數(shù)據(jù)運(yùn)算和處理赎线。

再仔細(xì)看下之前的代碼廷没,我們就會(huì)發(fā)現(xiàn)這些編程范式往往是要結(jié)合起來(lái)使用的。

const app = 'github';
const greeting = 'Hi, this is ';
console.log(greeting + app);

這個(gè)例子里面垂寥,除了命令式之外腕柜,我們還可以把前兩句語(yǔ)句賦值解讀成聲明式編程。

const Program = function(app) {
    this.app = app;
    this.greeting = function() {
        console.log('Hi, this is ' + this.app);
    };
};
const program = new Program('github');
program.greeting();

這里例子里面矫废,我們看到在類的 greeting 方法里面也用到了命令式的 console.log盏缤。在最后的執(zhí)行過(guò)程中的 program.greeing() 也是命令式的。

const greet = function(app) {
    return 'Hi, this is ' + app;
};
console.log(greet('github'));

函數(shù)式編程

使用函數(shù)式編程可以大大提高代碼的可讀性蓖扑。

函數(shù)式編程的學(xué)習(xí)曲線

你編寫的每一行代碼之后都要有人來(lái)維護(hù)唉铜,這個(gè)人可能是你的團(tuán)隊(duì)成員,也可能是未來(lái)的你律杠。如果代碼寫的太過(guò)復(fù)雜潭流,那么無(wú)論誰(shuí)來(lái)維護(hù)都會(huì)對(duì)你炫技式的故作聰明的做法倍感壓力。

對(duì)于復(fù)雜計(jì)算的場(chǎng)景下柜去,使用函數(shù)式編程相比于命令式編程有更好的可讀性灰嫉。

image.png

從命令式的編程到函數(shù)式編程轉(zhuǎn)換的道路上,可讀性會(huì)變低嗓奢,但是一旦度過(guò)了一個(gè)坎讼撒,也就是在你大量使用函數(shù)式編程時(shí),可讀性便會(huì)大大提升「校可是我們往往會(huì)被這個(gè)坎阻撓钳幅,在發(fā)現(xiàn)可讀性下降后放棄學(xué)習(xí)函數(shù)式編程。

因此除了學(xué)習(xí)函數(shù)式編程本身的知識(shí)之外炎滞,我們還需要明白學(xué)習(xí)可能經(jīng)歷的過(guò)程和最終的結(jié)果敢艰。

函數(shù)式編程定義

函數(shù)是第一公民。

JavaScript 是一門在設(shè)計(jì)之處就完全支持函數(shù)式編程的語(yǔ)言册赛,在 JavaScript 里面钠导,函數(shù)可以用 function 聲明,作為全局變量森瘪,也就是這里說(shuō)的“第一公民”辈双。我們不再使用 varconst 或者 let 等關(guān)鍵字聲明函數(shù)柜砾。我們也會(huì)大大減少變量的聲明湃望,通過(guò)函數(shù)的形參來(lái)替代變量的聲明。

函數(shù)式編程大量通過(guò)函數(shù)的組合進(jìn)行邏輯處理痰驱,因此我們?cè)诤竺鏁?huì)看到很多輔助函數(shù)证芭。通過(guò)這些輔助函數(shù),我們可以更方便得修改和組合函數(shù)担映。

什么是函數(shù)废士?

一個(gè)函數(shù)就是包含輸入和輸出的方程。數(shù)據(jù)流方向是從輸入到輸出蝇完。

在數(shù)學(xué)里面我們學(xué)到的函數(shù)是這樣的:

y = f(x)

在 JavaScript 里面官硝,函數(shù)是這樣表示的:

function(x) { return y; }

代碼中的函數(shù)和數(shù)學(xué)意義上的函數(shù)概念是一樣的。

函數(shù)和程序的區(qū)別

  • 程序是任意功能的合集短蜕,可以沒(méi)有輸入值氢架,可以沒(méi)有輸出值。
  • 函數(shù)必須有輸入值和輸出值朋魔。

函數(shù)適合的場(chǎng)景

函數(shù)適合:數(shù)學(xué)運(yùn)算岖研。不適合:與真實(shí)世界互動(dòng)。

實(shí)際的編程需要修改硬盤等警检。如果不改變東西孙援,等于什么都沒(méi)做。也就沒(méi)辦法完成任務(wù)了扇雕。

JavaScript 和函數(shù)式編程

JavaScript 支持函數(shù)式編程拓售。使用 JavaScript 進(jìn)行函數(shù)式編程時(shí),我們要使用 JavaScript 的子集镶奉。不使用 for 循環(huán), Math.random, Date, 不修改數(shù)據(jù)铃拇,來(lái)避免副作用,做到函數(shù)式編程浪谴。

下面,面向 JavaScript 開發(fā)者莹菱,介紹在 JavaScript 函數(shù)式編程中用到的一些概念移国。

高階函數(shù)

高階函數(shù)是由一個(gè)或多個(gè)函數(shù)作為輸入的函數(shù)吱瘩,或者是輸出一個(gè)函數(shù)的函數(shù)。

[1, 2, 3].map(function(item, index, array) {
    return item * 2;
});
[1, 2, 3].reduce(function(accumulator, currentValue, currentIndex, array) {
    return accumulator + currentValue;
}, 0);

mapreduce 是高階函數(shù)迹缀,它接收一個(gè)函數(shù)作為參數(shù)使碾。

純函數(shù)

純函數(shù)有兩個(gè)特點(diǎn):

  • 純函數(shù)是冪等的
  • 純函數(shù)沒(méi)有副作用

純函數(shù)是可靠的,可預(yù)測(cè)結(jié)果祝懂。帶來(lái)了可讀性和可維護(hù)性票摇。

冪等

冪等是指函數(shù)任意多次執(zhí)行所產(chǎn)生的影響和一次執(zhí)行的影響相同。函數(shù)的輸入和輸出都需要冪等砚蓬。

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

上面的函數(shù)是冪等的矢门。

function add(a) {
    return a + Math.random();
}

上面使用了隨機(jī)數(shù),每次執(zhí)行得到的結(jié)果不同灰蛙,所以這個(gè)函數(shù)不冪等祟剔。

var a = 1;
function add(b) {
    return a + b;
}

上面使用到函數(shù)外部的數(shù)據(jù),當(dāng)外部數(shù)據(jù)變化時(shí)摩梧,函數(shù)執(zhí)行的結(jié)果不再相同物延,所以這個(gè)函數(shù)不冪等。

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

上面的函數(shù)修改了函數(shù)外部的數(shù)據(jù)仅父,所以也不冪等叛薯。

副作用

副作用是當(dāng)調(diào)用函數(shù)時(shí),除了返回函數(shù)值之外笙纤,還對(duì)主調(diào)用函數(shù)產(chǎn)生附加的影響耗溜。

最常見的副作用是 I/O(輸入/輸出)。對(duì)于前端來(lái)說(shuō)省容,用戶事件(鼠標(biāo)强霎、鍵盤)是 JS 編程者在瀏覽器中使用的典型的輸入,而輸出的則是 DOM蓉冈。如果你使用 Node.js 比較多城舞,你更有可能接觸到和輸出到文件系統(tǒng)、網(wǎng)絡(luò)系統(tǒng)和/或者 stdin / stdout(標(biāo)準(zhǔn)輸入流/標(biāo)準(zhǔn)輸出流)的輸入和輸出寞酿。

純函數(shù)

一個(gè)純函數(shù)需要滿足下面兩個(gè)條件:

  • 純函數(shù)是冪等的
  • 純函數(shù)沒(méi)有副作用

不可變數(shù)據(jù)

不可變數(shù)據(jù)是指保持一個(gè)對(duì)象狀態(tài)不變家夺。

值的不可變性并不是不改變值。它是指在程序狀態(tài)改變時(shí)伐弹,不直接修改當(dāng)前數(shù)據(jù)拉馋,而是創(chuàng)建并追蹤一個(gè)新數(shù)據(jù)。這使得我們?cè)谧x代碼時(shí)更有信心,因?yàn)槲覀兿拗屏藸顟B(tài)改變的場(chǎng)景煌茴,狀態(tài)不會(huì)在意料之外或不易觀察的地方發(fā)生改變随闺。

在函數(shù)式和非函數(shù)式編程中,不可變數(shù)據(jù)對(duì)我們都有幫助蔓腐。

使用不可變數(shù)據(jù)的準(zhǔn)則

  • 使用 const矩乐,不使用 let
  • 不使用 splicepop回论、push散罕、shiftunshift傀蓉、reverse 以及 fill 修改數(shù)組
  • 不修改對(duì)象屬性或方法

使用不可變數(shù)據(jù)的弊端

不可變數(shù)據(jù)有更多內(nèi)存開銷欧漱。

image.png

修改數(shù)據(jù)的情況下,直接替換了變量的值葬燎,內(nèi)存開銷不變误甚。

image.png

使用不可變數(shù)據(jù)后,我們復(fù)制了一個(gè)對(duì)象谱净,內(nèi)存開銷翻倍窑邦。

image.png

使用 immutableJS 等輔助庫(kù)后,可以更好地利用之前的數(shù)據(jù)岳遥,優(yōu)化了內(nèi)存開銷奕翔。

閉包 vs 對(duì)象

閉包和對(duì)象是一樣?xùn)|西的兩種表達(dá)方式。一個(gè)沒(méi)有閉包的編程語(yǔ)言可以用對(duì)象來(lái)模擬閉包浩蓉;一個(gè)沒(méi)有對(duì)象的編程語(yǔ)言可以用閉包來(lái)模擬對(duì)象派继。兩者都可以用來(lái)維護(hù)數(shù)據(jù)。

var obj = {
    one: 1,
    two: 2
};

function run() {
    return this.one + this.two;
}

var three = run.bind(obj);

three();        // => 3
function getRun() {
    var one = 1;
    var two = 2;

    return function run(){
        return one + two;
    };
}

var three = getRun();

three();            // => 3

上面兩種方式都可以用來(lái)完成程序功能捻艳,對(duì)象和函數(shù)之間可以轉(zhuǎn)換驾窟。

常見的輔助函數(shù)

  • unary
  • reverseArgs
  • curry
  • uncurry
  • compose
  • pipe
  • asyncPipe

unaryreverseArgs认轨,curryuncurry 是用來(lái)進(jìn)行參數(shù)操作的绅络。composepipeasyncPipe 是用來(lái)進(jìn)行函數(shù)組合的嘁字。

unary

unary 是用來(lái)限制某個(gè)函數(shù)只接收一個(gè)參數(shù)的恩急。常見的使用場(chǎng)景是處理 parseInt 函數(shù):

['1', '2', '3'].map(parseInt);
// => [1, NaN, NaN]
['1', '2', '3'].map(unary(parseInt));
// => [1, 2, 3]

unary 的實(shí)現(xiàn)方式可以是:

const unary = (fn) => {
    return (arg) => {
        return fn(arg);
    };
};

reverseArgs

reverseArgs 是用來(lái)講函數(shù)參數(shù)反轉(zhuǎn)的,實(shí)現(xiàn)方式如下:

const reverseArgs = (fn) => {
    return (...args) => {
        return fn(...args.reverse());
    };
};

curry

curry 是用來(lái)把函數(shù)執(zhí)行滯后的纪蜒,讓我們可以逐步把參數(shù)傳入這個(gè)函數(shù)衷恭,當(dāng)參數(shù)完整之后,目標(biāo)函數(shù)才會(huì)執(zhí)行纯续。常見的用法如下:

function add(a, b) {
    return a + b;
}
function add10(a) {
    return add(10, a);
}
add10(1); // => 11

通過(guò) curry 函數(shù)随珠,可以把上面的代碼優(yōu)化一下:

function add(a, b) {
    return a + b;
}
const curriedAdd = curry(add);
const add10 = curriedAdd(10);
add10(1); // => 11

curry 的實(shí)現(xiàn)思路如下:

args灭袁,保存起來(lái),每個(gè) curried 函數(shù)接受一個(gè)參數(shù)窗看,將參數(shù)拼在之前的參數(shù)后面茸歧。

const curry = (fn) => {
    const curried = (curArg) => {
        const args = [...prevArgs, curArg];
        return curried;
    };
    return curried;
};

修改成用閉包保存參數(shù)。

const curry = (fn) => {
    return (curArg) => {
        const args = [...prevArgs, curArg];
        return nextCurried(...args);
    };
};

遞歸調(diào)用 nextCurried显沈,第一次柯里化的函數(shù)不傳入?yún)?shù)软瞎。

const curry = (fn) => {
    const nextCurried = (...prevArgs) => {
        return (curArg) => {
            const args = [...prevArgs, curArg];
            return nextCurried(...args);
        };
    };
    return nextCurried();
};

最后補(bǔ)全 arity 參數(shù),來(lái)定義目標(biāo)函數(shù)的參數(shù)數(shù)量构罗。這樣铜涉,我們可以定義柯里化后的參數(shù)數(shù)量智玻,如果傳入的參數(shù)數(shù)量到了函數(shù)需要的數(shù)量遂唧,則直接執(zhí)行函數(shù),并傳入所有的參數(shù)吊奢。

const curry = (fn, arity = fn.length) => {
    const nextCurried = (...prevArgs) => {
        return (curArgs) => {
            const args = [...prevArgs, curArgs];
            if (args.length >= arity) {
                return fn(...args);
            }
            return nextCurried(...args);
        };
    };
    return nextCurried();
};

或者我們可以實(shí)現(xiàn)一個(gè)支持傳入多個(gè)參數(shù)的柯里化函數(shù):

const curry = (fn, arity = fn.length) => {
    const nextCurried = (...prevArgs) => {
        return (...curArgs) => {
            const args = [...prevArgs, ...curArgs];
            if (args.length >= arity) {
                return fn(...args);
            }
            return nextCurried(...args);
        };
    };
    return nextCurried();
};

compose

compose 用來(lái)串聯(lián)執(zhí)行函數(shù)盖彭,執(zhí)行順序是從后向前的。與之對(duì)應(yīng)的是 pipe 函數(shù)页滚,同樣是串聯(lián)執(zhí)行函數(shù)召边,但是執(zhí)行順序是從前向后的。

compose 的用法:

function add10(value) {
    return value + 10;
}
function multiple10(value) {
    return value * 10;
}
const add10AndMultiple10 = compose(multiple10, add10);
add10AndMultiple10(1); // => 110

compose 的實(shí)現(xiàn):

const compose = (...fns) => {
    return fns.reduce((a, b) => {
        return (...args) => {
            return a(b(...args));
        };
    });
};

或者通過(guò) reduceRight 簡(jiǎn)單地從右邊向左執(zhí)行裹驰,這是更好理解的一種實(shí)現(xiàn)隧熙,但是有參數(shù)個(gè)數(shù)的限制。

const compose = (...fns) => {
    return (input) => {
        return fns.reduceRight((value, fn) => {
            return fn(value);
        }, input);
    };
};

pipe

pipe 也是用來(lái)組合函數(shù)的幻林,串聯(lián)執(zhí)行的順序是從前向后贞盯,與 compose 相反。pipe 的實(shí)現(xiàn)可以是:

const pipe = (...fns) => {
    return fns.reduceRight((a, b) => {
        return (...args) => {
            return a(b(...args));
        }
    });
};

pipe 的用法如下:

const addA = (value) => {
    return value + 'A';
};
const addB = (value) => {
    return value + 'B';
};
pipe(addA, addB)('1') // => 1AB

asyncPipe

對(duì)于異步函數(shù)來(lái)說(shuō)沪饺,如果我們要串聯(lián)執(zhí)行躏敢,可以使用 asyncPipe。實(shí)現(xiàn)可以是:

const asyncPipe =  (...fns) => {
    return fns.reduceRight((next, fn) => {
        return (...args) => {
            fn(...args, next);
        };
    }, () => {});
};

用法是:

const addA = (value, next) => {
    next(value + 'A', 'a');
};
const addB = (value, anotherValue, next) => {
    console.log(anotherValue);                      // => a
    next(value + 'B');
};
const consoleLog = (value, next) => {
    console.log(value);
};
asyncPipe(addA, addB, consoleLog)('1');              // => 1AB

函數(shù)式編程在數(shù)據(jù)結(jié)構(gòu)上的運(yùn)用

實(shí)現(xiàn)鏈表

主要思路是用函數(shù)閉包代替對(duì)象保存數(shù)據(jù)整葡。

const createNode = (value, next) => {
    return (x) => {
        if (x) {
            return value;
        }
        return next;
    };
};
const getValue = (node) => {
    return node(true);
};
const getNext = (node) => {
    return node(false);
};
const append = (next, value) => {
    if (next === null) {
        return createNode(value, null);
    }
    return createNode(getValue(next), append(getNext(next), value));
};
const reverse = (linkedList) => {
    if (linkedList === null) {
        return null;
    }
    return append(reverse(getNext(linkedList)), getValue(linkedList));
};
const linkedList1 = createNode(1, createNode(2, createNode(3, null)));
const linkedList2 = reverse(linkedList1);
getValue(linkedList1);                      // => 1
getValue(getNext(linkedList1));             // => 2
getValue(getNext(getNext(linkedList1)));    // => 3
getValue(linkedList2);                      // => 3
getValue(getNext(linkedList2));             // => 2
getValue(getNext(getNext(linkedList2)));    // => 1

同樣可以用函數(shù)式編程實(shí)現(xiàn)二叉樹件余。

總結(jié)

希望大家能夠通過(guò)學(xué)習(xí)函數(shù)式編程范式,加深對(duì)軟件研發(fā)的理解遭居,開拓視野啼器,找到更多組織代碼方式。

函數(shù)式編程能夠更好地組織業(yè)務(wù)代碼中的數(shù)據(jù)處理俱萍,更多地復(fù)用了函數(shù)端壳,減少了中間變量。

但是函數(shù)式編程也有缺點(diǎn)鼠次,它增加了學(xué)習(xí)成本更哄,需要大家理解高階函數(shù)芋齿。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市成翩,隨后出現(xiàn)的幾起案子觅捆,更是在濱河造成了極大的恐慌,老刑警劉巖麻敌,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栅炒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡术羔,警方通過(guò)查閱死者的電腦和手機(jī)赢赊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)级历,“玉大人释移,你說(shuō)我怎么就攤上這事×戎常” “怎么了玩讳?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嚼贡。 經(jīng)常有香客問(wèn)我熏纯,道長(zhǎng),這世上最難降的妖魔是什么粤策? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任樟澜,我火速辦了婚禮,結(jié)果婚禮上叮盘,老公的妹妹穿的比我還像新娘秩贰。我一直安慰自己,他們只是感情好熊户,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布萍膛。 她就那樣靜靜地躺著,像睡著了一般嚷堡。 火紅的嫁衣襯著肌膚如雪蝗罗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天蝌戒,我揣著相機(jī)與錄音串塑,去河邊找鬼。 笑死北苟,一個(gè)胖子當(dāng)著我的面吹牛桩匪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播友鼻,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼傻昙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼闺骚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起妆档,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤僻爽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后贾惦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胸梆,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年须板,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碰镜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡习瑰,死狀恐怖绪颖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杰刽,我是刑警寧澤菠发,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布王滤,位于F島的核電站贺嫂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雁乡。R本人自食惡果不足惜第喳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踱稍。 院中可真熱鬧曲饱,春花似錦、人聲如沸珠月。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啤挎。三九已至驻谆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庆聘,已是汗流浹背胜臊。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伙判,地道東北人象对。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宴抚,于是被迫代替她去往敵國(guó)和親勒魔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甫煞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 原文鏈接:https://github.com/EasyKotlin 值就是函數(shù),函數(shù)就是值冠绢。所有函數(shù)都消費(fèi)函數(shù)危虱,...
    JackChen1024閱讀 5,988評(píng)論 1 17
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,151評(píng)論 0 13
  • 函數(shù)和對(duì)象 1唐全、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語(yǔ)言來(lái)說(shuō)都是核心的概念埃跷。通過(guò)函數(shù)可以封裝任意多條語(yǔ)句,而且...
    道無(wú)虛閱讀 4,578評(píng)論 0 5
  • 上期防組三中 獨(dú)膽9 雙膽89 三碼589 四碼5689防組三及豹子 五碼56789防組三及豹子 小復(fù)式78/56...
    南宮尹馨閱讀 394評(píng)論 2 3
  • 朔風(fēng)初起送清寒邮利,曾經(jīng)繁華又寂然弥雹。萬(wàn)木枯落盡霜染,向晚殘照戀暮山延届。 樽酒淡剪勿,意闌珊,如鉤弦月冷穹天方庭。夜深幽夢(mèng)應(yīng)...
    柏蓮華閱讀 961評(píng)論 0 3