你根本不懂Javascript(4): this關(guān)鍵字和對(duì)象原型

本文最初發(fā)布于http://szhshp.org
轉(zhuǎn)載請(qǐng)注明

This關(guān)鍵字

function identify() {
    return this.name.toUpperCase();
}

function speak() {
    var greeting = "Hello, I'm " + identify.call( this );
    console.log( greeting );
}

var me = {
    name: "Kyle"
};

var you = {
    name: "Reader"
};

identify.call( me ); // KYLE
identify.call( you ); // READER

speak.call( me ); // Hello, I'm KYLE
speak.call( you ); // Hello, I'm READER

同時(shí)如果不使用this我們可以傳入一個(gè)上下文到調(diào)用的函數(shù)中,例如這樣:

function identify(context) {
    return context.name.toUpperCase();
}

function speak(context) {
    var greeting = "Hello, I'm " + identify( context );
    console.log( greeting );
}

identify( you ); // READER
speak( me ); // Hello, I'm KYLE

幾個(gè)對(duì)this關(guān)鍵字的誤解

認(rèn)為this是指向函數(shù)自身

function foo(num) {
console.log( "foo: " + num );

// keep track of how many times `foo` is called
this.count++;
}

foo.count = 0;

var i;

for (i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// how many times was `foo` called?
console.log( foo.count ); // 0 -- WTF?

但是實(shí)際上我們操作的不是這個(gè)foo里面的count而是一個(gè)全局變量count

解決方案

當(dāng)然解決這個(gè)問(wèn)題很簡(jiǎn)單,不要在函數(shù)中操作this就是一個(gè)Solution:

function foo(num) {
    console.log( "foo: " + num );

    // keep track of how many times `foo` is called
    foo.count++;
}

foo.count = 0;

或者操作一個(gè)全局的count.

或者用另一種辦法強(qiáng)行使用this

function foo(num) {
    console.log( "foo: " + num );

    // keep track of how many times `foo` is called
    // Note: `this` IS actually `foo` now, based on
    // how `foo` is called (see below)
    this.count++;
}

foo.count = 0;

var i;

for (i=0; i<10; i++) {
    if (i > 5) {
        // using `call(..)`, we ensure the `this`
        // points at the function object (`foo`) itself
        foo.call( foo, i );
    }
}

認(rèn)為this指向函數(shù)的scope

這太愚蠢了...

function foo() {
    var a = 2;
    this.bar(); //還不如不加this關(guān)鍵字直接調(diào)用_(:з」∠)_
}

function bar() {
    console.log( this.a );
}

foo(); //undefined

方法調(diào)用及調(diào)用棧

想要理解this首先就要了解一個(gè)方法在哪里調(diào)用的

function baz() {
    // call-stack is: `baz`
    // so, our call-site is in the global scope

    console.log( "baz" );
    bar(); // <-- call-site for `bar`
}

function bar() {
    // call-stack is: `baz` -> `bar`
    // so, our call-site is in `baz`

    console.log( "bar" );
    foo(); // <-- call-site for `foo`
}

function foo() {
    // call-stack is: `baz` -> `bar` -> `foo`
    // so, our call-site is in `bar`

    console.log( "foo" );
}

baz(); // <-- call-site for `baz`

多數(shù)瀏覽器的Debugger工具可以方便地看到調(diào)用棧

調(diào)用規(guī)則

  1. 默認(rèn)綁定

    var a = 10;
    b = 10;
    this.a === a; // true
    this.b === b; // true
    //--------------------------
    function foo() {
    console.log( this.a );
    }
    var a = 2;
    foo(); // 2
    
    • 直接定義的變量都屬于global object
    • 注意這種綁定在strict mode不生效并且會(huì)報(bào)Undefined
  2. 隱式綁定

    function foo() {
        console.log( this.a ); //`this.a` is synonymous with `obj.a`.
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    obj.foo(); // 2
    

    注意這里的調(diào)用處僅僅會(huì)剝離一層,因此最后一個(gè)調(diào)用者將會(huì)是this所代表的內(nèi)容

    function foo() {
        console.log( this.a );
    }
    
    var obj2 = {
        a: 42,
        foo: foo
    };
    
    var obj1 = {
        a: 2,
        obj2: obj2
    };
    
    obj1.obj2.foo(); // 42
    
  3. 隱式丟失

    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var bar = obj.foo; // function reference/alias!
    
    var a = "oops, global"; // `a` also property on global object
    
    bar(); // "oops, global"
    //--------------------------
    
    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var bar = obj.foo; // function reference/alias!
    
    var a = "oops, global"; // `a` also property on global object
    
    bar(); // "oops, global"
    setTimeout( obj.foo, 100 ); // "oops, global"
    
    

    特別對(duì)于上面setTimeout函數(shù)

    function setTimeout(fn,delay) {
    // wait (somehow) for `delay` milliseconds
    fn(); // <-- call-site!
    }
    
  4. 顯式綁定

    當(dāng)調(diào)用call()或者applt()的時(shí)候我們可以強(qiáng)行傳一個(gè)obj作為this

    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2
    };
    
    foo.call( obj ); // 2
    

    同時(shí)注意如果給this傳進(jìn)原始類型的數(shù)據(jù)時(shí),對(duì)應(yīng)數(shù)據(jù)會(huì)進(jìn)行裝包(boxing),即轉(zhuǎn)換成對(duì)應(yīng)Obj (new String(..), new Boolean(..), or new Number(..), respectively)

  5. 強(qiáng)綁定

    function foo() {
        console.log( this.a );
    }
    
    var obj = {
        a: 2
    };
    
    var bar = function() {
        foo.call( obj ); // 強(qiáng)行將obj傳給this
    };
    
    bar(); // 2
    setTimeout( bar, 100 ); // 2
    
    // `bar` hard binds `foo`'s `this` to `obj`
    // so that it cannot be overriden
    bar.call( window ); // 2
    

    另外使用bind()方法可以強(qiáng)行設(shè)定this的值為某個(gè)其他變量。

使用new關(guān)鍵字時(shí)發(fā)生了什么

  1. 新建立一個(gè)Obj
  2. 將這個(gè)Obj與原型相連接(見(jiàn)后文詳解)
  3. 新建立的Obj設(shè)置為對(duì)應(yīng)函數(shù)的this
  4. 除非函數(shù)返回了一些莫名其妙的東西,否則自動(dòng)返回新建立的元素
function foo(a) {
    this.a = a+1;
}

var bar = new foo( 2 );
console.log( bar.a ); // 3

綁定順序

  1. new綁定的條件下,那么這是一個(gè)全新的Obj

    var bar = new foo()
    
  2. 通過(guò)call或者apply進(jìn)行顯式綁定,或者使用了bind進(jìn)行強(qiáng)綁定,那么這就是個(gè)顯式綁定的Object

        var bar = foo.call( obj2 )
    
  3. 通過(guò)上下文進(jìn)行隱式調(diào)用,或者是某個(gè)對(duì)象的Attr,那么this就是當(dāng)前上下文

        var bar = obj1.foo()
    
  4. 否則就是默認(rèn)綁定了。記住如果是嚴(yán)格模式this=undefined, 否則this=global object

        var bar = foo()
    

例外情況

當(dāng)模塊不需要用到this的時(shí)候,但是卻需要使用bind等函數(shù),可以將null傳到this

同時(shí)這種情況下就會(huì)默認(rèn)使用默認(rèn)綁定的規(guī)則

function foo() {
    console.log( this.a );
}

var a = 2;

foo.call( null ); // 2
function foo(a,b) {
    console.log( "a:" + a + ", b:" + b );
}

// spreading out array as parameters
foo.apply( null, [2, 3] ); // a:2, b:3

// currying with `bind(..)`
var bar = foo.bind( null, 2 );
bar( 3 ); // a:2, b:3

Indirection

話說(shuō)這個(gè)到底怎么翻譯啊。。重定向嗎?

function foo() {
    console.log( this.a );
}

var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };

