JavaScript性能优化

#### JavaScript性能优化 **如何编写高性能的JavaScript**

- 性能优化是不可避免的

- 哪些内容可以看作是性能优化

- 无所不在的前端性能优化

#### 本阶段的核心是JavaScript语言的优化

- 内存管理

- 垃圾回收与常见的GC算法

- V8引擎的垃圾回收

- Performance工具(垃圾回收的监控)

- 代码优化实例

#### JavaScript内存管理(Memory Management)

- 内存: 由可读写的单元组成, 表示一片可操作空间

- 管理: 认为的去操作一片空间的申请、使用和释放

- 内存管理: 开发者主动申请空间、使用空间、释放空间

- 管理流程: 申请-使用-释放

```javascript

// 申请空间

let obj = {}

// 使用空间

obj.name = 'liuchao'

// 释放空间

obj = null

```

#### JavaScript中的垃圾回收

- JavaScript中内存管理是自动的

- 对象不再被引用时是垃圾

- 对象不能从根上访问到时是垃圾

#### 可达对象

- 可达对象: 可以访问到的对象(引用、作用域链)

- 可达的标准就是从根出发时候能够被找到

- JavaScript中的跟可以理解为是全局变量对象

```javascript

let obj = {name: 'liuchao'}

let ali = obj

obj = null//ali变量还是有值的

```

#### JavaScript中的引用与可达

```javascript

function objGroup (obj1, obj2){

    obj1.next = obj2

    obj2.prev = obj1

    return {

        o1: obj1,

        o2: obj2

    }

}

let obj = objGroup({name: 'obj1'}, {name: 'obj2'})

console.log(obj)

const user1 = {age: 1}

const user2 = {age: 2}

const user3 = {age: 3}

const nameList = [user1.name, user2.name, user3.name, ]

// function fn(){

//    num1 = 1

//    num2 = 2

// }

function fn(){

    const num1 = 1

    const num2 = 2

}

fn()

```

#### GC算法介绍

- GC就是垃圾回收机制的简写

- GC可以找到内存中的垃圾、并释放和回收空间 

    - 程序中不再需要使用的对象

    - 程序中不能在访问到的对象

- 算法就是工作时查找和回收所遵循的规则

- 常见GC算法

    - 引用计数

    - 标记清除

    - 标记整理

    - 分代回收

#### 引用计数算法实现原理

- 核心思想: 设置引用数, 判断当前引用数是否为0

- 引用计数器(被弃用的原因, 性能不好)

- 引用关系改变时修改引用数字

- 引用数字为0时立即回收

- 引用计数的优点

    - 发现垃圾时立即回收

    - 减少程序卡顿时间

    - 最大限度减少程序暂停(内存即将占满,立即清除)

- 引用计数的缺点

    - 无法回收循环引用的对象

    - 时间(资源)开销大

```javascript

// circular reference 对象循环引用

function fn(){

    const obj1 = {}

    const obj2 = {}

    obj1.name = obj2

    obj2.name = obj1

    return 'A circular reference.'

}

fn()

```

#### 标记清除算法的实现原理

- 核心思想: 分为标记和清除两个阶段

- 遍历所有对象找标记活动对象(对象、子对象递归标记)

- 遍历所有对象清除没有标记对象(也会清除未清除对象的标记)

- 回收相应的空间(回收的空间放到空闲链表上方便后续使用)

- 标记清除算法优点

    - 解决对象循环引用

- 标记清除算法缺点

    - 不会立即回收垃圾对象

    - 空间碎片化: 回收空间地址不连续(分散在各个角落)

#### 标记整理算的实现原理

- 标记整理可以看作是标记清除的增强

- 标记阶段的操作和标记清除一致

- 清除阶段会执行整理, 移动对象位置(为了地址上连续)

- 不会立即回收垃圾对象

#### 常见GC算法总结

- 引用计数的优点

    - 发现垃圾时立即回收

    - 减少程序卡顿时间

    - 最大限度减少程序暂停(内存即将占满,立即清除)

- 引用计数的缺点

    - 无法回收循环引用的对象

    - 时间(资源)开销大

- 标记清除算法优点

    - 解决对象循环引用

- 标记清除算法缺点

    - 不会立即回收垃圾对象

    - 空间碎片化: 回收空间地址不连续(分散在各个角落)

- 标记整理的优点

    - 减少碎片化空间

- 标记整理的缺点点

    - 不会立即回收垃圾对象

#### 认识V8

- V8是一款主流的JavaScript执行引擎

- V8采用即时编译

    - V8快: 1. 优秀内存管理机制 2. V8采用即时编译(原先源码转换成字节码, 然后字节码转换成机器码执行; 即时编译: 源码转换成机器码直接执行)

