2.javascript中this指向

为什么需要this

this用于:当对象的方法被调用时,在被调用的方法中获取调用当前方法的对象

function Person(name) {
  this.name = name;
  this.eatting = function () {
    console.log(this.name + "在吃东西");
  };
  this.running = function () {
    console.log(this.name + "在奔跑");
  };
  this.studying = function () {
    console.log(this.name + "在学习");
  };
}

var why = new Person("why");
why.eatting();
why.studying();
why.running();
var lily = new Person("lily");
lily.eatting();
lily.studying();
lily.running();

image.png

全局作用域下的this

在浏览器环境下,全局作用域下(即全局执行上下文中)的this指向window(即globalObject)
在Node环境下,全局作用域下的this指向{}

console.log(this)
image.png

image.png

但是,开发中很少直接在全局作用域下去使用this,通常都是在函数中使用

  • 所有的函数在被调用时,都会创建一个执行上下文:
  • 这个上下文中记录着函数的调用栈、AO对象等;
  • this也是其中的一条记录;

this到底指向什么

我们先来看一个让人困惑的问题:

  • 定义一个函数,我们采用三种不同的方式对它进行调用,它产生了三种不同的结果
function foo() {
  console.log(this); 
}
var obj = {
  name: "why",
  foo: foo
};
foo() //this -> window
obj.foo() //this -> obj;
foo.call('call') // this -> String{"call"}

image.png
这个的案例可以给我们什么样的启示呢?
  • 1.函数在调用时,JavaScript会默认给this绑定一个值;
  • 2.this的绑定和定义的位置(编写的位置)没有关系;
  • 3.this的绑定和调用方式以及调用的位置有关系;
  • 4.this是在运行时被绑定的;
那么this到底是怎么样的绑定规则呢?一起来学习一下吧
  • 绑定一:默认绑定;
  • 绑定二:隐式绑定;
  • 绑定三:显示绑定;
  • 绑定四:new绑定;

this的绑定规则

默认绑定

什么情况下使用默认绑定呢?独立函数调用。

  • 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
    在浏览器环境下,
    独立函数调用,函数内部的this默认被绑定为window
    跟函数的调用位置无关
    案例1
function foo1() {
  console.log(this); //window
}
function foo2() {
  console.log(this); //window
  foo1();
}
function foo3() {
  console.log(this); //window
  foo2();
}
foo3();

案例2

var obj = {
  name: "why",
  foo: function () {
    console.log(this);
  },
};
var bar = obj.foo;
bar(); //window

案例3

function foo() {
  console.log(this);
}
var obj1 = {
  name: "why",
  foo: foo,
};
var bar1 = obj.foo;
bar1(); //window

案例4

function foo4() {
  return function baz() {
    console.log(this);
  };
}
var baz = foo4();
baz(); //window

隐式绑定

另外一种比较常见的调用方式是通过某个对象进行调用的:

  • 也就是它的调用位置中,是通过某个对象发起的函数调用。
    函数以对象的方法被调用,js引擎会在函数的执行上下文中,把当前调用此函数的对象隐式绑定到this
    案例1
function foo() {
  console.log(this);
}
var obj = {
  name: "why",
  foo: foo,
};

obj.foo(); //this

案例二

var person = {
  name: "why",
  eatting: function () {
    console.log(this);
  },
  running: function () {
    console.log(this);
  },
};
person.eatting(); //person对象
person.running(); //person对象

案例三

function foo1() {
  console.log(this);
}
var obj1 = {
  name: "why",
  foo: foo,
};
var obj2 = {
  name: "why",
  foo: obj1.foo,
};
obj2.foo(); //obj2对象

显示绑定

隐式绑定有一个前提条件:

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性);
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
  • 正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
  • JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。
  • 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给this准备的。
  • 在调用这个函数时,会将this绑定到这个传入的对象上。
因为上面的过程,我们明确的绑定了this指向的对象,所以称之为 显示绑定

