JavaScript

JavaScript

一、JavaScript概述

1.1 JavaScript简介

JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态的功能。

1.2 JavaScript的使用

  1. 直接在HTML页面的body中加入< script>标签
<body>
    <script type="text/javascript">
        document.write("hello JavaScript");  // 向页面打印一条字符串
    </script>
</body>
  1. 外部引用,将JavaScript代码单独写到一个.js文件中,然后再将该.js文件引入HTML页面
<body>
    <script type="text/javascript" src="js/myjs.js"></script>
</body>

二、JavaScript基本语法

2.1 变量声明

在JavaScript中,任何变量都用var关键字来声明,var是variable的简写。

注意:JavaScript中的关键字不可以作为变量名

var a = 1;

JavaScript的部分关键字:

abstract、else、instanceof、super、boolean、enum、int、switch、break、export、interface、
synchronized、byte、extends、let、this、case、false、long、throw、catch、final、native、throws、
char、finally、new、transient、class、float、null、true、const、for、package、try、continue、
function、private、typeof、debugger、goto、protected、var、default、if、public、void、delete、
implements、return、bolatile、do、import、short、while、double、in、static、with

2.2 基本类型

变量的基本类型有Number、String、Boolean、Undefined、Null五种

  • 在Java中当一个变量未被初始化的时候,Java中是null或者基本数据类型的默认值
  • 在JavaScript中,当一个变量未被初始化的时候,它的值为undefined;当一个引用(类似Java中的对象)不存在时,它才为null
var a = 1;  // Number
var a = "1";  // String
var a = true;  // Boolean

var a;
console.log(a);  // Undefined

2.3 引用类型

在Java中需要进行类定义,然后再实例化对象

public class Student {
    public int id;
    public String name;
    public int age;
}
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.id = 1;
        student.name = "张三";
        student.age = 18;
    }
}

在JavaScript中对象可以直接写出来

var student = {id: 1, name: "张三", age: 18};
/*
  实际上, student被赋值为了一个JSON, JSON全称是JavaScript Object Notation
  叫做JavaScript对象标记. 也就是说, 在JavaScript中, JSON是用于标记一个对象的
*/

2.4 数组类型

在JavaScript中数组是一个Array类型,同一个数组中可以存放多种数据类型的数据

var n = [100, true, "hello", {name: "张三", age: 20}];
alert(n.length);  // 弹出框

2.5 运算符

逻辑运算:

名称 运算符 描述
&& 要求表达式左右把两边的表达式同为true,整体结果才是true
|| 要求表达式左右两边的表达式只要有一个为true,整体结果就是true
! 将布尔值取反操作

关系运算:

名称 运算符
等于 ==
小于 <
小于或等于 <=
大于 >
大于或等于 >=
不等于 !=
值和类型都相同 ===
// 注意: "=="和"==="的区别
//  "==" 相当于比较两个值, 不考虑数据类型; "===" 相当于比较两个引用(对象)
alert(1=="1");  // 是true
alert(1==="1");  // 是false

单目运算:

名称 运算符 描述
自增 ++ 变量的值每次加1,再赋值给变量
自减 -- 变量的值每次减1,再赋值给变量

双目运算:

名称 运算符
+
-
*
/
取余 %
赋值 =
加等 +=
减等 -=
除等 /=
乘等 *=
取余等 %=

三目运算: ?:

var a = 100;
// a大于100是否成立, 如果成立则打印true, 不成立则打印false
alert(a>100?true:false);

2.6 条件分支结构

if-else 分支:

var a = 1;
var b = 1;
if (a == b) {
    alert("相等");
} else {
    alert("不相等");
}

switch 分支:

var a = 2;
switch (a) {
    case 1:
        alert("值为1");
        break;
    case 2:
        alert("值为2");
        break;
    default:
        alert("找不到");
}

2.7 循环结构

for循环:

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

while循环:

var a = 0;
var i = 0
while (i < 100) {
    a += i;
    i++;
}
alert(a);

do-while循环:

var a = 0;
var i = 0;
do {
    a += i;
    i++;
} while (i < 100);
alert(a);

break与continue关键字:

  • break用于结束循环
  • continue用于结束本次循环

2.8 函数

函数定义:用function关键字来声明,后面是方法名字,参数列表里不写var,整个方法不写返回值类型

