温故而知新——javascript篇

js是前端工程师的核心,只有掌握了它才算进入真真的编程,代码虐我千百遍,我视代码如初恋。。。。

什么是javascript?

  • js是一种基于对象和事件驱动的客户端脚本语言,最初的设计是及检验html表单输入的正确性

javascript 的组成

  • ECMAscript(语法)
  • Browser Objects(DOM、BOM)


    image.png

如何使用

  • 可在head或body中使用<script>嵌入js脚本

js注释

  • 单行注释 //
  • 多行注释 /**/

js标识符

  • 变量、函数、属性的名称、函数参数
  • 命名规则

1、由字母、数字、下划线(_)或美元符号($)组成;
2、不能以数字开头;
3、不能使用关键字、保留字作为标识符;

变量

  • ECMAScript的变量是松散类型,可以用来保存任何数据类型
  • 变量声明使用var操作符;
  • 变量赋值:var 变量名 = 值

数据类型

  • 基本数据类型:Undefined、Null、Boolean、Number和String。
  • 复杂数据类型:Object

undefined

  • undefined类型只有一个值,即特殊的undefined。

null

  • null值表示一个空对象指针
  • 如果定义的变量准备在将来用于保存对象,那么最好将改始化为null而不是其他值。
  • 说明:undefined值是派生自null值的,所以undefined==n 回结果是true。

Number

  • Number:表示整数和浮点数
  • NaN:即非数值(Not a Number)是一个特殊的数值

1、任何涉及NaN的操作(例如NaN/10)都会返回
NaN。
2、NaN与任何值都不相等,包括NaN本身。

  • isNaN()

语法:isNaN(n)
功能:检测n是否是“非数值” 返回值:boolean
参数:参数n可以是任何类型
说明:isNaN()在接收到一个值之后,会尝试将这个值转换为数值。
某些不是数值的值会直接转换为数值。

  • 数值转换

有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。其中Number()可以用于任何数据类型,而parseInt()和parseFloat()则专门用于把字符串转换成数值。
一、parseInt() :会忽略字符串前面的空格,直至找到第一个非空格字符。
说明:
1、parseInt():转换空字符串返回NaN。
2、parseInt()这个函数提供第二个参数:转换时使用的基数(即多少进制)
二、parseFloat:从第一个字符开始解析每个字符,直至遇见一个无效的浮点数字符为止
说明:
除了第一个小数点有效外,parseFloat()与parseInt()的第二个区别在于它始终都会忽略前导的零。

String

String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号(”)或单引号(’)表示。

  • toString()与String()

语法:str.toString()
功能:将str转换为字符串
返回值:str的一个副本
参数:str是要转换的内容,可以是数值、布尔值、对象和字符串。
说明: 在不知道要转换的值是不是null或undefined的情况下,还可以使用String()函数,它能够将任何类型的值转换为字符串。

Boolean

  • 用于表示真假的类型,即true表示真,false表示假
  • 类型转换

1、除0之外的所有数字,转换为布尔型都为true
2、除””之外的所有字符,转换为布尔型都为true
3、null和undefined转换为布尔型为false

typeof

  • 语法:typeof 变量 或 typeof(变量)
  • 功能:检测变量类型
  • 返回值:string类型,有可能是:string、number、boolean、object、undefined、function

JavaScript操作符

操作符的分类

  • 算数操作符 +,- ,* ,/, %(取余)

递增和递减:
++a与a++都是对a进行递增的操作
区别:
++a先返回递增之后的a的值
a++先返回a的原值,再返回递增之后的值
2、递减同理

赋值操作符

  • 简单赋值:=
  • 复合赋值:+=、-=、*=、/=、%=

比较操作符

  • <、>=、<=、==、===、!=、!==、>
  • ==:相等,只比较值是否相等
  • ===:相等,比较值的同时比较数据类型是否相等
  • !=:不相等,比较值是否不相等
  • !==:不相等,比较值的同时比较数据类型是否不相等
  • 返回值:boolean型

三元操作符

  • 条件 ? 执行代码1:执行代码2

可代替简单的if语句,如果条件成立,执行代码1,否则执行代码2

逻辑操作符

  • 逻辑与:&&