使用call/apply/bind可以显示的为函数执行上下文中的this绑定对象
案例1:
foo独立调用,函数执行上下文中的this会被默认绑定为全局对象wiondow
如果想在函数foo执行时,其创建的执行上下文中的this绑定为obj对象,但时又不想给obj对象添加foo方法
可以使用call/apply/bind显示的把foo函数执行上下文中的this绑定为obj对象

function foo() {
  console.log(this);
}
var obj = {
  name: "why",
};


foo.call(obj); //obj对象
foo.apply(obj); //obj对象
//foo.bind(obj)执行的结果会返回一个全新的函数,返回的函数的执行上下文的this被显示的绑定为了obj
var bar = foo.bind(obj)
bar() //obj

案例2
foo.bind(obj)执行的结果会返回一个全新的函数,返回的函数的执行上下文的this被显示的绑定为了obj

function foo() {
  console.log(this);
}
var obj = {
  name: "why",
};


var bar = foo.bind(obj)
bar() //obj对象

new绑定

JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。

使用new关键字来调用函数是,会执行如下的操作:
  • 1.创建一个全新的对象;
  • 2.这个新对象会被执行prototype连接;
  • 3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
  • 4.如果函数没有返回其他对象,表达式会返回这个新对象;

通过new 执行函数构造器的时候,会创建一个新对象,
并把函数构造器执行时创建的执行上下文中的this绑定为这个新对象
如果函数构造器没有返回值,会默认返回这个新对象

function Person(name, age) {
  this.name = name;
  this.age = age;
}

var why = new Person("why", 18);
console.log(why.name, why.age);
var kobe = new Person("kobe", 40);
console.log(kobe.name, kobe.age);

一些高阶函数的回到函数中的this分析

1.setTimeout函数

setTimeout函数的回调函数在其内部是被独立调用的,
所以,回调函数执行时创建的执行上下文中的this指向window

setTimeout(function () {
  console.log(this); //this=> window
}, 3000);

模拟setTimeout函数的实现

function hySetTimeout(callback, duration) {
  callback();
}
2.onClick函数

当js引擎监听到boxDiv被点击时,会调用boxDiv.onClick(),
onClick函数执行上下文内部的this被隐式绑定为触发事件的DOM元素对象

var boxDiv = document.querySelector(".box");
boxDiv.onClick = function () {
  console.log(this); //this -> boxDiv
};
3.addEventListener函数
var box1Div = document.querySelector(".box1");
box1Div.addEventListener("click", function () {
  console.log(this); //this-> box1Div
});
box1Div.addEventListener("click", function () {
  console.log(this);//this-> box1Div
});
box1Div.addEventListener("click", function () {
  console.log(this);//this-> box1Div
});

模拟实现

var eventMap = new Map();
HTMLDivElement.prototype.addEventListener = function (eventType, eventFn) {
   var eventArray = eventMap.get(eventType);
   if (!eventArray) {
     eventMap.set(eventType, new Set());
   } else {
     //把监听函数添加到事件数组
     eventArray.add(eventFn);
   }
 }; 
//当监听到事件触发时,迭代eventArray中的函数,
eventMap.get("click").forEach((fn) => {
   fn.call(box1Div);
});
4.forEach/map/filter/find
var names = ["lily", "why", "kobe"];
names.forEach(function (item) {
  console.log(item, this); //this -> window
});
names.map(function (item) {
  console.log(item, this); //this -> window
});

//forEach/map回调函数执行时的执行上下文中this会被显示绑定到第二个参数对象
//如果第二个参数不为对象,会转换为对象
names.forEach(function (item) {
  console.log(item, this); //this -> String{"abc"}
}, "abc");
names.map(function (item) {
  console.log(item, this); //this -> String{"abc"}
}, "abc");

this绑定规则的优先级

学习了四条规则,接下来开发中我们只需要去查找函数的调用应用了哪条规则即可,但是如果一个函数调用位置应用了多
条规则,优先级谁更高呢?

1.默认规则的优先级最低
  • 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定

代码测试

//1.显示绑定优先级高于隐式绑定
//案例1
var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

obj.foo.call("call"); // this -> String{"call"}