- V8内存设限(64X: 1.5G; 32X: 800M)

    - V8本身为了浏览器而制造, 现有大小对网页应用来说足够使用了

    - V8内部的垃圾回收机制决定了再用此设置是合理的

        - 垃圾内存达到1.5G的时候使用增量标记进行垃圾回收是50ms,如果使用非增量标记进行垃圾回收需要1s

#### V8垃圾回收策略

- 采用分代回收的思想

- 内存分为新生代、老生代

- 针对不同对象采用不同GC算法

    - 分代回收

    - 空间复制

    - 标记清除

    - 标记整理

    - 标记增量

#### V8如何回收新生代对象

- V8内存分配

    - V8内存空间一分为二

    - 小空间用于存储新生代对象(32M(64X)|16M(32X))

    - 新生代对象指存活时间比较短的对象(eq: 局部变量)

- 新生代对象回收实现

    - 回收过程采用复制算法 + 标记整理

    - 新生代内存去分为二个等大小空间

    - 使用空间为From, 空闲空间为To

    - 活动对象存储于From空间

    - 标记整理后将活动对象拷贝至To

    - From与To交换空间, 完成释放

- 新生代对象回收实现---说明

    - 拷贝过程中可能出现晋升

    - 晋升就是将新生代对象移动至老生代

        - 一轮GC还存活的新生代需要晋升

        - To空间的使用率超过25%(整体移动至老生代: From和To会交换)

#### V8如何回收老生代对象

- 老生代对象存放在右侧老生代区域

- 内存限制(64位操作系统1.4G, 32位操作系统700M)

- 老生代对象就是指存活时间较长的对象(闭包等)

- 老生代对象回收实现

    - 主要采用标记清除、标记整理、增量标记算法

    - 首先使用标记清除完成垃圾空间的回收

    - 采用标记整理进行空间优化(晋升的时候并且老生代存储空间不足以存放新生代移动过来的对象)

    - 采用增量标记进行效率优化

#### 新生代与老生代回收的细节对比

- 新生代区域垃圾回收使用空间换时间(复制算法)

- 老生代区域篮机回收不适合复制算法(空间大浪费奢侈、对象数据比较多消耗时间多)

- 标记增量如何优化垃圾回收

    - 垃圾回收会终止程序执行

    - 标记增量: 整个垃圾回收过程拆分成多个小步,替代整个垃圾回收, 实现程序执行和垃圾回收交替执行

#### V8垃圾回收总结

- V8是一款主流的JavaScript执行引擎

- V8内存设限(64X: 1.5G; 32X: 800M)

- 采用分代回收的思想

- 内存分为新生代、老生代

- 针对不同对象采用不同GC算法

#### Performance工具介绍

### 为什么使用Performance

- GC的目的是为了实现内存空间的良性循环

- 良性循环的基石是合理使用

- 时刻关注才能确定是否合理

- Performance提供多种监控方式

- **通过Performance时刻监控内存**

#### Performance使用步骤

- 打开浏览器输入牧鞭网址

- 进入开发人员工具面板, 选择性能

- 开启录制功能, 访问具体页面

- 执行用户行为, 一段时间后停止录制

- 分析界面中记录的内存信息

#### 内存问题的外在体现

- 频繁垃圾回收: 页面出现延迟加载或经常性暂停(网络正常下)

- 内存膨胀: 页面持续性出现糟糕的性能(网络正常下)

- 内存泄漏: 页面性能随时间延长越来越差(网络正常下)

#### 监控内存的几种方式

- 内存泄漏: 内存使用持续升高

- 内存膨胀: 在多数设备上都存在性能问题(主流设备上测试)

- 频繁垃圾回收: 通过内存变化图进行分析

- 方式

    - 浏览器任务管理器

    - Timeline时序图记录

    - 堆快照查找分离DOM

    - 判断是否存在频繁的垃圾回收

#### 浏览器任务管理器监控内存

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>浏览器任务管理器监控内存变化</title>

</head>

<body>

    <button id='btn'>Add</button>

    <script>

        const oBtn = document.getElementById('btn')

        oBtn.onclick = function (){

            let arrList = new Array(1000000)

        }

    </script>

</body>

</html>

```

#### Timeline记录内存

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Timeline记录内存</title>

</head>

<body>

    <button id='btn'>Add</button>

    <script>

        // 1. 创建大量DOM节点模拟内存消耗

        // 2. DOM节点不够,数组配合其他方法, 形成非常长的字符串,模拟内存消耗


        const arrList = []

        function test(){

            for(let i = 0; i < 100000;i++){

                document.body.appendChild(document.createElement('p'))

            }

            arrList.push(new Array(1000000).join('x'))

        }

        document.getElementById('btn').addEventListener('click', test)

    </script>

</body>

</html>

```