&& (只要有一个条件不成立,返回false)
说明:在有一个操作数不是布尔值的情况,逻辑与操作就不一定返回值,
此时它遵循下列规则:
1、如果第一个操作数隐式类型转换后为true,则返回第二个操作数
2、如果第一个操作数隐式类型转换后为false,则返回第一个操作数
3、如果有一个操作数是null,则返回null
4、如果有一个操作数是NaN,则返回NaN
5、如果有一个操作数是undefined,则返回undefined

  • 逻辑或:||

|| (只要有一个条件成立,返回true)
说明:在有一个操作数不是布尔值的情况,逻辑与操作就不一定返回值,此时它遵循下列规则:
1、如果第一个操作数隐式类型转换后为true,则返回第一个操作数
2、如果第一个操作数隐式类型转换后为false,则返回第二个操作数
3、如果两个操作数是null,则返回null
4、如果两个操作数是NaN,则返回NaN
5、如果两个操作数是undefined,则返回undefined

  • 逻辑非:!

说明:
1、无论操作数是什么数据类型,逻辑非都会返回一个布尔值
2、 ! ! 同时使用两个逻辑非操作符时:
第一个逻辑非操作会基于无论什么操作数返回一个布尔值, 第二个逻辑非则对该布尔值求反。

JavaScript语句

  • if(条件){ }else{ }
  • if(条件){ }else if(条件){ } else{ }
  • switch语句:
switch( expression ){
       case value:statement
       break;
       case value:statement
       break;
       ......
      default:statement
}
  • for语句

语法:for(语句1;语句2;语句3){被执行的代码块;}
语句1 :在循环(代码块)开始前执行
语句2:定义运行循环(代码块)的条件
语句3 :在循环(代码块)已被执行之后执行
嵌套说明:当循环与循环发生嵌套时遵循下列规则:
1、外层为假时内层不执行;
2、先执行外层再执行内层,直至内层的条件为假时再返回外层去执行。

  • break语句:立即退出循环
  • continue 语句: 结束本次循环,继续开始下一次。

JavaScript中的函数

函数的作用

  • 通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。

函数的返回值

  • 任何函数在任何时候都可以通过return语句后跟要返回的值来实现返回值。

1、函数会在执行完return语句之后停止并立即退出。
2、return语句也可以不带有任何返回值,一般用于需要提前停止函数执行而又不需要返回值的情况下

函数参数

  • arguments

1、arguments对象只是与数组类似,并不是Array的实例。
2、[] 语法访问它的每一个元素。
3、length属性确定传递参数的个数

JavaScript中的内置对象

数组Array

  • 如何创建数组

创建数组的基本方式有两种:
1、使用Array构造函数,语法为:new Array()说明:
(1)如果预先知道数组要保存的项目数量。
(2)也可以向Array构造函数传递数组总应该包含的项目。
2、使用数组字面量表示法由一对包含数组项的方括号表示,多个数组项之间以逗号隔开。

  • 数组元素的读写

读取和设置值时,使用方括号并提供相应的索引说明:索引是从0开始的正整数

  • 数组长度

语法:array.length
功能:获取数组array的长度
返回值:number
说明:
1、通过设置length可以从数组的末尾移除项或向数组中添加新项。
2、把一个值放在超出当前数组大小的位置上时,会重新计算数组长度值,长度值等于最后一项索引加1。

  • 数组的栈方法
    1、push()

语法:
arrayObject.push(newele1,newele2,....,neweX)
功能:把它的参数顺序添加到arrayObject 的尾部。
返回值:把指定的值添加到数组后的新长度。

2、unshift()

语法:arrayObject.unshift(newele1,newele2,....,neweX)
功能:把它的参数顺序添加到arrayObject 的开头。
返回值:把指定的值添加到数组后的新长度

3、pop()

语法:arrayObject.pop()
功能:删除arrayObject 的最后一个元素。
返回值:被删除的那个元素。

4、shift()

语法:arrayObject.shift()
功能:删除arrayObject中的第一个元素
返回值:被删除的那个元素

  • 数组的转换方法
    join()

语法:arrayObject.join(separator)
功能:用于把数组中的所有元素放入一个字符串。
返回值:字符串

  • 数组的重排序方法
    1、reverse()

