Class的基本語法和繼承

基本語法

簡介

JavaScript語言中,生成實例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù).

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

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

ES6提供更接近傳統(tǒng)語言的寫法,引入了Class(類)這個概念,作為對象的模板.通過class關(guān)鍵字,可以定義類.
基本上,ES6的class可以看做只是一個語法糖,只是讓對象原型的寫法更加清晰,更像面向?qū)ο缶幊痰恼Z法而已.上面的代碼用class改寫,如下:

//定義類
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

constructor方法就是構(gòu)造方法,而this關(guān)鍵字則代表實例對象.
注意,定義"類"的方法的時候,前面不需要加上function這個關(guān)鍵字,直接把函數(shù)定義放進去就可以.另外,方法之間不需要逗號隔開,加了反而報錯.
ES6的類,完全可以看做構(gòu)造函數(shù)的另一種寫法.累的數(shù)據(jù)類型就是函數(shù),類本身就指向構(gòu)造函數(shù).
構(gòu)造函數(shù)的prototype屬性,在ES6的"類"上面繼續(xù)存在.事實上,類的所有方法都定義在類的"prototype"屬性上面.

class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

// 等同于

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

在類的實例上調(diào)用方法,其實就是調(diào)用原型上的方法.
Objet.assgin方法可以很方便的一次向類添加多個方法.

class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

另外,類的內(nèi)部所有定義的方法,都是不可枚舉的.

var Point = function (x, y) {
  // ...
};

Point.prototype.toString = function() {
  // ...
};

Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

采用ES5的寫法,toString方法就是可枚舉的.
類的屬性名,可以采用表達式.

嚴格模式

類和模塊的內(nèi)部,默認就是嚴格模式.只要你的代碼寫在類或模塊之中,就只有嚴格模式可用.

constructor方法

construtor方法是類的默認方法,通過new命令生成對象實例時,自動調(diào)用該方法.一個類必須有constructor方法,如果沒有顯示定義,一個空的constructor方法會被默認添加.
constructor方法默認返回實例對象(即this),完全可以指定返回另外一個對象.

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false

類必須使用new調(diào)用,而普通構(gòu)造函數(shù)不用new也可以執(zhí)行.

類的實例對象

與ES5一樣,實例的屬性除非顯示定義在其本身(即定義在this對象上),否則都是定義在原型上(即定義在class上)
類的所有實例共享一個原型對象,這就意味著,可以通過實例的__proto__屬性為"類"添加方法.
__protp__并不是語言本身的特性,是各大廠商具體實現(xiàn)時添加的私有屬性,不建議生產(chǎn)使用該屬性,避免對環(huán)境產(chǎn)生依賴.生產(chǎn)環(huán)境中,可以使用Object.getPrototypeOf方法來獲取實例對象的原型,然后再來為原型添加方法/屬性.

Class表達式

const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};

注意:這個類的名字是MyClass而不是Me,Me只在Class的內(nèi)部代碼可用,指代當前類.
如果類的內(nèi)部沒有用到的話,可以省略Me.
采用Class表達式,可以寫出立即執(zhí)行的Class

不存在變量提升

ES6不會把類的聲明提升到代碼頭部,這種規(guī)定的愿意與繼承有關(guān),必須保證子類在父類之后定義.

私有方法和私有屬性

現(xiàn)有方法

ES6不提供私有方法這個常見需求,變通方法:
1.在命名上加以區(qū)別
2.將私有方法移除模塊,因為模塊內(nèi)部的所有方法都是對外可見的
3.利用Symbol值的唯一性,將私有方法的名字命名為一個Symbol值

私有方案的提案

目前有一個提案,為class加了私有屬性,方法是在屬性名之前,使用#表示.

class Point {
  #x;

  constructor(x = 0) {
    #x = +x; // 寫成 this.#x 亦可
  }