#### 堆快照查找分离DOM

- 界面元素存活在DOM树上

- 垃圾对象时的DOM节点(从DOM树上脱离, 并且程序中无引用)

- 分离状态的DOM节点(分离节点: 从DOM树上脱离, 但是程序中有引用; 页面上看不见, 但是占据内存, 导致内存泄漏)

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>堆快照查找分离DOM</title>

</head>

<body>

    <button id='btn'>Add</button>

    <script>

        // 1. 创建大量DOM节点模拟内存消耗

        var tmpELe

        function  fn() {

            var ul = document.createElement('ul')

            for(var i = 0;i < 10;i++){

                var li = document.createElement("li")

                ul.appendChild(li)

            }

            tmpELe = ul

            tmpELe = null

        }

        document.getElementById('btn').addEventListener('click', fn)

    </script>

</body>

</html>

```

#### 判断是否存在频繁的垃圾回收

- 原因

    - GC工作时应用程序时停止的

    - 频繁且过长的GC会导致应用假死

    - 用户使用中感知应用卡顿

- 方法

    - Timeline中频繁的上升和下降

    - 任务管理器中数据频繁的增大减小

#### Performance使用总结

- Performance使用步骤

- 内存问题的相关分析

- Performance时序图监控内存变化

- 浏览器任务管理器监控内存

- 堆快照查找分离DOM

#### 代码优化介绍

- 如何精准测试JavaScript性能

    - 本质上就是采集大量的执行样本进行数学统计和分析

    - 使用基于Benchmark.js的https://jsperf.com/ 完成

#### Jsperf使用流程

- 使用GitHub账号登陆

- 填写个人信息(非必须)

- 填写详细的测试用例信息(title、slug)

- 填写准备代码(DOM操作室经常使用)

- 填写必要的setup和teardown代码

- 填写测试代码片段

#### 慎用全局变量

- 全局变量定义在全局执行上下文, 是所有作用域链的顶端(时间长)

- 全局执行上下文一直存在于上下文执行栈, 知道程序退出(GC工作不力, 降低内存使用)

- 如果某个局部作用域出现了同名变量则护额遮蔽或者污染全局

```javacsript

    // 全局变量

    var i, str = ''

    for(i = 0; i < 100; i++ ){

        str += i

    }

    for(let i = 0; i < 100; i++ ){

        let str = ''

        str += i

    }

    // 局部变量有很大性能提升

    // Jsperf中查看结果

```

#### 缓存全局变量

- 将使用中无法避免的全局变量缓存到局部

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>缓存全局变量</title>

</head>

<body>

    <input type="button" value='btn' id='btn1'>

    <input type="button" value='btn' id='btn2'>

    <input type="button" value='btn' id='btn3'>

    <p>111</p>

    <input type="button" value='btn' id='btn4'>

    <input type="button" value='btn' id='btn5'>

    <p>222</p>

    <input type="button" value='btn' id='btn6'>

    <input type="button" value='btn' id='btn7'>

    <input type="button" value='btn' id='btn8'>

    <p>33333</p>

    <input type="button" value='btn' id='btn9'>

    <input type="button" value='btn' id='btn10'>

    <script>

        function getBtn() {

            let oBtn1 = document.getElementById('btn1')

            let oBtn3 = document.getElementById('btn3')

            let oBtn5 = document.getElementById('btn5')

            let oBtn7 = document.getElementById('btn7')

            let oBtn9 = document.getElementById('btn9')

        }

        function getBtn2() {

            let obj = document

            let oBtn1 = obj.getElementById('btn1')

            let oBtn3 = obj.getElementById('btn3')

            let oBtn5 = obj.getElementById('btn5')

            let oBtn7 = obj.getElementById('btn7')

            let oBtn9 = obj.getElementById('btn9')

        }

        // Jsperf中查看结果

    </script>

</body>

</html>

```

#### 通过原型新增方法

- 在原型对象上新增实例对象需要的方法

```javascript

var fn1= function(){

    this.foo = function(){

        console.log(11111)

    }

}

let f1 = new fn1()

var fn2 = function(){}

fn2.prototype.foo = function(){

    console.log(1111)

}

let f2 = new fn2()

// Jsperf中查看运算速度

```

#### 避开闭包陷阱

- 闭包特点

    - 外部具有指向内部的引用

    - 在‘外’部作用域访问‘内’部作用域的数据

