HTML&CSS+JS综合复习1

HTML&CSS:

一.浏览器内核:

1.渲染引擎:渲染引擎负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。
2.js引擎:JS 引擎则是解析 Javascript 语言,执行 javascript 语言来实现网页的动态效果。

二.盒模型(标准盒模型和怪异盒模型的区别):

1.标准盒模型:标准盒模型中width指的是内容区域content的宽度;height指的是内容区域content的高度。标准盒模型下盒子的大小 = content + border + padding + margin
2.怪异盒模型:怪异盒模型中的width指的是内容、边框、内边距总的宽度(content + border + padding);height指的是内容、边框、内边距总的高度。怪异盒模型下盒子的大小=width(content + border + padding) + margin
最后可以通过box-sizing:border-box

三.flex布局:

display:flex(常用)
flex-direction:决定项目的排列方向。(常用)
flex-wrap:即一条轴线排不下时如何换行。(常用)
flex-flow:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
justify-content:定义了项目在主轴上的对齐方式。(justify)(常用)
align-items:定义项目在交叉轴上如何对齐。(常用)
align-content:定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。(换行会产生多轴)

四.两/三栏布局、水平/垂直居中:

1.两栏布局:浮动 、定位、 flex
2.三栏布局:绝对定位、 浮动+负外边距(双飞翼布局)、浮动定位法、flex、圣杯布局(设置左右边距加+positive定位)
3.水平/垂直居中:text-align:center 、margin:0 auto; 、使用定位属性、flexbox布局(display: flex; justify-content: center;)、多行的行内元素(使用给父元素设置display:table-cell;和vertical-align: middle;属即可;)

五.什么是BFC:

BFC是一块独立的布局环境,保护其中内部元素不收外部影响,也不影响外部。本身BFC是一种css的布局方式,只是我们可以利用它来解决外边距折叠的问题,BFC并不是专门用来解决这个问题而创的;

五.清除浮动:

1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
2.父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
3.使用after伪元素清除浮动(推荐使用)
.clearfix:after{/伪元素是行内元素 正常浏览器清除浮动方法/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
zoom: 1;/ie6清除浮动的方式 号只有IE6-IE7执行,其他浏览器不执行/
}
<body>
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>

</div>
<div class="footer"></div>
4.使用before和after双伪元素清除浮动
.clearfix:after,.clearfix:before{
content: "";
display: table;
}
.clearfix:after{
clear: both;
}
.clearfix{
*zoom: 1;
}
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
<div class="footer"></div>

六.css3动画:

第一种是:transition实现渐变动画,第二种是:transform转变动画,第三种是:animation实现自定义动画,具体使用步骤看文档。

七.H5新特性:

语义标签、增强型表单、视频和音频、Canvas绘图、SVG绘图、地理定位、拖放API、WebWorker、WebStorage、WebSocket,具体用法看文档。

八.行内元素有哪些?块级元素有哪些?空元素有哪些?

首先:css规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,如div 的display默认值为“block” ,则为“块级”元素;span 默认display 属性值为“inline”,是“行内”元素。
常用的块状元素有:div p h1..h6 ol ul dl table address blockquote form
常见的内联元素有:a span br i em strong label q var cite code
常用的内联块状元素有:img input
知名的空元素:br hr img input link meta br

九浏览器是怎么对 HTML5 的离线储存资源进行管理和加载的呢?

在线的情况下,浏览器发现 html 头部有 manifest 属性,它会请求 manifest 文件,如果是第
一次访问 app,那么浏览器就会根据 manifest 文件的内容下载相应的资源并且进行离线存储。
如果已经访问过 app 并且资源已经离线存储了,那么浏览器就会使用离线的资源加载页面,
然后浏览器会对比新的 manifest 文件与旧的 manifest 文件,如果文件没有发生改变,就不做
任何操作,如果文件改变了,那么就会重新下载文件中的资源并进行离线存储。
离线的情况下,浏览器就直接使用离线存储的资源