o.foo(); // 3
(p.foo = o.foo)(); // 2

還是很好理解的,上面的賦值語(yǔ)句執(zhí)行后返回了一個(gè)單純的foo變量,因此導(dǎo)致了Indirection,并且使用了默認(rèn)綁定

注意默認(rèn)綁定的規(guī)則:

  • non-strict mode模式下:引用global object
  • strict mode模式下:對(duì)應(yīng)引用變成Undefined

語(yǔ)義綁定/Lexical this/ES6

ES6多了個(gè)新玩意:箭頭符號(hào)

相關(guān)的綁定稱作"Lexical this"

function foo() {
    // return an arrow function
    return (a) => {
        // `this` here is lexically adopted from `foo()`
        console.log( this.a );
    };
}

var obj1 = {
    a: 2
};

var obj2 = {
    a: 3
};

var bar = foo.call( obj1 ); // 返回值是一個(gè)函數(shù),并且函數(shù)里面的this被綁定到obj1
bar.call( obj2 ); 
// 輸出2, not 3!

如果是普通函數(shù)輸出應(yīng)該是3因?yàn)?code>this綁定到了obj2

而語(yǔ)義綁定無(wú)法被重載,即使用了new關(guān)鍵字

一個(gè)例子:

function foo() {
    setTimeout(() => {
        // `this` here is lexically adopted from `foo()`
        console.log( this.a );
    },100);
}