- 闭包

    - 闭包是一种强大的语法

    - 闭包使用不当很容易出现内存泄漏

    - 不要为了闭包而闭包

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>闭包陷阱</title>

</head>

<body>

    <button id="btn">btn</button>

    <script>

        // function foo() {

        //    var el = document.getElementById('btn')

        //    el.onclick = function () {

        //        console.log(el.id)

        //    }

        // }

        // foo()

        // 闭包优化

        function foo() {

            var el = document.getElementById('btn')

            el.onclick = function () {

                console.log(el)

                console.log(el, el.id)

            }

            el = null

        }

        foo()

    </script>

</body>

</html>

```

#### 避免属性访问方法使用

- JavaScript中的面向对象

    - JS不需要属性的访问方法, 所有属性都是外部可见的

    - 书用属性访问方法只会增加一层重定义, 没有访问的控制力

```javascript

function Person (){

    this.name = 'Person'

    this.age = 19

    this.getAge = function(){

        return this.age

    }

}

const p1 = new Person()

const a = p2.getAge()

function Person1 (){

    this.name = 'Person'

    this.age = 19

}

const p2 = new Person1()

const b = p2.age

// Jsperf上查看运行速度

```

#### For循环优化 length提取

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>For循环优化</title>

</head>

<body>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <p class="btn"></p>

    <script>

        var aBtns = document.getElementsByClassName('btn')

        for(var i = 0; i < aBtns.length; i++){

            console.log(i)

        }

        for(var i = 0, len = aBtns.length; i < len; i++){

            console.log(i)

        }

        // Jsperf上查看运行速度

    </script>

</body>

</html>

```

#### 采用最优循环方式

```javascript

// 采用最优循环方式

const arr = new Array(1, 2, 3, 4, 5)

arr.forEach(function(item){

    console.log(item)

})

for(var i = arr.length; i; i--){

    console.log(arr[i])

}

for(var i in arr){

    console.log(arr[i])

}

// Jsperf中查看运行速度

```

#### 节点添加优化

- 节点添加操作必然会有回流和重绘

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>优化节点添加</title>

</head>

<body>

    <script>

        for(var i = 0; i < 100; i++){

            var oP = document.createElement('p')

            oP.innerHTML = i

            document.body.appendChild(oP)

        }

        const fragEle = document.createDocumentFragment()

        for(var i = 0; i < 100; i++){

            var oP = document.createElement('p')

            oP.innerHTML = i

            fragEle.appendChild(oP)

        }

        document.body.appendChild(fragEle)

        // Jsperf 查看运行速度

    </script>

</body>

</html>

```

#### 克隆优化节点操作

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>克隆优化节点操作</title>

</head>

<body>

    <p id="box1">old</p>

    <script>

        // 创建新节点时 : 先clone一个已有的节点, 再在clone节点上作修改

        for(var i = 0; i< 5; i++){

            var oP = document.createElement("p")

            oP.innerHTML = i

            document.body.appendChild(oP)

        }

        var oldP = document.getElementById('box1')

        for(var i = 0; i< 5; i++){

            var newP = oldP.cloneNode(false)

            newP.innerHTML = i

            document.body.appendChild(newP)

        }

        // Jsperf 查看运行速度

    </script>

</body>

</html>

```

#### 直接量替换Object操作

```javascript

var a = [1, 2, 3]

var a1 = new Array(3)

a1[0] = 1

a1[1] = 2

a1[2] = 3

// Jsperf中查看运算速度

```

#### **JavaScript性能提升2**  空间换时间 或者 时间换空间

#### JSBench的使用(JSBench.me)

#### 堆栈中代码执行流程

```javascript

let a = 10

function foo(b){

    let a = 2

    function baz(c){

        console.log(a+b+c)

    }

    return baz

}

let fn = foo(2)

fn(3)//=> 7 存在闭包 导致函数foo作用域没有被释放

// 减少判断层级 

```

#### 减少判断层级

```javascript

// function doSome(part, chapter){

//    const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']

//    if(part){

//        if(parts.includes(part)){

//            console.log('属于前端课程')

//            if(chapter > 5){

//                console.log('需要提供Vip身份')

//            }

//        }

//    }else{

//        console.log('请确认模块信息')

//    }

// }

// doSome('ES2016', 6)

function doSome(part, chapter){

    const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']

    if(!part){

        console.log('请确认模块信息')

        return

    }

    if(!parts.includes(part)) return

    console.log('属于前端课程')

    if(chapter > 5){

        console.log('需要提供Vip身份')

    }

}

doSome('ES2016', 6)

```

#### 减少作用域链查找层级