/**
 * 方法的参数表不用写参数类型, 方法不用写返回值类型, 如果没有写return就没有返回值
 */
function add(a, b) {
    return a + b;
}

var a = 1;
var c = 2;
var sum = add(a, c);
alert(sum);

若想要函数立马执行,可以在函数后面加一个小括号

2.9 常见弹窗函数

alert弹窗:这是一个只能点击“确定”按钮的弹窗。alert方法没有返回值,也就是说如果用一个变量去接收返回值,将会得到undefined,无论点击“确定”还是右上角的“x”关闭。

alert("nihao!");

confirm弹窗:这是一个用户可以点击“确定”或者“取消”的弹窗。confirm方法有返回值,返回值是boolean类型。当用户点击“确定”时返回true;当用户点击“取消”或者“x”关闭时返回false。

var result = confirm("nihao!");
document.write(result);

prompt弹窗:这是一个用户可以输入文本内容的弹窗。prompt方法有两个参数,第一个参数是提示信息,第二个参数是用户输入的默认值。prompt方法有返回值。当用户点击“确定”时返回用户输入的内容;当用户点击“取消”或者“x”关闭时返回null。

var result = confirm("how are you?", "i am fine!");
document.write(result);

2.10 事件

事件名称 描述
onchange html元素内容改变
onclick 用户点击html元素
onmouseover 用户将鼠标移入一个html元素中
onmousemove 用户在一个html元素上移动鼠标
onmouseout 用户将鼠标从一个html元素上移开
onkeyup 用户按下键盘按键抬起后触发
onkeydown 用户按下键盘按键时触发
onload 浏览器已完成页面的加载
onsubmit 表单提交
<body>
    <script>
        function bt() {
            alert("被点击");
        }
    </script>
    <!-- 给这个button按钮绑定单击事件, 事件被触发时, 回调函数bt() -->
    <button onclick="bt()"></button>
</body>

2.11 正则表达式

  • 正则表达式是描述字符模式的对象
  • 正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。
  • 语法:
    • var patt = new Reg(模式, 修饰符);
    • 或者 var patt = /模式/修饰符;
var reg = /a/;
var str = "hello a";
var result = reg.test(str);  // 判断变量str中是否包含a(默认对大小写敏感)
alert(result);

修饰符:用于执行区分大小写和全局匹配

修饰符 描述
i 执行对大小写不敏感的匹配
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)
m 执行多行匹配
var reg = /a/ig;  // 加入修饰符后对大小写不敏感, 并且全局匹配
var str = "aAAaaiiifff";
var result = str.match(reg);  // 返回结果是所有匹配到的字符
alert(result);  

方括号:用于查找某个范围内的字符

表达式 描述
[abc] 查找方括号之间的任何字符
[^abc] 查找任何不在方括号之间的字符
[0-9] 查找任何从0至9的数字
[a-z] 查找任何从小写a到小写z的字符
[A-Z] 查找任何从大写A到大写Z的字符
[A-z] 查找任何从大写A到小写z的字符(即包含所有大小写字母)
[adgk] 查找给定集合内的任何字符
[^adgk] 查找给定集合外的任何字符
[red|blue|green] 查找任何指定的选项

元字符:是拥有特殊含义的字符

元字符 描述
. 查找单个字符,除了换行和行结束符
\ 查找单词字符
\W 查找非单词字符
\d 查找数字
\D 查找非数字字符
\s 查找空白字符
\S 查找非空白字符
\b 查找单词边界
\B 查找非单词边界
\0 查找NULL字符
\n 查找换行符
\f 查找换页符
\r 查找回车符
\t 查找制表符
\v 查找垂直制表符
\xxx 查找以八进制数xxx规定的字符
\xdd 查找以十六进制dd规定的字符
\uxxxx 查找以十六进制xxxx规定的Unicode字符

量词:用于表示重复次数的含义