var obj = {
    a: 2
};

foo.call( obj ); // 2

另一種針對(duì)箭頭符號(hào)的解決方案,通過(guò)外部重新賦值來(lái)實(shí)現(xiàn)可讀性,這樣就知道這兒的this是指向函數(shù)的了

function foo() {
    var self = this; // lexical capture of `this`
    setTimeout( function(){
        console.log( self.a );
    }, 100 );
}

var obj = {
    a: 2
};

foo.call( obj ); // 2

不過(guò)上述兩段代碼都是某種意義上的偷懶,如果真的想要掌握this還是需要:

  1. Use only lexical scope and forget the false pretense of this-style code.

  2. Embrace this-style mechanisms completely, including using bind(..) where necessary, and try to avoid self = this and arrow-function "lexical this" tricks.

Objects

Shadow Copy & Deep Copy

function anotherFunction() { /*..*/ }

var anotherObject = {
    c: true
};

var anotherArray = [];

var myObject = {
    a: 2,
    b: anotherObject,    // reference, not a copy!
    c: anotherArray,    // another reference!
    d: anotherFunction
};

anotherArray.push( anotherObject, myObject );

上面這一段玩意,如果使用

  1. Shadow Copy:那么a會(huì)直接復(fù)制,bcd會(huì)保留對(duì)函數(shù)的引用
  2. Deep Copy:完全復(fù)制abcd,這樣會(huì)造成環(huán)形引用導(dǎo)致錯(cuò)誤

屬性標(biāo)識(shí)符 Property Descriptors

沒(méi)什么好說(shuō)的,就幾個(gè)特殊的屬性:

Writable

注意必須要在嚴(yán)格模式下才會(huì)報(bào)錯(cuò)

"use strict"; //注意必須要在嚴(yán)格模式下才會(huì)報(bào)錯(cuò)

var myObject = {};

Object.defineProperty( myObject, "a", {
value: 2,
writable: false, // not writable!
configurable: true,
enumerable: true
} );

myObject.a = 3; // TypeError

Configurable

表示是否允許下一次使用defineProperty進(jìn)行配置

非嚴(yán)格模式下也會(huì)報(bào)錯(cuò), 這是一種無(wú)法返回的操作

var myObject = {
a: 2
};

myObject.a = 3;
myObject.a;                    // 3

Object.defineProperty( myObject, "a", {
value: 4,
writable: true,
configurable: false,    // not configurable!
enumerable: true
} );