语法:stringObject.reverse()
功能:用于颠倒数组中元素的顺序。
返回值:数组

2、sort()

语法:arrayObject.sort(sortby)
功能:用于对数组的元素进行排序。
返回值:数组。
说明:
1、即使数组中的每一项都是数值,sort()方法比较的也是字符串。
2、sort()方法可以接收一个比较函数作为参数

  • 数组的操作方法
    1、concat()

语法:arrayObject.concat(arrayX,arrayX,......,arrayX)
功能:用于连接两个或多个数组。
返回值:数组。

2、slice()

语法:arrayObject.slice(start,end)
功能:从已有的数组中返回选定的元素。
参数:start (必需)规定从何处开始选取,如果是负数,那么它规定从数组尾部开始算起的位置。
end(可选)规定从何处结束选取,该参数是数组片断结束处的数组下标。
说明:
1、如果没有指定end,那么切分的数组包含从start 到数组结束的所有元素。
2、如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。
返回值:数组

3、splice()方法删除数组项

语法:arrayObject.splice(index,count)
功能:删除从index 处开始的零个或多个元素。
返回值:含有被删除的元素的数组。
说明:count是要删除的项目数量,如果设置为0,则不会删除项目。如果不设置,则删除从index开始的所有值

splice()方法插入数组项

语法:arrayObject.splice(index,0,item1,.....,itemX)
功能:在指定位置插入值
参数:
Index:起始位置
0:要删除的项数
item1...itemX:要插入的项
返回值:数组

splice()方法替换数组项

语法:arrayObject.splice(index,count,item1,.....,itemX)
功能:在指定位置插入值,且同时删除任意数量的项
参数:
Index:起始位置
count:要删除的项数
item1...itemX:要插入的项
返回值:从原始数组中删除的项(如果没有删除任何项,则返回空数组)

  • 数组添加的两个位置方法
    1、 indexOf()

语法:arrayObject.indexOf(searchvalue,startIndex)
功能:从数组的开头(位置0)开始向后查找。
参数:
searchvalue:必需,要查找的项;
startIndex:可选,起点位置的索引。
返回值:number,查找的项在数组中的位置,没有找到的情况下返回-1
2、 lastIndexOf()
语法:arrayObject.lastIndexOf(searchvalue,startIndex)
功能:从数组的末尾开始向前查找。
参数:
searchvalue:必需,要查找的项;
startIndex:可选,起点位置的索引。
返回值:number,查找的项在数组中的位置,没有找到的情况下返回-1。

字符串String

  • 字符串对象的方法
    1、charAt()

语法:stringObject.charAt(index)
功能:返回stringObject中index位置的字符

2、charCodeAt()

语法:stringObject.charCodeAt(index)
功能:返回stringObject中index位置字符的字符编码

3、indexOf()

语法:stringObject.indexOf("o")
功能:从一个字符串中搜索给定的子字符串,返回子字符串的位置。
返回值:数值说明:如果没有找到该子字符串,则返回-1。

4、lastIndexOf()

语法:stringObject.lastIndexOf("o")
功能:从一个字符串中搜索给定的子字符串,返回子字符串的位置
返回值:数值
说明:如果没有找到该子字符串,则返回-1

5、split()

语法:stringObject.split(separator)
功能:把一个字符串分割成字符串数组。
返回值:Array。
说明:separator:必需,分隔符

6、replace()

语法:stringObject.replace(regexp/substr,replacement)
功能:在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
返回值:String
参数:
regexp:必需。规定子字符串或要替换的模式的RegExp对象。
replacement::必需。一个字符串值

  • 字符串对象的截取方法
    1、slice()

语法:stringValue.slice(start,end)
功能:截取子字符串。
参数说明:
1、start:必需,指定子字符串的开始位置。
2、end:可选,表示子字符串到哪里结束,end本身不在截取范围之内,省略时截取至字符串的末尾。
3、当参数为负数时,会将传入的负值与字符串的长度相加。

2、substring()

说明:语法及功能同slice()完全一样。
区别在于:
1、当参数为负数时,自动将参数转换为0。
2、substring()会将较小的数作为开始位置,将较大的数作为结束位置。