量词 描述
n+ 匹配任何包含至少一个n的字符串。例如:/a+/ 匹配"candy"中的"a","caaaaandy"中的所有"a"
n* 匹配任何包含零个或多个n的字符串。例如:/bo*/ 匹配"booooed"中的"boooo","bird"中的"b",但是不匹配"ghost"(因为"ghost"里没有"b")
n? 匹配任何包含零个或一个n的字符串。例如:/e?le?/ 匹配"angel"中的"el","angle"中的"le"
n{X} 匹配包含X个n的序列的字符串。例如:/a{2}/ 不匹配"candy"中的"a",但是匹配"caandy"中的"aa",匹配"caaaaandy"中的前两个"a"
n{X,} 匹配至少包含X个n序列的字符串。例如:/a{2,}/ 不匹配"candy"中的"a",但是匹配"caandy"中的"aa",匹配"caaaaandy"中的所有"a"
n{X,Y} 匹配至少包含X个且至多包含Y个n序列的字符串。例如 /a{1,2}/ 匹配"candy"中的"a",匹配匹配"caandy"中的"aa",匹配"caaaaandy"中的前两个"a"
n$ 匹配任何结尾为n的字符串
^n 匹配任何开头为n的字符串
?=n 匹配任何其后紧跟着指定字符串n的字符串
?!n 匹配任何其后没有紧跟着指定字符串n的字符串

RegExp对象的方法:

方法 描述
compile 编译正则表达式
exec 检索字符串中指定的值。返回找到的值,并确定其位置,如果没找到则返回null
test 检索字符串中指定的值。返回true或false

支持正则表达式的String对象的方法:

方法 描述
search 检索与正则表达式相匹配的值
match 找到一个或多个正则表达式的匹配
replace 替换与正则表达式匹配的子串
split 把字符串分割为字符串数组

三、JavaScript的DOM

3.1 概述

  • 通过HTML DOM,可访问JavaScript HTML文档的所有元素
  • 当网页被加载时,浏览器会创建页面的文档对象模型(Document Objevt Model)
  • HTML DOM 模型被构造为对象的树

通过可编程的对象模型,JavaScript获得了足够的能力来创建动态的HTML

  • JavaScript能够改变页面中的所有HTML元素
  • JavaScript能够改变页面中的所有HTML属性
  • JavaScript能够改变页面中的所有CSS样式
  • JavaScript能够对页面中的所有事件做出反应

3.2 查找HTML元素

通过id找到HTML元素:

  • 在DOM中查找HTML元素最简单的方法,是通过使用元素的id
  • 方法:document.getElementById("id属性的值");
  • 如果找到该元素,则该方法将以对象的形式返回该元素
  • 如果未找到该元素,则返回null

通过标签名找到HTML元素:

  • 方法:document.getElementsByTagName("合法的元素名");
  • 返回的是一个数组

通过类名找到HTML元素:

  • 方法:document.getElementsByClassName("class属性的值");
  • 返回的是一个数组

通过querySelectorquerySelectorAll查找HTML元素:

  • query系列属于W3C中的Selectors API规范、getElement系列属于W3C的DOM规范
  • 方法:document.querySelector("CSS选择器") / document.querySelectorAll("CSS选择器")

query系列和getElement系列的区别:

  • 接收参数不同:query系列接收CSS选择器,如:querySelector(".div1");getElement系列接收属性的值,如getElementById("div1")
  • 返回值不同:query系列返回值是一个NodeList;getElement系列返回值是一个HTMLCollection
    • NodeList对象会包含文档中的所有节点,如Element、Text、Attribute等
    • HTMLCollection对象只会包含文档中的Element节点

3.3 改变HTML

改变HTML输出流

  • 方法:document.write(xxxx);
  • 可用于直接向HTML输出流写内容
<body>
    <script>
        document.write("hello world");  // 会直接覆盖掉原本页面的所有内容, 然后将输出的内容展示到页面
    </script>
</body>

改变HTML内容(即双标签括起来的部分)

  • 通过修改HTML元素对象的innerHTML属性
<body>
    <p id="p1">hello world!</p>
    <script>
        document.getElementById("p1").innerHTML = "ABCD";
    </script>
</body>

改变HTML属性

  • 通过修改HTML元素对象的对应的属性的值即可 对象.属性名 = 新属性值
<body>
    <img id="img1" src="1.jpg">hello world!</div>
    <script>
        document.getElementById("img1").src = "2.jpg";
    </script>
</body>

3.4 CSS变化

修改HTML元素的CSS样式

  • 通过修改 对象.style.css属性 = 新样式值
<body>
    <div id="d1" style="color:red">hello world</div>
    <script>
        var d1 = document.getElementById("d1");
        d1.style.color = "blue";
        // 注意: 原本的css属性名为 font-size, 在HTML元素对象中的属性值为驼峰式的命名规则 fontSize
        d1.style.fontSize = "10px";  
    </script>