十.页面渲染时,dom 元素所采用的 布局模型,可通过box-sizing 进行设置。根据计算宽高的区域可分为:

content-box (W3C 标准 盒模型 )
border-box (IE 盒模 型)
padding-box (FireFox 曾经 支持 )
margin-box (浏览器未实现)
Tips: 理论上是有上面 4 种盒子,但现在 w3c 与 mdn 规范中均只支持 content-box 与border-box

十一.ie 盒模型算上 border、padding 及自身(不算 margin),标准的只算上自身窗体的大小 css

标准模型 :box-sizing:content-box;
IE 模型:box-sizing:border-box;

十二.几种获得宽高的方式 :

1.dom.style.width/height
这种方式只能取到 dom 元素内联样式所设置的宽高,也就是说如果该节点的样式是在 style
标签中或外联的 CSS 文件中设置的话,通过这种方法是获取不到 dom 的宽高的。21
2.dom.currentStyle.width/height
这种方式获取的是在页面渲染完成后的结果,就是说不管是哪种方式设置的样式,都能获取
到。但这种方式只有 IE 浏览器支持。
3.window.getComputedStyle(dom).width/height
这种方式的原理和 2 是一样的,这个可以兼容更多的浏览器,通用性好一些。
4.dom.getBoundingClientRect().width/height
这种方式是根据元素在视窗中的绝对位置来获取宽高的
5.dom.offsetWidth/offsetHeight
这个就没什么好说的了,最常用的,也是兼容最好的。

十三.居中方法:

答: 1. 水平方向上

//针对 inline, 内联块 inline-block, 内联表 inline-table, inline-flex 元素及 img,span,button 等元素
.text_div{
    text-align:center;
 }
//不定宽块状元素居中
.text_div{
   margin:0 auto;//且需要设置父级宽度
}
//通过给父元素设置 float,然后给父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left: -50% 来实现水平居中。
.wrap{
    float:left; 
    position:relative; 
    left:50%;
    clear:both; 
  }
.wrap-center{ 
  left:-50%;
   }
  1. 垂直居中
//单行内联(inline-)元素垂直居中
//通过设置内联元素的高度(height)和行高(line-height)相等,从而使元素垂直居中。
.text_div{
    height: 120px;
    line-height: 120px; 
   }
//利用表布局
.father { display: table; }
.children {
    display: table-cell;
     vertical-align: middle;
     text-align: center;
}
//flex 布局
.center-flex { 
   display: flex;
   flex-direction: column;//上下排列
   justify-content: center;
 }
//绝对布局方式
//已知高度
.parent {
 position: relative;
 }
.child {
  position: absolute;
  top: 50%; 
  height: 100px;
  margin-top: -50px; 
 }
//未知高度
.parent { position: relative; }
.child {
   position: absolute;
   top: 50%;
   transform: translateY(-50%);
 }
  1. 垂直水平居中根据上方结合
///flex 方 式
 .parent {
    display: flex;
    justify-content: center; 
    align-items: center; 
 }
 //grid 方 式
 .parent{
    height: 140px;
    display: grid;
 }
.child { margin: auto};

十四.css 优先确定级:

每个选择器都有权值,权值越大越优先
继承的样式优先级低于自身指定样式
!important 优先级最高 js 也无法修改
权值相同时,靠近元素的样式优先级高 顺序为内联样式表(标签内部)> 内部样式表(当
前文件中)> 外部样式表(外部文件中)

十五.如何清除浮动:

不清楚浮动会发生高度塌陷:浮动元素父元素高度自适应(父元素不写高度时,子元素写了
浮动后,父元素会发生高度塌陷)clear 清除浮动(添加空 div 法)在浮动元素下方添加空 div,并给该元素写 css 样式:

              {clear:both;height:0;
               overflow:hidden;}