3、substr()

语法:stringValue.substr(start,len)
功能:截取子字符串。
参数说明:
1、start:必需,指定子字符串的开始位置。
2、len:可选,表示截取的字符总数,省略时截取至字符串的末尾。
3、当start为负数时,会将传入的负值与字符串的长度相加。
4、当len为负数时,返回空字符串

  • 字符串转换方法
    1、toUpperCase()

语法:stringValue.toUpperCase()
功能:把字符串转换为大写。

2、toLowerCase()

语法:stringValue.toLowerCase()
功能:把字符串转换为小写

算数Math

  • Math.min()

语法:Math.min(num1,num2...numN)
功能:求一组数中的最小值。
返回值:Number

  • Math.max()

语法:Math.max(num1,num2...numN)
功能:求一组数中的最大值。
返回值:Number。

  • Math.ceil()

语法:Math.ceil(num)
功能:向上取整,即返回大于num的最小整数。
返回值:Number。

  • Math.floor()

语法:Math.floor(num)
功能:向下取整,返回num的整数部分。
返回值:Number。

  • Math.round()

法:Math.round(num)
功能:将数值四舍五入为最接近的整数。
返回值:Number。

  • Math.abs()

语法:Math.abs (num)
功能:返回num的绝对值。
返回值:Number。

  • Math.random()

语法:Math.random()
功能:返回大于等于0小于1的一个随机数。
返回值:Number。
说明:
求n到m之间的随机整数的公式:random=Math.floor(Math.random()*(m-n+1)+n)

日期Date

  • 如何创建一个日期对象

语法:new Date();
功能:创建一个日期时间对象
返回值:不传参的情况下,返回当前的日期时间对象。
说明:
如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数或者是一组用逗号隔开的表示年月日时分秒的参数

  • 获取年月日时分秒及星期的方法

1、getFullYear():返回4位数的年份
2、getMonth():返回日期中的月份,返回值为0-11
3、getDate():返回月份中的天数
4、getDay():返回星期,返回值为0-6
5、getHours():返回小时
6、getMinutes():返回分
7、getSeconds():返回秒
8、getTime():返回表示日期的毫秒数

  • 设置年月日时分秒及星期的方法

1、setFullYear(year):设置4位数的年份
2、setMonth(mon):设置日期中的月份,从0开始,0表示1月
3、setDate():设置日期
4、setDay():设置星期,从0开始,0表示星期日
5、setHours():设置小时
6、setMinutes():设置分
7、setSeconds():设置秒
8、setTime():以毫秒数设置日期,会改变整个日期

JavaScript 事件基础

  • 事件

事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。

  • DOM0级事件

语法:ele.事件=执行脚本
功能:在DOM对象上绑定事件
说明:执行脚本可以是一个匿名函数,也可以是一个函数的调用。

  • 鼠标事件

onload:页面加载时触发
onclick:鼠标点击时触发
onmouseover:鼠标滑过时触发
onmouseout:鼠标离开时触发
onfoucs:获得焦点时触发
onblur:失去焦点时触发
onchange:域的内容改变时发生
onsubmit:表单中的确认按钮被点击时发生
onmousedown:鼠标按钮在元素上按下时触发
onmousemove:在鼠标指针移动时发生
onmouseup:在元素上松开鼠标按钮时触发
onresize:当调整浏览器窗口的大小时触发
onscroll:拖动滚动条滚动时触发

  • 键盘事件与keyCode属性

onkeydown:在用户按下一个键盘按键时发生
onkeypress:在键盘按键被按下并释放一个键时发生
onkeyup:在键盘按键被松开时发生
keyCode:返回onkeypress、onkeydown或onkeyup事件触发的键的值的字符代码,或者的键的代码

JavaScript BOM

  • BOM

BOM(browser object model)浏览器对象模型

  • window

window是浏览器的一个实例,在浏览器中,window对象有双重角色,它既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象。

  • Window对象的方法
    1、window.alert

语法:window.alert(”content”)
功能:显示带有一段消息和一个确认按钮的警告框

2、window.confirm