</body>

3.5 DOM事件

HTML DOM允许我们通过触发事件来执行代码

<body>
    <h1 id="h1">hello world</h1>
    <!-- 当单击事件触发后, 执行单击事件里的代码 -->
    <button type="button" onclick="document.getElementById('h1').style.color='red'">
        请点击
    </button>
</body>
<body>
    <!-- 在当前HTML元素中, 直接使用this关键字就可以获取到当前HTML元素的对象 -->
    <h1 onclick="this.style.color='red'">请点击文本</h1>
</body>

除了直接执行代码之外,还可以调用函数

<head>
    <script>
        function changeText(data) {
            data.innerHTML = "hello";
        }
        
        function changeText1() {
            var h1 = document.getElementById("h1");
            h1.innerHTML = "hello1";
        }
    </script>
</head>

<body>
    <!-- 单击事件触发后, 调用了上面定义好的changeText方法, 入参为当前HTML元素对象 -->
    <h1 onclick="changeText(this)">请点击文本</h1>
    <!-- 也可以不用入参, 在方法内再获取HTML元素对象也行 -->
    <h1 id="h1" onclick="changeText1()">请点击文本1</h1>
</body>

3.6 EventListener

HTML元素对象的 addEventListener 方法,在用户点击时触发监听事件

  • addEventListener方法用于向指定元素添加事件句柄
  • addEventListener方法添加的事件句柄不会覆盖已存在的事件句柄
  • 可以向一个元素添加多个事件句柄
  • 可以向同一个元素添加多个同类型的事件句柄,比如:两个"click"事件
  • 可以向任何DOM对象添加事件监听,不仅仅是HTML元素对象。比如:window对象
  • addEventListener方法可以更简单的控制事件(冒泡与捕获)
  • 当使用addEventListener方法时,JavaScript从HTML标记中分离开来,可读性更强,在没有控制HTML标记时也可以添加事件监听
  • 可以用removeEventListener方法来移除事件的监听

语法:

element.addEventListener(event, function, useCapture);
参数名 描述
event 事件的类型(比如:"click")
function 事件触发后调用的函数
userCapture 用于描述事件是冒泡(取值false)还是捕获(取值true)(不写默认为false)

示例:

// 当用户点击元素时弹出"hello world"
function fc1() {
    alert("hello world");
}

var element = document.getElementById("d1");
element.addEventListener("click", fc1);
// 可以向同一个元素添加不同类型的事件
element.addEventListener("mouseover", fc1);
element.addEventListener("click", fc2);
element.addEventListener("mouseout", fc3);
// 可以向不同的元素添加同一个事件
element1.addEventListener("click", fc1);
element2.addEventListener("click", fc1);

事件的传递:冒泡与捕获

  • 事件传递定义了元素事件触发的顺序。如果将< div id="d1">插入到< div id="d2">中,用户点击d1元素,哪个元素的"click"事件会先被触发呢?
    • 冒泡中,内部元素的事件会先被触发,然后再触发外部元素的事件
    • 捕获中,外部元素的事件会先被触发,然后再触发内部元素的事件
  • addEventListener方法的第三个参数userCapture可以指定事件的传递类型

removeEventListener方法可以移除由addEventListener方法添加的事件句柄

// 注意: 移除时的事件和方法要和添加时的一致
element.removeEventListener("click", fc1);

3.7 操作元素

如需向HTML DOM添加新元素或者元素节点,必须首先创建该元素对象或者元素节点对象,然后向一个已存在的元素对象中追加该对象

  • 创建元素:document.createElement("xxx"); document.createTextNode("xxx");
  • 追加元素:element.appendChild(xxx);
<div id="d1">
    <p id="p1">段落1</p>
    <p id="p2">段落2</p>
</div>

<script>
    // 创建一个新的HTML元素对象, 参数是HTML标签名
    var para = document.createElement("p");  
    // 创建一个新的元素内容节点对象, 参数是要被标签包裹的内容
    var node = document.createTextNode("新创建的段落3");  
    // 也可以使用para.innerHTML("新创建的段落3"); 来添加内容
    para.appendChild(node);  // 向para元素对象中追加node节点对象
    
    var element = document.getElementById("d1");
    element.appendChild(para);  // 向d1中追加para元素对象