```javascript

// var name = 'foo'

// function foo(){

//    name = 'foo666'//全局的name变量

//    function baz(){

//        var age = 39

//        console.log(age)

//        console.log(name)

//    }

//    baz()

// }

// foo()

var name = 'foo'

function foo(){

    var name = 'foo666'//全局的name变量

    function baz(){

        var age = 39

        console.log(age)

        console.log(name)

    }

    baz()

}

foo()

```

#### 减少数据读取次数

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>减少数据读取次数</title>

</head>

<body>

    <div id="skip" class="skip"></div>

    <script>

        var oBox = document.getElementById('skip')

        // function hasEle(ele, cls){

        //    return ele.className == cls

        // }

        function hasEle(ele, cls){

            var clsname = ele.className

            return clsname == cls

        }

        console.log(hasEle(oBox, 'skip'))

    </script>

</body>

</html>

```

#### 字面量与构造式

```javascript

// let test = () => {

//    let obj = new Object()

//    obj.name = 'liuchao'

//    obj.age = 39

//    obj.slogan = '我为前端而活'

//    return obj

// }

// let test = () => {

//    let obj = {

//        name : 'liuchao',

//        age : 39,

//        slogan : '我为前端而活'

//    }

//    return obj

// }

// console.log(test())

// ----------------------

var str1 = '我为前端而活'

var str2 = new String('我为前端而活')

console.log(str1)

console.log(str2)

```

#### 减少循环体活动

```javascript

// var test = () => {

//    var i

//    var arr = ['liuchao', 39, '我为前端而活']

//    for( i =0;i<arr.length;i++){

//        console.log(arr[i])

//    }

// }

// var test = () => {

//    var i

//    var arr = ['liuchao', 39, '我为前端而活']

//    var len = arr.length

//    for( i =0;i<len;i++){

//        console.log(arr[i])

//    }

// }

var test = () => {

    var arr = ['liuchao', 39, '我为前端而活']

    var len = arr.length

    while(len--){

        console.log(arr[len])

    }

}

test()

```

#### 减少声明及语句数--词法分析消耗时间

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>减少声明及语句数-词法分析消耗时间</title>

</head>

<body>

    <div id="box" style="width: 100px;height: 200px;"></div>

    <script>

        var oBox = document.getElementById('box')

        // var test = function (ele){

        //    let w = ele.offsetWidth

        //    let h = ele.offsetHeight

        //    return w * h

        // }

        // var test = function (ele){

        //    return ele.offsetWidth * ele.offsetHeight

        // }

        // console.log(test(oBox))

        // var test = () => {

        //    var name = 'liucaho'

        //    var age = 39

        //    var slogan = '我为前端而活'

        //    return name + age + slogan

        // }

        var test = () => {

            var name = 'liucaho',

                age = 39,

                slogan = '我为前端而活'

            return name + age + slogan

        }

        console.log(test())

    </script>

</body>

</html>

```

#### 惰性函数与性能

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>惰性函数与性能</title>

</head>

<body>

    <button id="btn">点击</button>

    <script>

        var oBtn = document.getElementById('btn')

        function foo(){

            console.log(this)

        }

        // function addEvent (obj, type, fn){

        //    if(obj.addEventListerer){

        //        obj.addEventListerer(type, fn, false)

        //    }else if(obj.attachEvent){

        //        obj.attachEvent('on' + type, fn)

        //    }else{

        //        obj['on' + type ] = fn

        //    }

        // }

        function addEvent (obj, type, fn){

            if(obj.addEventListerer){

                addEvent = obj.addEventListerer(type, fn, false)

            }else if(obj.attachEvent){

                addEvent = obj.attachEvent('on' + type, fn)

            }else{

                addEvent = obj['on' + type ] = fn

            }

            return addEvent

        }

        addEvent(oBtn, 'click', foo)

    </script>

</body>

</html>

```

#### 采用事件委托

```html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>使用事件委托</title>

</head>

<body>

    <ul id="ul">

        <li>liuchao</li>

        <li>28</li>

        <li>male</li>

        <li>我为前端而活</li>

    </ul>

    <script>

        var list = document.querySelectorAll('li')

        // function showTxt(ev){

        //    console.log(ev.target.innerHTML)

        // }

        // for(let item of list){

        //    item.onclick = showTxt

        // }

        var oUl = document.getElementById('ul')

        oUl.addEventListener('click', showTxt, false)

        function showTxt(ev){

            var obj = ev.target

            if(obj.nodeName.toLowerCase() === 'li'){

                console.log(ev.target.innerHTML)

            }

        }

    </script>

</body>

</html>

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349