//案例2
function foo() {
  console.log(this);
}
var obj1 = {
  name: "obj",
  foo: foo.bind("coder"),
};
obj1.foo(); // this -> String{"coder"}
3.new绑定优先级高于隐式绑定

代码测试

//2.new绑定优先级高于隐式绑定
//案例1
var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

var f = new obj.foo() //this -> foo {}
4.new绑定优先级高于bind
  • new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
  • new绑定可以和bind一起使用,new绑定优先级更高
    代码测试
//2.new绑定优先级高于显式绑定
//案例1
function foo() {
  console.log(this);
}

var bar = foo.bind("aaa");
var obj = new bar(); //this -> foo {}

this规则之外

1.忽略显示绑定

apply/call/bind内部,做了判断,如果第一个参数为undefined或null, 给函数执行上下文内部的this绑定为window

function foo() {
  console.log(this);
}
foo.apply("aaa"); //this -> String {"aaa"}
foo.apply({}); //this -> {}

//apply/call/bind内部,做了判断,如果第一个参数为undefined或null, 给函数执行上下文内部的this绑定为window
foo.apply(undefined); //this -> window
foo.apply(null); //this -> window
foo.call(undefined); //this -> window
foo.call(null); //this -> window

var bar = foo.bind(null);
var baz = foo.bind(undefined);
bar(); //this -> window
baz(); //this -> window

2. 间接函数引用

var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

var obj1 = {
  name: "why",
};

(obj1.bar = obj.foo)(); //this -> window
//obj1.bar = obj.foo返回obj.foo这个函数对象的16进制引用地址,
//函数对象引用地址()调用,是独立函数调用

需要注意:下面代码不能执行:obj对象后面没有分号

var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

var obj1 = {
  name: "why",
}

(obj1.bar = obj.foo)(); 

因为代码在解析阶段,会把下面当成一段代码

var obj1 = {
  name: "why",
}

(obj1.bar = obj.foo)();

比如我不加分号的情况下,格式化代码:

var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

var obj1 = {
  name: "why",
}((obj1.bar = obj.foo))(); 

3.ES6箭头函数

箭头函数是ES6之后增加的一种编写函数的方法,并且它比函数表达式要更加简洁:

  • 箭头函数不会绑定thisarguments属性;
  • 箭头函数不能作为构造函数来使用(不能和new一起来使用,会抛出错误);
箭头函数如何编写呢?
  • (): 函数的参数
  • {}: 函数的执行体
var sum = (num1, num2, num3) => {
  console.log(num1 + num2 + num3);
};

//高阶函数在使用时也可以传入箭头函数
var nums = [2, 34, 67];
nums.forEach((item, index, arr) => {
  console.log(item, index, arr);
});

箭头函数的编写优化
优化一: 如果只有一个参数()可以省略
//1.如果参数只有一个,小括号可以省略
nums.forEach((item) => {
  console.log(item);
});
优化二: 如果函数执行体中只有一行代码, 那么可以省略大括号
  • 并且这行代码的返回值会作为整个函数的返回值
//2.如果函数执行体中只有一行代码, 那么可以省略大括号,并且这行代码的执行结果会作为整个函数的返回值
nums.forEach((item) => console.log(item));

var doubleNums = nums.map((item) => item * 2);
console.log(doubleNums);
var newNums = nums
  .filter((item) => item % 2 === 0)
  .map((item) => item * 100)
  .reduce((preResult, item) => preResult + item);

console.log(newNums);
优化三: 如果函数执行体只有返回一个对象, 那么需要给这个对象加上()
//3. 如果函数执行体只有返回一个对象, 那么需要给这个对象加上()
var foo = () => {
  return { name: "why", age: 18 };
};
var bar = () => ({ name: "why", age: 18 });
this规则之外 – ES6箭头函数

箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this

我们来看一个模拟网络请求的案例:
  • 这里我使用setTimeout来模拟网络请求,请求到数据后如何可以存放到data中呢?
  • 我们需要拿到obj对象,设置data;
  • 但是直接拿到的this是window,我们需要在外层定义:var _this = this
  • 在setTimeout的回调函数中使用_this就代表了obj对象