语法:window.confirm(“message")
功能:显示一个带有指定消息和OK及取消按钮的对话框
返回值:如果用户点击确定按钮,则confirm()返回true,如果用户点击取消按钮,则confirm()返回false

3、window.prompt

语法:window.prompt(“text,defaultText")
参数说明:
text:要在对话框中显示的纯文本(而不是HTML格式的文本)
defaultText:默认的输入文本
返回值:如果用户单击提示框的取消按钮,则返回null如果用户单击确认按钮,则返回输入字段当前显示的文本

4、window.open

语法:window.open(pageURL,name,parameters)
功能:打开一个新的浏览器窗口或查找一个已命名的窗口
参数说明:
pageURL:子窗口路径
name:子窗口句柄。
parameters :窗口参数(各参数用逗号分隔)

5、window.close

语法:window.close( )
功能:关闭浏览器窗口

  • 超时调用

语法:setTimeout(code,millisec)
功能:在指定的毫秒数后调用函数或计算表达式
参数说明:
1、code:要调用的函数或要执行的JavaScript代码串
2、millisec:在执行代码前需等待的毫秒数
说明:
setTimeout()只执行code一次。如果要多次调用,请使用setInterval()或者让code自身再次调用setTimeout()

  • 清除超时调用

语法:clearTimeout(id_of_settimeout)
功能:取消由setTimeout()方法设置的timeout
参数说明:
1、id_of_settimeout:由setTimeout()返回的ID值,该值标识要取消的延迟执行代码块

  • 间歇调用

语法:setInterval(code,millisec)
功能:每隔指定的时间执行一次代码
参数说明:
1、code:要调用的函数或要执行的代码串。
2、millisec:周期性执行或调用code之间的时间间隔,以毫秒计

location对象

location对象提供了与当前窗口中加载的文档有关的信息,还提供了一些导航的功能,它既是window对象的属性,也是document对象的属性

  • location对象的常用属性
    1、location.href

功能:返回当前加载页面的完整URL
说明:location.href与window.location.href等价

2、location.hash

功能:返回URL中的hash(#号后跟零或多个字符),如果不包含则返回空字符串。

3、location.host

功能:返回服务器名称和端口号(如果有)

4、location.hostname

功能:返回不带端口号的服务器名称。

5、location.pathname

返回URL中的目录和(或)文件名。

6、location.port

功能:返回URL中指定的端口号,如果没有,返回空字符串。

7、location.protocol

功能:返回页面使用的协议。

8、location.search

功能:返回URL的查询字符串。这个字符串以问号开头。

9、location.replace()

语法:location.replace(url)
功能:重新定向URL。
说明:
使用location.replace不会在历史记录中生成新纪录。

10、location.reload()

功能:重新加载当前显示的页面。
说明:
location.reload()有可能从缓存中加载
location.reload(true)从服务器重新加载

  • 改变浏览器位置的方法:

location.href属性
location对象其他属性也可改变URL:
location.hash
location.search

history历史对象

  • history.back()

功能:回到历史记录的上一步
说明:相当于使用了history.go(-1)

  • location.forward()

功能:回到历史记录的下一步
说明:相当于使用了history.go(1)

  • history.go(-n)

功能:回到历史记录的前n步

  • history.go(n)

功能:回到历史记录的后n步

Screen对象属性

  • screen.availWidth:返回可用的屏幕宽度
  • screen.availHeight :返回可用的屏幕高度

Navigator对象

  • UserAgent

用来识别浏览器名称、版本、引擎以及操作系统等信息的内容

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

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,151评论 0 4
  • 第1章 认识JS JavaScript能做什么?1.增强页面动态效果(如:下拉菜单、图片轮播、信息滚动等)2.实现...
    mo默22阅读 1,171评论 0 5
  • 这两天呆在家里,多亏有它做伴儿, 稍有空气异味儿,立马发红色警报, 一会儿又变黄色,最后恢复到绿色, 清除室内异味...
    梦想917阅读 224评论 0 2
  • 官网解释如下: This issue has been specifically observed in Wind...
    Joezer阅读 4,017评论 0 3
  • 67天 小雨 还在天猫,但是不放心,还不如直接到官网去。原来是x要到11月,但是我就得等到那会耶,那就刚刚好...
    红玫瑰4阅读 205评论 0 0