myObject.a;                    // 4
myObject.a = 5;
myObject.a;                    // 5

Object.defineProperty( myObject, "a", {
value: 6,
writable: true,
configurable: true,
enumerable: true
} ); // TypeError

并且設(shè)置為false之后也無(wú)法使用delete刪除對(duì)應(yīng)的屬性

myObject.a;                // 2
delete myObject.a;
myObject.a;                // 2, 上一句上刪除失敗了

delete用于刪除一個(gè)是對(duì)象的屬性, 如果這個(gè)屬性是某變量的最后一個(gè)屬性, 那么delete之后就會(huì)變成空引用并且對(duì)應(yīng)資源會(huì)被回收

但是這玩意不能用于內(nèi)存回收, 他只是刪除了一個(gè)屬性而已

Enumerable

很多奇怪的函數(shù)里面會(huì)進(jìn)行判斷這個(gè)屬性

Immutability

這不是一個(gè)實(shí)際的屬性, 不過(guò)我們有時(shí)候需要將一個(gè)變量變得永恒不變, 通過(guò)下面這些辦法:

對(duì)象常量 Object Constant

很簡(jiǎn)單:

writable:false and configurable:false

var myObject = {};

Object.defineProperty( myObject, "FAVORITE_NUMBER", {
    value: 42,
    writable: false,
    configurable: false
} );

關(guān)閉擴(kuò)充性 Prevent Extensions

Object.preventExtensions(..)將令變量無(wú)法添加新屬性

var myObject = {
    a: 2
};

Object.preventExtensions( myObject );

myObject.b = 3;
myObject.b; // undefined
  1. 嚴(yán)格模式下: 報(bào)錯(cuò)
  2. 非嚴(yán)格模式: 不報(bào)錯(cuò), 但是修改無(wú)效, b依然等于2

Seal

Object.seal(..) = Object.preventExtensions(..) + configurable:false

但是依然可以修改屬性的值

var obj = {name: 'John'}

// 密封
Object.seal(obj)

// 可以修改已有屬性的值
obj.name = 'Backus'
console.log(obj.name) // 'Backus'

Freeze

Object.freeze(..) = Object.seal(..) + writable:false

var obj = {name: 'John'}

// 密封
Object.freeze(obj)

// 無(wú)法修改已有屬性的值
obj.name = 'Backus'
console.log(obj.name) // 'John', 修改失敗

Class

這里只強(qiáng)調(diào)ES6的class的使用方法

基本和多數(shù)OO語(yǔ)言一樣

// unnamed
var Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// named
var Rectangle = class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

構(gòu)造函數(shù)和屬性方法

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area);

靜態(tài)方法

不通過(guò)初始化實(shí)例就能調(diào)用的方法


class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.sqrt(dx*dx + dy*dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

繼承


class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak();

注意即使是以前使用原型創(chuàng)造的父類也可以進(jìn)行繼承

function Animal (name) {
  this.name = name;  
}

Animal.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
}

//和上面一樣繼承

還有另一種繼承方法,使用Object.setPrototypeOf(Dog.prototype, Animal);