给浮动元素父级设置高度,父级同时浮动(需要给父级同级元素添加浮动),父级设置成 inline-block,其 margin: 0 auto 居中方式失效给父级添加 overflow:hidden 清除浮动方法
万能清除法 after 伪类 清浮动(现在主流方法,推荐使用)

float_div:after{ content:"."; clear:both; display:block; height:0; overflow:hidden;
visibility:hidden; }.float_div{ zoom:1
}
.clearfix:after {
/生成内容作为最后一个元素/
content: "";
/使生成的元素以块级元素显示,占满剩余空间/
display: block;
/避免生成内容破坏原有布局的高度/
height: 0;
/使生成的内容不可见,并允许可能被生成内容盖住的内容可以进行点击和交互/
visibility: hidden;
/清除浮动元素对当前元素的影响/24
clear: both; }
.clearfix {
/用于兼容 IE, 触发 IE hasLayout/ *zoom:1;}

十六.display 相关:

block:div 等容器类型 inline:img span 等行内类型
table 系列:将样式变成 table 类型 flex:重点把握,非常强大
grid:同上
inline-block:可设置宽度,两者间有一点间隙 inherit:继承父级

十七.介绍一下 CSS 的盒子模型:

有两种, IE 盒子模型、标准 W3C 盒子模型;
IE 的 content 部分包含了 border 和 pading;
盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border)

十八.优先级算法如何计算?

优先级就近原则,同权重情况下样式定义最近者为准
载入样式以最后载入的定位为准;优先级为:
!important > id > class > tag
important 比 内联优先级高,但内联比 id 要高

十九.列出 display 的值,说明他们的作用?

block 象块类型元素一样显示。
inline 缺省值。象行内元素类型一样显示。
inline-block 象行内元素一样显示,但其内容象块类型元素一样显示。list-item 象块类型元素
一样显示,并添加样式列表标记

二十.position 的值,relative 和 absolute 分别是相对于谁进行定位的?

absolute生成绝对定位的元素,相对于 static 定位以外的第一个祖先元素进行定位。
fixed (老 IE 不支持)生成绝对定位的元素,相对于浏览器窗口进行定位。
relative 生成相对定位的元素,相对于其在普通流中的位置进行定位。
static 默认值。没有定位,元素出现在正常的流中*(忽略 top, bottom, left, right z-index 声
明)。inherit 规定从父元素继承 position 属性的值

二十一.Less/Sass/Scss 的区别?

Scss 其实是 Sass 的改进版本 Scss 是 Sass 的缩排语法,对于写惯 css 前端的 web 开发者来说
很不直观,也不能将 css 代码加入到 Sass 里面,因此 Sass 语法进行了改良,Sass 3 就变成了
Scss(sassy css)。与原来的语法兼容,只是用{}取代了原来的缩进。
Less 环境较 Sass 简单 Sass 的安装需要安装 Ruby 环境,Less 基于 JavaScript,需要引入 Less.js
来处理代码输出 css 变量符不一样,Less 是@,而 Sass 是$,而且变量的作用域也不一样。
Sass 没有局部变量,满足就近原则。Less 中{}内定义的变量为局部变量。
Less 没有输出设置,Sass 提供 4 中输出选项:
输出样式的风格可以有四种选择,默认为 nested nested:嵌套缩进的 css 代码 expanded:
展开的多行 css 代码 compact:简洁格式的 css 代码 compressed:压缩后的 css 代码
Sass 支持条件语句,可以使用 if{}else{},for{}循环等等。而 Less 不支持。
Less 与 Sass 处理机制不一样 Less 是通过客户端处理的,Sass 是通过服务端处理,相比较之
下 Less 解析会比 Sass 慢一点30
Sass 和 Less 的工具库不同 Sass 有工具库 Compass, 简单说,Sass 和 Compass 的关系有点像
Javascript 和 jQuery 的关系,Compass 是 Sass 的工具库。在 它的基础上,封装了一系列有用
的模块和模板,补充强化了 Sass 的功
能。
Less 有 UI 组件库 Bootstrap, Bootstrap 是 web 前端开发中一个比较有名的前端 UI 组件库,
Bootstrap 的样式文件部分源码就是采用 Less 语法编写,不过 Bootstrap4 也开始用 Sass 编写
了。