//箭头函数之前的解决方案
var obj = {
  data: [],
  getData: function () {
    const _this = this;
    setTimeout(function () {
      //setTimeout回调函数执行上下文中的this为window
      var result = ["abc", "bac"];
      _this.data = result;
    }, 1000);
  },
};

使用箭头函数

var obj = {
  data: [],
  getData: function () {
    setTimeout(() => {
      //setTimeout回调函数为箭头函数,
      //因为箭头函数并不绑定this对象,那么this引用就会从上层作用域中找到对应的this
      var result = ["abc", "bac"];
      this.data = result;
    }, 1000);
  },
};

this面试题

面试题一

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
};
function sayName() {
  var foo = person.sayName;
  foo(); 
  person.sayName();  
  (person.sayName)(); 
  (b = person.sayName)();  
}
sayName();

最终输出结果: window person person window

解析
/**
 * 全局执行上下文编译阶段:
 * VO->GO  GO -> {name: undefined, person: #0x100, sayName: #0x200}
 * #0x100 -> {name: "person", sayName: #0x300}
 * 
 * 全局执行上下文执行阶段:
 * VO->GO  GO -> {name: window, person: #0x100, sayName: #0x200}
 */
var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
};
function sayName() {
  //父级作用域 GO
  /**
   * 创建sayName函数执行上下文
   * 编译阶段:
   * VO->AO  AO-> {foo:undefined}
   * 执行阶段:
   * VO->AO  AO-> {foo:0x300}
   */
  var foo = person.sayName;
  foo(); //window   独立函数调用this -> window  
  person.sayName();  //person 作为对象的方法调用,this被隐式绑定为调用函数的对象person person.name为person
  (person.sayName)(); //person 加不加小括号效果一样
  (b = person.sayName)();  //window  赋值表达式最后会返回person.sayName的引用地址#0x300, #0x300()是独立函数调用
}
sayName();

面试题二

var name = "window";
var person1 = {
 name: "person1",
 foo1: function () {
   console.log(this.name);
 },
 foo2: () => console.log(this.name), 
 foo3: function () {
   return function () {
     console.log(this.name);
   };
 },
 foo4: function () {
   return () => {
     console.log(this.name);
   };
 },
};
var person2 = { name: "person2" };

person1.foo1();
person1.foo1.call(person2); 
person1.foo2();
person1.foo2.call(person2); 

person1.foo3()();
person1.foo3.call(person2)(); 
person1.foo3().call(person2); 

person1.foo4()(); 
person1.foo4.call(person2)(); 
person1.foo4().call(person2); 

执行结果 person person2 window window window window person2 person1 person2 person1

解析
/**
 * 全局执行上下文:
 * 编译阶段:
 * VO-> GO  GO {name: undefined, person1: #0x100, person2: #0x200}
 * person1: #0x100 {name: 'person1', foo1: #0x101, foo2: #0x102, foo3: # 0x103, foo4: # 0x104}
 * person2: #0x200 { name: "person2" }
 * 执行阶段: GO {name: "window", person1: #0x100, person2: #0x200}
 */
var name = "window";
var person1 = {
  name: "person1",
  foo1: function () {
    console.log(this.name);
  },
  foo2: () => console.log(this.name), //父级作用域GO
  foo3: function () {
    //父级作用域为GO
    return function () {
      //父级作用域为foo3 AO
      console.log(this.name);
    };
  },
  foo4: function () {
    //父级作用域为GO
    return () => {
      //箭头函数不绑定this  this为其父级作用域的this
      console.log(this.name);
    };
  },
};
var person2 = { name: "person2" };

person1.foo1(); //person1  隐式绑定this -> person1
person1.foo1.call(person2); //person2 显示绑定this -> person2
person1.foo2(); //window 箭头函数不绑定this this为父级作用域的this
person1.foo2.call(person2); //window 箭头函数显示绑定this无效 this为父级作用域的this

person1.foo3()(); //window 独立函数调用 this->window
person1.foo3.call(person2)(); // window 独立函数调用 this->window
person1.foo3().call(person2); //person2 显示绑定 this为person2