  get x() { return #x }
  set x(value) { #x = +value }
}

#x就表示私有屬性x,在Point類之外是讀取不到這個屬性的.而且私有屬性與實例的屬性是可以同名的.
另外,私有屬性也可以設(shè)置getter和setter方法.

this的指向

類的內(nèi)部如果含有this,它默認指向類的實例.

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined

如果將這個方法提取出來單獨使用,this會指向該方法運行時所在的環(huán)境.
一個比較簡單的解決方法是,在構(gòu)造方法中綁定this.

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}

另一種解決方法是試用箭頭函數(shù)

class Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`);
    };
  }

  // ...
}

還有一種解決方法就是使用Proxy,獲取方法的時候,自動綁定this.

function selfish (target) {
  const cache = new WeakMap();
  const handler = {
    get (target, key) {
      const value = Reflect.get(target, key);
      if (typeof value !== 'function') {
        return value;
      }
      if (!cache.has(value)) {
        cache.set(value, value.bind(target));
      }
      return cache.get(value);
    }
  };
  const proxy = new Proxy(target, handler);
  return proxy;
}

const logger = selfish(new Logger());

name屬性

name屬性總是返回緊跟在class關(guān)鍵字后面的類名

Class的取值函數(shù)(getter)和存執(zhí)函數(shù)(setter)

在"類"的內(nèi)部可以使用getset關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為.
存值函數(shù)和取值函數(shù)是設(shè)置在屬性的Descriptor對象上

class CustomHTMLElement {
  constructor(element) {
    this.element = element;
  }

  get html() {
    return this.element.innerHTML;
  }

  set html(value) {
    this.element.innerHTML = value;
  }
}

var descriptor = Object.getOwnPropertyDescriptor(
  CustomHTMLElement.prototype, "html"
);

"get" in descriptor  // true
"set" in descriptor  // true

Class的Generator方法

如果某個方法之前加上星號*,就表示該方法是個Generator函數(shù).

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

Symbol.iterator方法返回一個Foo類的默認遍歷器,for...of循環(huán)會自動調(diào)用這個遍歷器.

Class的靜態(tài)方法

類相當于實例的原型,所有在類中定義的方法,都會被實例繼承.如果在一個方法前,加上static關(guān)鍵字,就表示該方法不會被實例繼承,而是直接通過類來調(diào)用,這就成為"靜態(tài)方法".
注意:如果靜態(tài)方法包含this關(guān)鍵字,這個this指的是類,而不是實例.

class Foo {
  static bar () {
    this.baz();
  }
  static baz () {
    console.log('hello');
  }
  baz () {
    console.log('world');
  }
}

Foo.bar() // hello

靜態(tài)方法可以與非靜態(tài)方法重名.
父類的靜態(tài)方法,可以被子類繼承,靜態(tài)方法也是可以從super對象上調(diào)用的.

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'

//從super對象上調(diào)用
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"

Class的靜態(tài)屬性和實例屬性

靜態(tài)屬性指的是Class本身的屬性,即Class.propName,而不是定義在實例對象this上的屬性.

class Foo {
}

Foo.prop = 1;
Foo.prop // 1
為Foo類定義了一個靜態(tài)屬性prop

目前,只有這種方法可行,因為ES6明確規(guī)定,Class內(nèi)部只有靜態(tài)方法,沒有靜態(tài)屬性.
目前有一個關(guān)于靜態(tài)屬性的提案,對實例屬性和靜態(tài)屬性都規(guī)定了新的寫法:
1.類的實例屬性
類的實例屬性可以用等式,寫入類的定義之中

class MyClass {
  myProp = 42;

  constructor() {
    console.log(this.myProp); // 42
  }
}

以前我們定義實例屬性,只能寫在類的constructor方法里面.

class ReactCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
}

為了可讀性的目的,對于那些在construtor里面已經(jīng)定義的實例屬性,新鞋發(fā)允許直接列出.
2.類的靜態(tài)屬性
只要在上面的實例寫法前面加上static關(guān)鍵字就可以了

class MyClass {
  static myStaticProp = 42;

  constructor() {
    console.log(MyClass.myStaticProp); // 42
  }
}

new.target屬性

new是從構(gòu)造函數(shù)生成實例對象的命令.ES6為new命令引入一個new.target屬性,該屬性一般用在構(gòu)造函數(shù)之中,返回new命令作用域的那個構(gòu)造函數(shù).如果構(gòu)造函數(shù)不是通過new命令調(diào)用的,new.target會返回undefined,因此這個屬性可以用來確定構(gòu)造函數(shù)是怎么調(diào)用的.

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必須使用 new 命令生成實例');
  }
}

// 另一種寫法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必須使用 new 命令生成實例');
  }
}

var person = new Person('張三'); // 正確
var notAPerson = Person.call(person, '張三');  // 報錯

Class內(nèi)部調(diào)用new.target返回當前Class.需要注意的是,子類繼承父類時,new.target會返回子類.

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length) {
    super(length, length);
  }
}

var obj = new Square(3); // 輸出 false

利用這個特點,可以寫出不能獨立使用,必須繼承后才能使用的類.

class的繼承

基本用法

Class可以通過extends關(guān)鍵字實現(xiàn)繼承,ES5是通過修改原型鏈實現(xiàn)繼承的

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調(diào)用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
  }
}

constructor方法和toString方法之中,都出現(xiàn)了super關(guān)鍵字,它在這里表示父類的構(gòu)造函數(shù),用來新建父類的this對象.
子類必須在constructor方法中調(diào)用super方法,否則新建實例時會報錯.這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工.如果不調(diào)用super方法,子類就得不到this對象.
ES5的繼承實質(zhì)是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this)).ES6的繼承機制完全不同,實質(zhì)是先創(chuàng)造父類的實例對象this(所以必須先調(diào)用super方法),然后再用子類的構(gòu)造函數(shù)修改this.
如果子類沒有定義constructor方法,這個方法會被默認添加.

class ColorPoint extends Point {
}

// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

特別注意:在子類的構(gòu)造函數(shù)總,只有調(diào)用super之后,才可以使用this關(guān)鍵字,否則會報錯.
通過子類生成實例的對象同時是父類和子類的實例.
父類的靜態(tài)方法也會被子類繼承.

Object.getPrototypeOf()

該方法可以用來從子類上獲取父類,可以用這個方法判斷,一個類是否繼承了另一個類.

super關(guān)鍵字

super關(guān)鍵字既可以當做函數(shù)使用,也可以當做對象使用.


第一種情況,super作為函數(shù)調(diào)用時,代表父類的構(gòu)造函數(shù).ES6要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù).
注意.super雖然代表了父類的構(gòu)造函數(shù),但是返回的是子類的實例.即super內(nèi)部的this指向的是子類,因此super()在這里相當于父類.prototype.constructor.call(this).
作為函數(shù)時,super()只能用在子類的構(gòu)造函數(shù)中.


第二種情況,super作為對象時,在普通方法總,指向父類的原型兌現(xiàn);在靜態(tài)方法中,指向父類.

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();

這里需要注意,由于super指向父類的原型對象,所以定義在父類實例上的方法或?qū)傩?是無法通過super調(diào)用的.
在子類普通方法中通過super調(diào)用父類的方法時,方法內(nèi)部的this指向當前的子類實例.

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2

由于this指向子類實例,所以如果通過super對某個屬性賦值,這是super就是this,賦值的舒心會變成子類實例的屬性.
如果super作為對象用在靜態(tài)方法中,這時super將指向父類,而不是父類的原型對象.

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }

  myMethod(msg) {
    console.log('instance', msg);
  }
}

class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }

  myMethod(msg) {
    super.myMethod(msg);
  }
}

Child.myMethod(1); // static 1

var child = new Child();
child.myMethod(2); // instance 2

在子類的靜態(tài)方法中通過super調(diào)用父類的方法時,方法內(nèi)部的this指向當前的子類,而不是子類的實例.
注意,使用super的時候,必須顯示指定是作為函數(shù),還是作為對象使用,否則會報錯.
由于對象總是繼承其它對象的,所以可以在任意一個對象中,使用super關(guān)鍵字.

var obj = {
  toString() {
    return "MyObject: " + super.toString();
  }
};

obj.toString(); // MyObject: [object Object]

類的prototype屬性和proto屬性

大多數(shù)瀏覽器的ES5實現(xiàn)中,每一個對象都有__proto__屬性,指向?qū)?yīng)的構(gòu)造函數(shù)的prototype屬性.Class作為構(gòu)造函數(shù)的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈.
1.子類的__proto__屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類.
2.子類prototype屬性的__proto__屬性表示方法的繼承,總是指向父類的prototype屬性.

extends的繼承目標

extends關(guān)鍵字后面可以跟多種類型的值.
只要是一個有prototype屬性的函數(shù),就能被B繼承.由于函數(shù)都有prototype屬性(除了Function.prototype函數(shù)),因此被繼承的可以是任意函數(shù).
三種特殊情況:

  1. 繼承Object
class A extends Object {
}

A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

這種情況下,A其實就是構(gòu)造函數(shù)Object的復(fù)制,A的實例就是Object的實例.

  1. 不存在任何繼承
class A {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

這種情況下,A作為一個基類(不存在任何繼承),就是一個普通函數(shù),所以直接繼承Function.prototype.但是,A調(diào)用后返回一個空對象,所以A.prototype.__proto__指向構(gòu)造函數(shù)(Object)的prototype屬性.

  1. 子類繼承null
class A extends null {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === undefined // true

與第二種情況類似.A也是一個普通函數(shù),所以直接繼承Funciotn.prototype.但是,A調(diào)用后返回的對象不繼承任何方法,所以他的__proto__指向Function.prototype,即實質(zhì)執(zhí)行了下面代碼:

class C extends null {
  constructor() { return Object.create(null); }
}

實例的__proto__屬性

子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性.子類的原型的原型就是父類的原型.

原生構(gòu)造函數(shù)的繼承

ECMAScript的原生構(gòu)造函數(shù)大致有:

  • Boolean
  • Number
  • String
  • Array
  • Date
  • Function
  • RegExp
  • Error
  • Object

以前這些原生構(gòu)造函數(shù)無法繼承,之所以不能繼承,是因為子類無法獲得原生構(gòu)造函數(shù)的內(nèi)部屬性,通過apply()或者分配給原型對象都不行.原生構(gòu)造函數(shù)會忽略apply方法傳入的this,即原生構(gòu)造函數(shù)的this無法綁定,導(dǎo)致拿不到內(nèi)部屬性.
ES5是先新建子類的實例對象this,再將父類的屬性添加到子類上,由于子類的內(nèi)部屬性無法獲取,導(dǎo)致無法繼承原生的構(gòu)造函數(shù).比如,Array構(gòu)造函數(shù)有一個內(nèi)部屬性[[DefineOenProperty]],用來定義新屬性時,更新length屬性,這個內(nèi)部屬性無法在子類獲取,導(dǎo)致子類的length屬性行為不正常.
ES6允許集成原生構(gòu)造函數(shù)定義子類,因為ES6是先新建父類的實例對象this,然后再用子類的構(gòu)造函數(shù)修飾this,使得父類的所有行為都可以繼承.這意味著,ES6可以自定義原生數(shù)據(jù)結(jié)構(gòu)的子類.
extends關(guān)鍵字不僅可以用來繼承類,還可以用來繼承原生的構(gòu)造函數(shù).因此可以再原生數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上,定義自己的數(shù)據(jù)結(jié)構(gòu).
注意,繼承Object的子類,有一個行為差異

class NewObj extends Object{
  constructor(){
    super(...arguments);
  }
}
var o = new NewObj({attr: true});
o.attr === true  // false

NewObj繼承了Object,但是無法通過super方法向父類傳參.這是因為ES6改變了Object構(gòu)造函數(shù)的行為,一旦發(fā)現(xiàn)Obejct方法不是通過new Object()這種形式調(diào)用,ES6規(guī)定Object構(gòu)造函數(shù)會忽略參數(shù).

Mixin模式的實現(xiàn)

Mixin指的是多個對象合成一個新的對象,新對象具有各個組成成員的接口.最簡單實現(xiàn)為:

const a = {
  a: 'a'
};
const b = {
  b: 'b'
};
const c = {...a, ...b}; // {a: 'a', b: 'b'}

更完備的實現(xiàn),將多個類的接口"混入"(mix in)另一個類

function mix(...mixins) {
  class Mix {}

  for (let mixin of mixins) {
    copyProperties(Mix, mixin); // 拷貝實例屬性
    copyProperties(Mix.prototype, mixin.prototype); // 拷貝原型屬性
  }

  return Mix;
}

function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor"
      && key !== "prototype"
      && key !== "name"
    ) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}

上面代碼的mix函數(shù),可以將多個對象合成一個類.使用的時候,只要繼承這個類即可.

class DistributedEdit extends mix(Loggable, Serializable) {
  // ...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市话速,隨后出現(xiàn)的幾起案子署拟,更是在濱河造成了極大的恐慌浪耘,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎柄,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機伐割,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刃唤,“玉大人隔心,你說我怎么就攤上這事∩邪” “怎么了硬霍?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笼裳。 經(jīng)常有香客問我唯卖,道長,這世上最難降的妖魔是什么躬柬? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任耐床,我火速辦了婚禮,結(jié)果婚禮上楔脯,老公的妹妹穿的比我還像新娘。我一直安慰自己胯甩,他們只是感情好昧廷,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布堪嫂。 她就那樣靜靜地躺著,像睡著了一般木柬。 火紅的嫁衣襯著肌膚如雪皆串。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天眉枕,我揣著相機與錄音恶复,去河邊找鬼。 笑死速挑,一個胖子當著我的面吹牛谤牡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姥宝,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼翅萤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腊满?” 一聲冷哼從身側(cè)響起套么,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碳蛋,沒想到半個月后胚泌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡肃弟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年玷室,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愕乎。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡阵苇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出感论,到底是詐尸還是另有隱情绅项,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布比肄,位于F島的核電站快耿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芳绩。R本人自食惡果不足惜掀亥,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妥色。 院中可真熱鬧搪花,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至幢踏,卻和暖如春髓需,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背房蝉。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工僚匆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搭幻。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓咧擂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親粗卜。 傳聞我的和親對象是個殘疾皇子屋确,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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