var Animal = {
  speak() {
    console.log(this.name + ' makes a noise.');
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

Object.setPrototypeOf(Dog.prototype, Animal);// If you do not do this you will get a TypeError when you invoke speak

var d = new Dog('Mitzie');
d.speak(); //Mitzie makes a noise.

超類

直接用super關(guān)鍵字

class Lion extends Cat {
  speak() {
    super.speak(); // 直接用super關(guān)鍵字
    console.log(this.name + ' roars.');
  }
}

多繼承

ES不支持多繼承,但是可以用mixin的方法偽裝一個(gè):

//將一個(gè)類傳入,并且返回一個(gè)擴(kuò)展之后的類
var calculatorMixin = Base => class extends Base {
  calc() { }
};

//同樣將一個(gè)類傳入,并且返回一個(gè)擴(kuò)展之后的類
var randomizerMixin = Base => class extends Base {
  randomize() { }
};

class Foo { } //初始化一個(gè)類

//將類傳入,進(jìn)行兩次擴(kuò)展,然后擴(kuò)展到子類Bar中,如此就進(jìn)行了多次擴(kuò)張類似于多繼承
class Bar extends calculatorMixin(randomizerMixin(Foo)) { } 

Prototype

所有的Object都的最頂層都是Object.prototype.

Setting & Shadowing Properties

var anotherObject = {
    a: 2
};

var myObject = Object.create( anotherObject );

anotherObject.a; // 2
myObject.a; // 2

anotherObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "a" ); // false,a是繼承過(guò)來(lái)的自然返回false

myObject.a++; // oops, implicit shadowing!

anotherObject.a; // 2
myObject.a; // 3

myObject.hasOwnProperty( "a" ); // true

注意上面如果不給子類自增而直接給父類執(zhí)行自增,那么子類因?yàn)槭钦{(diào)用繼承的屬性因此也會(huì)返回3

  1. 當(dāng)一個(gè)屬性在繼承鏈的高層被發(fā)現(xiàn)并且可寫(xiě)的話, 那么就會(huì)發(fā)生Property Shadowing
  2. 當(dāng)然如果在高層發(fā)現(xiàn)并且不可寫(xiě), 那么就會(huì)設(shè)置失敗, 并且嚴(yán)格模式下會(huì)直接報(bào)錯(cuò)
  3. 單原型鏈上存在一個(gè)與這個(gè)屬性相關(guān)的Setter并且一定會(huì)調(diào)用到這個(gè)Setter, 那么這個(gè)屬性的再次賦值必然會(huì)失敗

constructor

constructor 沒(méi)啥特別的, 一個(gè)類對(duì)應(yīng)的函數(shù)就是一個(gè)constructor

但是使用new關(guān)鍵字的時(shí)候會(huì)調(diào)用這個(gè)constructor, 這是唯一一個(gè)constructor和函數(shù)的區(qū)別

constructor和prototype的關(guān)系

function test() {
    console.log( "Don't mind me!" );
}

var t = new test(); // output: dont mind me

t.constructor===test; // true
test.prototype.constructor == test; // true

  • 首先new的時(shí)候執(zhí)行了對(duì)應(yīng)的constructor, 輸出
  • t是沒(méi)有prototype這個(gè)屬性的, 因?yàn)樗皇莄lass而是obj
  • test.prototype.constructortest()定義的時(shí)候創(chuàng)建的
  • t.constructor也指向同一個(gè)test()

另外, 如果將testprototype改為另一個(gè)方法, 那么t.constructor也會(huì)指向那個(gè)新方法

function test() {
    console.log( "Don't mind me!" );
}

var t1 = new test();
t1.constructor === test; // true

test.prototype = {
    test2: function(){
        console.log( "New" );
    }
}

var t2 = new test();
t2.constructor === Object; // true
t2.constructor === Object.prototype.constructor; // true 

因?yàn)槲覀儗?code>test.prototype轉(zhuǎn)到了一個(gè)新的Obj上面, 并且修改之后test.prototype.constructor不存在了 ,因此接下來(lái)初始化的Obj會(huì)繼承最高層的Object.prototype.constructor

解決這個(gè)問(wèn)題的方法很簡(jiǎn)單, 在切換這個(gè)test.prototype的同時(shí)也將constructor也賦值過(guò)去, 或者直接在新的prototype里面放一個(gè)constructor的屬性

Object.defineProperty( test.prototype, "constructor" , {
    enumerable: false,
    writable: true,
    configurable: true,
    value: test    // point `.constructor` at `test`
} );
t2.constructor === test;// true

Generally, such references should be avoided where possible.

"(Prototypal) Inheritance"

正確的繼承方法

function Foo(name) {
    this.name = name;
}

Foo.prototype.myName = function() {
    return this.name;
};

function Bar(name,label) {
    Foo.call( this, name );
    this.label = label;
}