二十二. css 与 js 动画差异?

css 性能好
css 代码逻辑相对简单 js 动画控制好
js 兼容性好
js 可实现的动画多 js 可以添加事件

二十三 .如何居中 div?如何居中一个浮动元素?如何让绝对定位的 div 居中?

div:
border: 1px solid red;34
margin: 0 auto;
height: 50px;
width: 80px;
浮动元素的上下左右居中:
border: 1px solid red;
float: left;
position: absolute;
width: 200px;
height: 100px;
left: 50%;
top: 50%;
margin: -50px 0 0 -100px;
绝对定位的左右居中:
border: 1px solid black;
position: absolute;
width: 200px;
height: 100px;
margin: 0 auto;
left: 0;
right: 0

二十四.请解释一下 CSS3 的 flexbox(弹性盒布局模型),以及适用场景?

该布局模型的目的是提供一种更加高效的方式来对容器中的条目进行布局、对齐和分配空
间。在传统的布局方式中,block 布局是把块在垂直方向从上到下依次排列的;而 inline 布
局则是在水平方向来排列。弹性盒布局并没有这样内在的方向限制,可以由开发人员自由操
作。 使用场景:弹性布局适合于移动前端开发,在 Android 和 ios 上也完美支持。

JavaScript:

一.介绍一下js的数据类型有哪些,值是如何存储的:

一共8种数据类型 分为基本数据类型和引用数据类型
基本数据类型:undefined 、Null 、boolean、 Number、 String、 Symbol、 Biglnt
引用数据类型:Object 里面包含 function、Array、Date等
1.原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
2.引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

二. && 、 ||和!! 运算符分别能做什么:

• && 叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
• || 叫逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。
• !! 运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。

三. js的数据类型的转换:

• 转换为布尔值(调用Boolean()方法)
• 转换为数字(调用Number()、parseInt()和parseFloat()方法)
• 转换为字符串(调用.toString()或者String()方法)
• null和underfined没有.toString方法

四. js的数据类型的判断:

1.typeof 对于原始类型来说,除了 null 都可以显示正确的类型。
2.instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。可以看出直接的字面量值判断数据类型,instanceof可以精准判断引用数据类型(Array,Function,Object),而基本数据类型不能被instanceof精准判断。
3.constructor这里有一个坑,如果我创建一个对象,更改它的原型,constructor就会变得不可靠了。
4.Object.prototype.toString.call() 使用 Object 对象的原型方法 toString ,使用 call 进行狸猫换太子,借用Object的 toString 方法。

五. js的内置对象有那些:

(1)值属性,这些全局属性返回一个简单值,这些值没有自己的属性和方法。例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。例如 eval()、parseFloat()、parseInt() 等
(3)基本对象,基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来表示数字、日期和执行数学计算的对象。例如 Number、Math、Date
(5)字符串,用来表示和操作字符串的对象。例如 String、RegExp
(6)可索引的集合对象,这些对象表示按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。例如 Array
(7)使用键的集合对象,这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。例如 Map、Set、WeakMap、WeakSet
(8)矢量集合,SIMD 矢量集合中的数据会被组织为一个数据序列。例如 SIMD 等
(9)结构化数据,这些对象用来表示和操作结构化的缓冲区数据,或使用 JSON 编码的数据。例如 JSON 等
(10)控制抽象对象例如 Promise、Generator 等
(11)反射 例如 Reflect、Proxy
(12)国际化,为了支持多语言处理而加入 ECMAScript 的对象。例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其他 例如 arguments

六. undefined 与 undeclared 的区别?

已在作用域中声明但还没有赋值的变量,是 undefined。相反,还没有在作用域中声明过的变量,是 undeclared 的。