person1.foo4()(); //person1
/**
 * person1.foo4()();解析:
 * 1.person1.foo4()
 * 编译阶段:
 * 父级作用域 GO
 * AO {anonyFn: #0x401}
 *  this-> person1
 * 2.anonyFn()
 * 父级作用域  AO(person1.foo4)
 * 箭头函数不绑定this this为父级作用域的this
 */
person1.foo4.call(person2)(); // person2
/**
 * person1.foo4.call(person2)();解析:
 * 1.person1.foo4.call(person2)
 * 执行阶段:
 * 父级作用域 GO
 * AO {anonyFn: #0x402}
 * this被显示绑定为person2
 * 2.anonyFn()
 * 父级作用域  AO(person1.foo4)
 * 箭头函数不绑定this this为父级作用域的this
 */

person1.foo4().call(person2); //person1
/**
 * person1.foo4().call(person2);解析:
 * 1.person1.foo4()
 * 编译阶段:
 * 父级作用域 GO
 * AO {anonyFn: #0x403}
 *  this-> person1
 * 2.anonyFn()
 * 父级作用域  AO(person1.foo4)
 * 箭头函数不绑定this,显示绑定this无效 this为父级作用域的this
 */

//执行结果 person person2 window window window window person2 person1 person2 person1

面试题三

var name = "window";
function Person(name) {
  this.name = name;
  this.foo1 = function () {
    console.log(this.name);
  };
  this.foo2 = () => console.log(this.name); 
  this.foo3 = function () {
    return function () {
      console.log(this.name);
    };
  };
  this.foo4 = function () {
    return () => {
      console.log(this.name);
    };
  };
}

var person1 = new Person("person1")
var person2 = new Person("person2")

person1.foo1() 
person1.foo1.call(person2) 

person1.foo2() 
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)() 
person1.foo3().call(person2) 

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

执行结果: person1 person2 person1 person1 window window person2 person1 person2 person1

解析
var name = "window";
function Person(name) {
  //父级作用域GO
  this.name = name;
  this.foo1 = function () {
    console.log(this.name);
  };
  this.foo2 = () => console.log(this.name);
  this.foo3 = function () {
    return function () {
      console.log(this.name);
    };
  };
  this.foo4 = function () {
    return () => {
      console.log(this.name);
    };
  };
}

var person1 = new Person("person1")
var person2 = new Person("person2")

person1.foo1() //person1
person1.foo1.call(person2) //person2

person1.foo2() //person1 箭头函数不绑定this, this为父级作用域中的this
person1.foo2.call(person2)//person1 箭头函数不绑定this,显示绑定this无效 this为父级作用域中的this

person1.foo3()() //window 独立函数调用 this默认被绑定为window
person1.foo3.call(person2)() //window 独立函数调用 this默认被绑定为window
person1.foo3().call(person2) //person2 显示绑定this为person2

person1.foo4()() //person1 箭头函数不绑定this, this为父级作用域中的this
person1.foo4.call(person2)()//person2 箭头函数不绑定this, this为父级作用域中的this
person1.foo4().call(person2)//person1 箭头函数不绑定this,显示绑定this无效 this为父级作用域中的this

面试题四

var name = "window";
function Person(name) {
  this.name = name;
  this.obj = {
    name: "obj",
    foo1: function () {
      return function () {
        console.log(this.name);
      };
    },
    foo2: function () {
      return () => {
        console.log(this.name);
      };
    },
  };
}

var person1 = new Person("person1");
var person2 = new Person("person2");

person1.obj.foo1()(); //window 独立函数调用
person1.obj.foo1.call(person2)(); //window 独立函数调用
person1.obj.foo1().call(person2); //person2 显示绑定this

person1.obj.foo2()(); //obj 箭头函数不绑定this,this为上级作用域的this
person1.obj.foo2.call(person2)(); //person2 箭头函数不绑定this,this为上级作用域的this
person1.obj.foo2().call(person2); //obj 箭头函数不绑定this,显示绑定无效,this为上级作用域的this

非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript 的知识

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

推荐阅读更多精彩内容