// here, we make a new `Bar.prototype`
// linked to `Foo.prototype`
Bar.prototype = Object.create( Foo.prototype );

// Beware! Now `Bar.prototype.constructor` is gone,
// and might need to be manually "fixed" if you're
// in the habit of relying on such properties!

Bar.prototype.myLabel = function() {
    return this.label;
};

var a = new Bar( "a", "obj a" );

a.myName(); // "a"
a.myLabel(); // "obj a"

錯(cuò)誤的繼承方法

// doesn't work like you want!
Bar.prototype = Foo.prototype;

// works kinda like you want, but with side-effects you probably don't want :(
Bar.prototype = new Foo();

第一行改變了引用, 因此之后如果希望可以Bar進(jìn)行擴(kuò)展(比如添加新方法)的時(shí)候?qū)嶋H擴(kuò)展了Foo
第二行同樣使用Foo的constructor來(lái)創(chuàng)建新實(shí)例, 但是要注意進(jìn)行擴(kuò)展(比如擴(kuò)展this)的時(shí)候同樣會(huì)擴(kuò)展到Foo

ES6的擴(kuò)展

// pre-ES6
// throws away default existing `Bar.prototype`
Bar.prototype = Object.create( Foo.prototype );

// ES6+
// modifies existing `Bar.prototype`
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

類反射 Reflection

前三種方法中: 父類必然是子類實(shí)例對(duì)應(yīng)的class

就是OOP里面根據(jù)instance獲取對(duì)應(yīng)class的方法:

a instanceof Bar; // true
a instanceof Foo; // true, Bar is inherited from Foo 

更詳細(xì)的一種方法:

function isRelatedTo(o1, o2) {
    function F(){}
    F.prototype = o2;
    return o1 instanceof F; //重點(diǎn)還是和F的prototype進(jìn)行匹配, 即使F是個(gè)空函數(shù)
}

更簡(jiǎn)單的一種方法:

Foo.prototype.isPrototypeOf( a ); // true

簡(jiǎn)單粗暴的ES5的方法:

Object.getPrototypeOf( a ) === Foo.prototype; // false, 如果Bar繼承于Foo, 此處依然檢測(cè)不出來(lái)
Object.getPrototypeOf( a ) === Bar.prototype; // true

總結(jié)

上方繼承代碼集合:

function Foo() { /* .. */ }
Foo.prototype...

function Bar() { /* .. */ }
Bar.prototype = Object.create( Foo.prototype );

var b1 = new Bar( "b1" );

類反射判斷:


// relating `Foo` and `Bar` to each other
Bar.prototype instanceof Foo; // true
Object.getPrototypeOf( Bar.prototype ) === Foo.prototype; // true
Foo.prototype.isPrototypeOf( Bar.prototype ); // true

// relating `b1` to both `Foo` and `Bar`
b1 instanceof Foo; // true
b1 instanceof Bar; // true
Object.getPrototypeOf( b1 ) === Bar.prototype; // true
Foo.prototype.isPrototypeOf( b1 ); // true
Bar.prototype.isPrototypeOf( b1 ); // true

使用原始的對(duì)象連接OLOO (objects-linked-to-other-objects)模式來(lái)實(shí)現(xiàn)上方的代碼:

var Foo = { /* .. */ };

var Bar = Object.create( Foo );
Bar...

var b1 = Object.create( Bar );

對(duì)應(yīng)的類反射就有些不同:

// relating `Foo` and `Bar` to each other
Foo.isPrototypeOf( Bar ); // true
Object.getPrototypeOf( Bar ) === Foo; // true

// relating `b1` to both `Foo` and `Bar`
Foo.isPrototypeOf( b1 ); // true
Bar.isPrototypeOf( b1 ); // true
Object.getPrototypeOf( b1 ) === Bar; // true

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,241評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,939評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,697評(píng)論 6 409
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,182評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,406評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,772評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,973評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,638評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,866評(píng)論 1 285
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,644評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,953評(píng)論 2 373

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