七. null 和 undefined 的区别?

首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
undefined 代表的含义是未定义
null 代表的含义是空对象

八. {}和[]的valueOf和toString的结果是什么?

{} 的 valueOf 结果为 {} ,toString 的结果为 "[object Object]"
[] 的 valueOf 结果为 [] ,toString 的结果为 ""

九. Javascript 的作用域和作用域链?

作用域: 作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。
作用域链: 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和 函数。
作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前 端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。

十. javascript 创建对象的几种方式?

(1)第一种是工厂模式,工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系。
(2)第二种是构造函数模式。js 中每一个函数都可以作为构造函数,只要一个函数是通过 new 来调用的,那么我们就可以把它称为构造函数。执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的 prototype 属性,然后将执行上下文中的 this 指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。因为 this 的值指向了新建的对象,因此我们可以使用 this 给对象赋值。构造函数模式相对于工厂模式的优点是,所创建的对象和构造函数建立起了联系,因此我们可以通过原型来识别对象的类型。但是构造函数存在一个缺点就是,造成了不必要的函数对象的创建,因为在 js 中函数也是一个对象,因此如果对象属性中如果包含函数的话,那么每次我们都会新建一个函数对象,浪费了不必要的内存空间,因为函数是所有的实例都可以通用的。
(3)第三种模式是原型模式,因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用。这种方式相对于构造函数模式来说,解决了函数对象的复用问题。但是这种模式也存在一些问题,一个是没有办法通过传入参数来初始化值,另一个是如果存在一个引用类型如 Array 这样的值,那么所有的实例将共享一个对象,一个实例对引用类型值的改变会影响所有的实例。
(4)第四种模式是组合使用构造函数模式和原型模式,这是创建自定义类型的最常见方式。因为构造函数模式和原型模式分开使用都存在一些问题,因此我们可以组合使用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点,但是有一点不足的就是,因为使用了两种不同的模式,所以对于代码的封装性不够好。
(5)第五种模式是动态原型模式,这一种模式将原型方法赋值的创建过程移动到了构造函数的内部,通过对属性是否存在的判断,可以实现仅在第一次调用函数时对原型对象赋值一次的效果。这一种方式很好地对上面的混合模式进行了封装。
(6)第六种模式是寄生构造函数模式,这一种模式和工厂模式的实现基本相同,我对这个模式的理解是,它主要是基于一个已有的类型,在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函数,也达到了扩展对象的目的。它的一个缺点和工厂模式一样,无法实现对象的识别。

十一.JavaScript 继承的几种实现方式?