</script>
  • 删除元素:removeChild(xxx)
<div id="d1">
    <p id="p1">段落1</p>
    <p id="p2">段落2</p>
</div>

<script>
    var d1 = document.getElementById("d1");
    // 删除d1元素的第1个子元素
    d1.removeChild(d1.children[0]);  // d1.children是得到d1元素中所有的子元素
</script>

四、浏览器BOM

浏览器对象模型(BOM:Browser Object Model)使得 JavaScript 有能力与浏览器“对话”。

由于现代浏览器几乎已经实现 JavaScript交互性方面的相同方法和属性,因此常被认为是BOM的方法和属性。

4.1 window

  • 所有浏览器都支持window对象。它表示浏览器的窗口
  • 所有JavaScript全局对象、函数以及变量均自动称为window对象的成员
  • 全局变量是window对象的属性
  • 全局函数是window对象的方法
  • 甚至HTML DOM的document也是window对象的属性之一

window的尺寸:

  • 对于Internet Explorer、Chrome、Firefox、Opera以及Safari:

    • window.innerHeight:浏览器窗口内部的高度(包括滚动条)
    • window.innerWidth:浏览器窗口内部的宽度(包括滚动条)
  • 对于 Internert Explorer 5/6/7/8:

    • document.documentElement.clientHeight:浏览器窗口内部的高度(包括滚动条)

    • document.documentElement.clientWidth:浏览器窗口内部的宽度(包括滚动条)

      或者

    • document.body.clientHeight

    • document.body.clientHeight

Window Screen:

  • 可用高度:screen.availHeight 属性返回访问者屏幕的高度,以像素记,减去界面特性,比如窗口任务栏
  • 可用宽度:screen.availWidth 属性返回访问者屏幕的宽度,以像素记,减去界面特性,比如窗口任务栏

Window Location:

  • window.location 对象用于获得当前页面的地址(URL),并把浏览器重定向到新的页面
  • window.location 对象在编写时可不使用window这个前缀。location对象的一些属性和方法如下:
    • location.hostname:返回web主机的域名
    • location.pathname:返回当前页面的路径和文件名
    • location.port:返回web主机的端口(80或443)
    • location.protocol:返回所使用的web协议(http://或https://)
    • location.href:返回当前页面的URL
    • location.assign():方法加载新的文档
<head>
    <script>
        // 加载新文档
        function newDoc() {
            window.location.assign("http://www.baidu.com");
        }
    </script>
</head>

<body>
    <buttom onclick="newDoc()">Load new document</buttom>
</body>

Window History:

  • window.history 对象包含浏览器的历史
  • window.history 对象在编写时可不使用window这个前缀。history对象的方法:
    • history.back():方法与在浏览器点击后退按钮相当
    • history.forward():方法与在浏览器点击前进按钮相当
<head>
    <script>
        function goBack() {
            window.history.back();
        }
        
        function goForward() {
            window.history.forward();
        }
    </script>
</head>

<body>
    <buttom onclick="goBack()">goBack</buttom>
    <buttom onclick="goForward()">goForward</buttom>
</body>

Window Navigator:

  • window.navigator 对象包含浏览器的各种信息
  • window.navigator 对象在编写时可不使用window这个前缀。navigator对象的一些属性:
    • navigator.appCodeName:返回浏览器代号
    • navigator.appName:返回浏览器名称
    • navigator.appVersion:返回浏览器版本
    • navigator.cookieEnabled:返回浏览器是否启用Cookies
    • navigator.platform:返回浏览器硬件平台
    • navigator.userAgent:返回浏览器当前用户代理
    • navigator.systemLanguage:返回浏览器用户代理语言

4.2 JavaScript定时器

定义定时器:

  • setInterval("调用函数", 毫秒时间); 每间隔固定毫秒值就执行一次调用函数(每到时间就执行一次,无限重复
  • setTimeout("调用函数", 毫秒时间); 在固定时间之后执行一次调用函数(执行一次之后就不再执行了)

关闭定时器:按照定义时的定时器类型选择关闭定时器的方法

  • clearInterval(定时器名称)
  • clearTimeout(定时器名称)

定义定时器和关闭定时器都是window对象的方法,在使用时前缀window可以省略

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

推荐阅读更多精彩内容