(1)第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。
(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。
(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。
(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。
(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。
(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。

十二.谈谈你对this、call、apply和bind的理解?

  1. 在浏览器里,在全局范围内this 指向window对象;
  2. 在函数中,this永远指向最后调用他的那个对象;
  3. 构造函数中,this指向new出来的那个新的对象;
  4. call、apply、bind中的this被强绑定在指定的那个对象上;
  5. 箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种 方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下 来;
  6. apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时 也可以传参。

十三.js 获取原型的方法?

• p.proto
• p.constructor.prototype
• Object.getPrototypeOf(p)

十四.什么是闭包?

闭包是指有权访问另一个函数作用域内变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。

十五.什么是 DOM 和 BOM?

DOM 指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM 的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局) 对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 locati on 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对 象的子对象。

十六.三种事件模型是什么?

事件 是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型。

  1. DOM0级模型: ,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js属性来指定监听函数。这种方式是所有浏览器都兼容的。
  2. IE 事件模型: 在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
  3. DOM2 级事件模型: 在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

十七.事件委托是什么?

事件委托 本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到 目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。
使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。

十八.什么是事件传播?

当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在“当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。
事件传播有三个阶段:

  1. 捕获阶段–事件从 window 开始,然后向下到每个元素,直到到达目标元素事件或event.target。
  2. 目标阶段–事件已达到目标元素。
  3. 冒泡阶段–事件从目标元素冒泡,然后上升到每个元素,直到到达 window。

十九.什么是事件捕获?

百度

十九.什么是事件冒泡?

百度

二十.DOM 操作——怎样添加、移除、移动、复制、创建和查找节点?

(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
(2)添加、移除、替换、插入
appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)
(3)查找
getElementById();
getElementsByName();
getElementsByTagName();
getElementsByClassName();
querySelector();
querySelectorAll();
(4)属性操作
getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);

二十一.数组和对象有哪些原生方法,列举一下?

Array.concat( ) 连接数组
Array.join( ) 将数组元素连接起来以构建一个字符串
Array.length 数组的大小
Array.pop( ) 删除并返回数组的最后一个元素
Array.push( ) 给数组添加元素
Array.reverse( ) 颠倒数组中元素的顺序
Array.shift( ) 将元素移出数组
Array.slice( ) 返回数组的一部分
Array.sort( ) 对数组元素进行排序
Array.splice( ) 插入、删除或替换数组的元素
Array.toLocaleString( ) 把数组转换成局部字符串
Array.toString( ) 将数组转换成一个字符串
Array.unshift( ) 在数组头部插入一个元素
Object.hasOwnProperty( ) 检查属性是否被继承
Object.isPrototypeOf( ) 一个对象是否是另一个对象的原型
Object.propertyIsEnumerable( ) 是否可以通过for/in循环看到属性
Object.toLocaleString( ) 返回对象的本地字符串表示
Object.toString( ) 定义一个对象的字符串表示
Object.valueOf( ) 指定对象的原始值

二十二.Ajax 是什么? 如何创建一个 Ajax?

它是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
创建步骤:

面试手写(原生):
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
    if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
         console.log(xhr.responsetXML)
}
jQuery写法
 $.ajax({
         type:'post',
         url:'',
         async:ture,//async 异步  sync  同步
         data:data,//针对post请求
         dataType:'jsonp',
         success:function (msg) {

         },
         error:function (error) {

         }
       })
promise 封装实现:
// promise 封装实现:
function getJSON(url) {
 // 创建一个 promise 对象
 let promise = new Promise(function(resolve, reject) {
   let xhr = new XMLHttpRequest();

   // 新建一个 http 请求
   xhr.open("GET", url, true);

   // 设置状态的监听函数
   xhr.onreadystatechange = function() {
     if (this.readyState !== 4) return;

     // 当请求成功或失败时,改变 promise 的状态
     if (this.status === 200) {
       resolve(this.response);
     } else {
       reject(new Error(this.statusText));
     }
   };

   // 设置错误监听函数
   xhr.onerror = function() {
     reject(new Error(this.statusText));
   };

   // 设置响应的数据类型
   xhr.responseType = "json";

   // 设置请求头信息
   xhr.setRequestHeader("Accept", "application/json");

   // 发送 http 请求
   xhr.send(null);
 });

 return promise;
}

二十三. js 延迟加载的方式有哪些?

js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
我了解到的几种方式是:

  1. 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
  2. 给 js 脚本添加 defer属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
  3. 给 js 脚本添加 async属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
  4. 动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。

二十四.谈谈你对模块化开发的理解?

一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念 ,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污 染,并且模块间没有联系。
后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所 有的所有的模块成员,外部代码可以修改内部属性的值。
现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。

二十五.js 的几种模块规范?

• 第一种是 CommonJS 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
• 第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。
• 第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
• 第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。

二十六.ECMAScript 2015(ES6)有哪些新特性?

• 块作用域
• 类
• 箭头函数
• 模板字符串
• 加强的对象字面
• 对象解构
• Promise
• 模块
• Symbol
• 代理(proxy)Set
• 函数默认参数
• rest 和展开

二十七. var,let和const的区别是什么?

var声明的变量会挂载在window上,而let和const声明的变量不会:
var声明变量存在变量提升,let和const不存在变量提升:
let和const声明形成块作用域,同一作用域下let和const不能声明同名变量,而var可以

二十八.什么是高阶函数?

高阶函数只是将函数作为参数或返回值的函数。
function higherOrderFunction(param,callback){
return callback(param);
}

二十九.深浅拷贝

image.png

三十.对象的拷贝?

浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
Object.assign
展开运算符(...)
深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
JSON.parse(JSON.stringify(obj)): 性能最快
具有循环引用的对象时,报错 当值为函数、undefined、或 symbol 时,无法拷贝 递归进行逐一赋值

三十一.new 运算符的执行过程?

新生成一个对象
链接到原型: obj.proto = Con.prototype
绑定 this: apply
返回新对象(如果构造函数有自己 retrun 时,则返回该值)

三十二.类型转换?

大家都知道 JS 中在使用运算符号或者对比符时,会自带隐式转换,规则如下:
-、*、/、% :一律转换成数值后计算
+:
数字 + 字符串 = 字符串, 运算顺序是从左到右
数字 + 对象, 优先调用对象的 valueOf -> toString
数字 + boolean/null -> 数字
数字 + undefined -> NaN
[1].toString() === '1'
{}.toString() === '[object object]'
NaN !== NaN 、+undefined 为 NaN

三十三.类型判断?

判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本
质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:
基本类型(null): 使用 String(null)
基本类型(string / number / boolean / undefined) + function: 直接使用
typeof 即可
其余引用类型(Array / Date / RegExp Error): 调用 toString 后根据[object54
XXX]进行判断
很稳的判断封装:
let class2type = {}'Array Date RegExp Object Error'.split(' ').forEach(e
=> class2type[ '[object ' + e + ']' ] = e.toLowerCase())
function type(obj) {
if (obj == null) return String(obj)
return typeof obj === 'object' ?
class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof
obj
}

三十四.防抖与节流?

防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。

function debounce(fn, wait, immediate) {
let timer = null
return function() {
let args = arguments
let context = this
if (immediate && !timer) {55
fn.apply(context, args)
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}

节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500ms 执行一次即可。

function throttle(fn, wait, immediate) {
let timer = null
let callNow = immediate
return function() {
let context = this,
args = arguments
if (callNow) {
fn.apply(context, args)
callNow = false
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
}

三十五.说一下闭包

一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外
调用,子函数所在的父函数的作用域不会被释放

7.如何解决异步回调地狱

promise、generator、async/await

三十六.说一下图片的懒加载和预加载

预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加
载。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

三十七.mouseover 和 mouseenter 的区别

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,
冒泡的过程。对应的移除事件是 mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是
不会冒泡,对应的移除事件是 mouseleave

三十八.JS 中的垃圾回收机制

必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才
能对他们进行动态的存储分配。JavaScript 程序每次创建字符串、数组或对象
时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最
终都要释放这些内存以便他们能够被再用,否则,JavaScript 的解释器将会消
耗完系统中所有可用的内存,造成系统崩溃。
这段话解释了为什么需要系统需要垃圾回收,JS 不像 C/C++,他有自己的一套垃
圾回收机制(Garbage Collection)。JavaScript 的解释器可以检测到何时程序
不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这
个对象,可以把它所占用的内存释放掉了。例如:68
var a="hello world";
var b="world";
var a=b;
//这时,会释放掉"hello world",释放内存以便再引用
垃圾回收的方法:标记清除、计数引用。
标记清除
这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境
“,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入
环境变量所占用的内存,只要执行流程进入相应的环境,就可能用到他们。当离
开环境时,就标记为离开环境。
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然
后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除
标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被
删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存。
引用计数法
另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的
次数,当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引
用次数为 1,;相反的,如果包含了对这个值引用的变量又取得了另外一个值,
则原先的引用值引用次数就减 1,当这个值的引用次数为 0 的时候,说明没有办
法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行
的时候,就会释放引用次数为 0 的这些值。
用引用计数法会存在内存泄露,下面来看原因:
function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
在这个例子里面,objA 和 objB 通过各自的属性相互引用,这样的话,两个对象
的引用次数都为 2,在采用引用计数的策略中,由于函数执行之后,这两个对象
都离开了作用域,函数执行完成之后,因为计数不为 0,这样的相互引用如果大
量存在就会导致内存泄露。
特别是在 DOM 对象中,也容易存在这种问题:
var element=document.getElementById(’‘);
var myObj=new Object();
myObj.element=element;
element.someObject=myObj;
这样就不会有垃圾回收的过程。

11.setTimeout、setInterval 和 requestAnimationFrame

之间的区别

与 setTimeout 和 setInterval 不同,requestAnimationFrame 不需要设置时间
间隔,
大多数电脑显示器的刷新频率是 60Hz,大概相当于每秒钟重绘 60 次。大多数浏
览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频
率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是 1000ms/60,约
等于 16.6ms。
RAF 采用的是系统时间间隔,不会因为前面的任务,不会影响 RAF,但是如果前
面的任务多的话,73
会响应 setTimeout 和 setInterval 真正运行时的时间间隔。
特点:
(1)requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次
重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
(2)在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回
流,这当然就意味着更少的 CPU、GPU 和内存使用量
(3)requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览
器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,
有效节省了 CPU 开销

三十九.数据去重

法一:indexOf 循环去重
法二:ES6 Set 去重;Array.from(new Set(array))
法三:Object 键值对去重;把数组的值存成 Object 的 key 值,比如
Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的
话,就说明该值是重复的

四十.数组常用方法

push(),pop(),shift(),unshift(),splice(),sort(),reverse(),map()等

四十一. js 判断类型

判断方法:typeof(),instanceof,Object.prototype.toString.call()等

四十二. 事件代理在捕获阶段的实际应用

可以在父元素层面阻止事件向子元素传播,也可代替子元素执行某些操作。

四十三. 性能优化

减少 HTTP 请求
使用内容发布网络(CDN)
添加本地缓存
压缩资源文件
将 CSS 样式表放在顶部,把 javascript 放在底部(浏览器的运行机制决定)
避免使用 CSS 表达式
减少 DNS 查询
使用外部 javascript 和 CSS
避免重定向
图片 lazyLoa

四十四.跨域的原理

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,
是浏览器对 JavaScript 实施的安全限制,那么只要协议、域名、端口有任何一
个不同,都被当作是不同的域。跨域原理,即是通过各种方式,避开浏览器的安
全限制

四十五.this 的指向 哪几种

默认绑定:全局环境中,this 默认绑定到 window。
隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this
隐式绑定到该直接对象。
隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到
window。显式绑定:通过 call()、apply()、bind()方法把对象绑定到 this 上,
叫做显式绑定。
new 绑定:如果函数或者方法调用之前带有关键字 new,它就构成构造函数调用。
对于 this 绑定来说,称为 new 绑定。87
【1】构造函数通常不使用 return 关键字,它们通常初始化新对象,当构造函数
的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计
算结果就是这个新对象的值。
【2】如果构造函数使用 return 语句但没有指定返回值,或者返回一个原始值,
那么这时将忽略返回值,同时使用这个新对象作为调用结果。
【3】如果构造函数显式地使用 return 语句返回一个对象,那么调用表达式的值
就是这个对象

四十六.ES6 箭头函数的特性

ES6 增加了箭头函数,基本语法为
let func = value => value;
相当于
let func = function (value) {
return value;
};
箭头函数与普通函数的区别在于:
1、箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值,这就意
味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的
this,
2、箭头函数没有自己的 arguments 对象,但是可以访问外围函数的 arguments
对象
3、不能通过 new 关键字调用,同样也没有 new.target 值和原型

四十七.async 和 await 具体该怎么用

(async () = > {
await new promise();
})()

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

推荐阅读更多精彩内容