- HTML————CSS
- middle{
background-color: #E41627;
width: 100px;
height: 100px;
margin: auto;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
-webkit-transform: translateX(-50%) translateY(-50%);
}- 6.怎样让一个元素居中?
- 14.请列出至少三种垂直居中的方式?
- 两栏布局方式
- Position 的属性值及区别:
- 如何垂直居中
- 1. title 和 alt 的区别
- 4. 盒模型
- 7. bfc
- 4. h5 新出的 api
- 5. css 的标准化组织是?
- 1.对 WEB 标准以及 W3C 的理解与认识
- xhtml 和 html 有什么区别
- Doctype 严格模式与混杂模式-如何触发这两种模式,区分它们有何意义?
- 行内元素有哪些?块级元素有哪些?CSS 的盒模型?
- 5.CSS 引入的方式有哪些? link 和 @import 的区别是?
- 6.CSS 选择符有哪些? 哪些属性可以继承? 优先级算法如何计算? 内联和 important 哪个优先级高?
- 7.前端页面有哪三层构成,分别是什么?作用是什么?
- 8.css 的基本语句构成是?
- 9.你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么?
- 10.写出几种 IE6 BUG 的解决方法
- 11.标签上 title 与 alt 属性的区别是什么?
- 12.描述 css reset 的作用和用途。
- 13.解释 css sprites,如何使用。
- 14.浏览器标准模式和怪异模式之间的区别是什么?
- 3. 屏幕正中间有一个元素 A,元素 A 中有文字 A,随着屏幕宽度的变化,始终需要满足下列要求:(请用 html css 实现)
- 4. ul 内部除最后一个 li 以外设置右边框效果
- 5. 一个标签的 class 样式的渲染顺序, ID, class,标签,伪类的优先级
- 5.如何用 jQuery 给一个标签添加一个属性?
- 10.虚线边框
- 11.怎么实现在 jQuery 中找到所在的同辈元素?
- 12. 简述 h5 中新增的 canvas,svg,audio 标签的作用
- 13. 简述如何通过 css 实现响应式布局的方式
- 1.HTML、CSS、js作用是什么?
- flex 有哪些属性
- css 画一个三角形
- css 画等边三角形
- 画个菱形
- 11. 盒模型 top:50%,margin-top:50%,相对什么计算的
- 12. 怎么求的盒子的总尺寸
- 13. clientWidth,offsetWidth,scrollWidth 的区别
- js
- 1.javascript 的 typeof 返回哪些数据类型
- 9. 数据类型
- 2.例举 3 种强制类型转换和 2 种隐式类型转换?
- 数组常见 API
- 6. 产生一个不重复的随机数组
- 实现一个函数,给定一个非负数整数 num,反复将各个位上的数字相加,直到结果为一位数。
- 1.独一无二的次数:
- 2.字符串压缩
- 3.删除给定值 val
- 2. var arr=[1,2,3,”1”,1,2,”a”,”b”] var brr=[1,2,”b”] 输出[3,”1’,”a”] 注:arr 先去重
- 3.split() join() 的区别
- 13.map 和 forEach 的区别?
- 判断数组的几种方法:
- 在 javascript 中什么是伪数组?如何将伪数组转换为标准数组
- 6. array 和 object 的区别
- 1.请实现一个模块 math,支持链式调用 math.add(2,4).minus(3).times(2);
- 3.请实现一个深拷贝、浅拷贝
- 6.[“1”,”2”,”3”,”4”,”5”,6,7,8,9,10,11,12,13,14,15].map(parselnt);
- 数组去重, 能写几种写几种
- 重写数组 7 个会改变原数组的方法,只有用这 7 种方法才会触发数组对应的 watcher 进行更新
- 对于数组,是用重写的 splice 来实现
- labal 的使用?
- 画 0.5px 直线;
- 1. 讲一讲继承的所有方式都有什么 手写一个寄生组合式继承
- 7. this 指向问题
- 8. call apply bind 区别
- 1. 我们来假设一个场景,就比如说部门给我们一个任务,让我们做一个上传文件的区域,该怎么做啊
- 7. 构造函数给我讲讲可以吗,最好加上你自己对于他的理解
- 为什么要去匹配&符号
- 如果攻击者输入了 script 标签的话,怎么做
- 原型,prototype,constructor,proto
- 箭头函数
- 没有自己的 arguments 那怎么拿到 arguments
- let,const,var
- 请说一下响应式数据的理解
- 12.讲一下虚拟 dom
- webpack 编译原理
- class 和原型继承的区别
- 讲一下闭包
- 自执行函数
- js 的垃圾回收机制
- 如何解决内存泄漏,大型项目又咋办
- 控制台工具有个 memory 还是 performance 来着
- 6. 填充字符串(编程)
- 7. 管道函数(编程)
- 8. 箭头函数和普通函数的区别
- 7. continue 和 break 的区别
- 10. 遍历一个目录,将文件中所有的标签改成<&b>
- 11. 文件流 steam
- 12. git 回退操作
- 13. translate 的优点
- 14. 实现一个球
- 15. 一个白板,实现画笔的功能
- 8. 图片的一些格式,以及它们的区别,和其中的算法
- 5.事件绑定和普通事件有什么区别
- 6.IE 和 DOM 事件流的区别
- 7.IE 和标准下有哪些兼容性的写法
- 9.call 和 apply 的区别
- 11.b 继承 a 的方法
- 12.写一个获取非行间样式的函数
- 16.添加 删除 替换 插入到某个接点的方法
- 18.javascript 的本地对象,内置对象和宿主对象
- 19.document load 和 document ready 的区别
- 20.””和“=”的不同
- 21.javascript 的同源策略
- 对于 bootstrap 的理解:
- call、apply、bind 的区别
- 1. 说一下减少 DOM 数量的办法,一次性给你大量的 DOM 怎么优化?
- 2. 描述一下 script 标签的 async 和 defer 的区别
- 9.写出 setTimeout,SetInterval 和 requestAnimationFrame 的区别
- 15. 简述 DOM, HTML DOM 的区别跟联系
- 14. 什么是事件流
- 10.什么是事件委托?
- 8. jq 事件委托
- 通过哪个属性来获取目标对象
- 委托对象的 target 属性
- 5. 事件循环方面的执行顺序出了一个题
- 15.如何阻止事件冒泡和默认事件
- 性能优化
- VUE
- 5. 简述 vdom
- 3. vue 框架的优点
- 请列举出三个 Vue 中常用的生命周期钩子函数
- 5.简述 VUE 的生命周期
- vue 组件之间的传值
- 7.组件有多少种传参方式?
- 父子组件传递数据?
- 子父组件传递数据?
- 子组件与子组件如何通信
- 兄弟组件传递数据?
- 路由之间跳转, 如何携带参数, 有哪些方式
- 7.描述一下路由的功能
- vuex 有哪几种属性? 不用 vuex 会带来什么问题?
- vue 双向绑定原理
- vue 如何优化首页加载?
- 8.写出 VUEX 中,State,mutation 和 action 的联系
- 为什么 v-for 要带 key,key 的作用,
- 用数组索引作为 key 好不好,为什么
- 了解 diff 的讲一下 diff 中 key 的好处
- 网络
- 3. http 常见的请求头都有什么啊 (能记住几个说几个)
- 13. 说一下 http
- 6. 加密过程是什么
- 5. http 状态码
- 4. 304 浏览器缓存
- 4. 了解定长包体吗
- 5. 你对 refer 怎么理解的
- 跨域
- 跨域,解决方案
- 为什么 JSONP 可以实现跨域
- 7. jsonp 原理,手写 jsonp
- 17.解释 jsonp 的原理,以及为什么不是真正的 ajax
- 12.数据链路层
- 请简述一下 cookies,sessionStorage 和 localStorage 的区别?
- get 和 post 请求区别?
- 14. 什么是 ajax
- axios 是什么?如何使用?
- 10.ajax 请求时,如何解释 json 数据
- 8.ajax 请求的时候 get 和 post 方式的区别
- 9.ajax 发送请求出现乱码怎么解决?
- 3.写出 axios 和 ajax 的区别
- 1. TCP 的拥塞控制
- 2. DNS
- 防止 CSRF:就是让你的每个请求都带一个从 cookie 中拿到的 key, 根据浏览器同源策略,假冒的网站是拿不到你 cookie 中得 key 的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。
- es6 里面出现的网络请求方案——fetch
- 16.简述一下协商缓存和强缓存的区别?
- 如何禁用缓存
- 习题
- 以下代码执行输出结果是什么
- 设置元素浮动后,该元素的 display 值是多少()
- 2.对权重排序正确的是()
- 3.下列说法不正确的是()
- 4.下面哪组全时置换元素()
- 5.新窗口打开网页,用到以下哪个值()
- 6.下面程序输出结果()
- 7.请写出下面程序的输出()
- 8.下边的代码输出的结果是()
- 9.以下运行结果()
- 10.下列关于闭包描述不正确的是()
- 6. 写出代码的执行结果,并解释为什么
- 7. 请写出弹出值,并解释为什么
- 8. 写出下面程序的打印顺序,并简要说明原因
- 17.算法:统计一个字符串中出现最多的字符?
- 18.算法:求一个数组中最大值和最小值的差?
- 12. 快速排序思想与实现
- 19.算法:给 n 个元素的数组,元素取值只有 0,1,2 三种可能,请为这个数组进行排序?
- 9. 排序算法
- 给个数字,判断它的二进制有没有相邻位,如 5 是 101,没有相邻位,返回 false;6 是 110,有相邻位,返回 true
- 利用 num & 1 得到 num 的最后一位二进制为 0 或者 1,依次判断
- 其他
- 1.什么是优雅降级和渐进增强?
- 2.同源策略是什么?
- 3.浏览器是如何渲染页面的?
- 时间复杂度
- 4.SCSS 可以用什么代替?
- 模拟场景,我是黑客,面试官是用户,我如何进行 XSS 攻击
- 用 node 写过哪些接口,登录验证是怎么做的
- XSS 攻击和 CSRF 攻击
- 6.写出 es6 都有哪些新特性
- webpack 热更新原理,底层实现(主要是我简历写了熟悉 webpack,问的有点深,懵逼了)
- 8. webpack 中 devserve
- 10. 什么是 webpack,为什么要用它
- 2.请简述 ES6 代码转成 ES5 代码的实现思路。
- 6. es6 之前如何模拟类的
- 11. web socket 和 socket io
- 16. 什么是 promise,手写 promise
- 17. 什么是 less 和 sass,为什么用
- 18. jquery,vue,react,angular 区别
- 项目
HTML————CSS
清理浮动方法有哪些?
1.使用空标签清除浮动 clear:both(理论上能清楚任何标签,增加无意义的标签)
2.使用 overflow:auto(空标签元素清除浮动而不得不增加无意代码的弊端,,使用 zoom:1 用于兼容 IE)
3.是用 afert 伪元素清除浮动(用于非 IE 浏览器)
清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为 0 的问题
1.如下,我给父盒子设置一个 boder,内部放两个盒子一个 big 一个 small,未给 big 和 small 设置浮动,则他们会默认撑开父盒子]
2.当我给内部两个盒子加上 float 属性的时候
顶部深蓝色盒子就会顶上来,然后父盒子因为没设置高度,变成一条线,big 和 small 已经浮动了
总结一下:
当父元素不给高度的时候,
内部元素不浮动时会撑开
而浮动的时候,父元素变成一条线
这时候很多人会想到新建标签 clear:both 和 float 方法,但是这两种方法并不推荐使用!
什么是 clear:both
clear:both:本质就是闭合浮动, 就是让父盒子闭合出口和入口,不让子盒子出来
清除浮动的方法(最常用的 4 种)
1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置 clear:both;)(不推荐)
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>.fahter{width: 400px;border: 1px solid deeppink;}.big{width: 200px;height: 200px;background: darkorange;float: left;}.small{width: 120px;height: 120px;background: darkmagenta;float: left;}.footer{width: 900px;height: 100px;background: darkslateblue;}.clear{clear:both;}</style></head><body><div class="fahter"><div class="big">big</div><div class="small">small</div><div class="clear">额外标签法</div></div><div class="footer"></div></body></html>
此时
如果我们清除了浮动,父元素自动检测子盒子最高的高度,然后与其同高。
优点:通俗易懂,方便
缺点:添加无意义标签,语义化差
不建议使用。
2.父级添加 overflow 属性(父元素添加 overflow:hidden)(不推荐)
通过触发 BFC 方式,实现清除浮动
.fahter{width: 400px;border: 1px solid deeppink;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 class="clear">额外标签法</div>--></div><div class="footer"></div></body>
优点:符合闭合浮动思想,结构语义化正确
缺点:ie6-7 不支持伪元素:after,使用 zoom:1 触发 hasLayout.
推荐使用
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>
优点:代码更简洁
缺点:用 zoom:1 触发 hasLayout.
推荐使用
描述 HTML 渲染?
浏览器渲染页面的一般过程:
1.浏览器解析 html 源码,然后创建一个 DOM 树。并行请求 css/image/js 在 DOM 树中,每一个 HTML 标签都有一个对应的节点,并且每一个文本也都会有一个对应的文本节点。DOM 树的根节点就是 documentElement,对应的是 html 标签。
2.浏览器解析 CSS 代码,计算出最终的样式数据。构建 CSSOM 树。对 CSS 代码中非法的语法它会直接忽略掉。解析 CSS 的时候会按照如下顺序来定义优先级:浏览器默认设置 < 用户设置 < 外链样式 < 内联样式 < html 中的 style。
3.DOM Tree + CSSOM —> 渲染树(rendering tree)。渲染树和 DOM 树有点像,但是是有区别的。
DOM 树完全和 html 标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如 head、display:none 的元素等。而且一大段文本中的每一个行在渲染树中都是独立的一个节点。渲染树中的每一个节点都存储有对应的 css 属性。
4.一旦渲染树创建好了,浏览器就可以根据渲染树直接把页面绘制到屏幕上。
以上四个步骤并不是一次性顺序完成的。如果 DOM 或者 CSSOM 被修改,以上过程会被重复执行。实际上,CSS 和 JavaScript 往往会多次修改 DOM 或者 CSSOM。
水平、垂直居中的方法 5 种?
文字居中
垂直居中(vertical-align)
我们都知道有这么一个属性可以让图片,文本等在元素中垂直居中
vertical-align:middle;
vertical-align 值有很多,常用的就是 middle,bottom,text-bottom 等,我们先说 middle。
vertical-align 时而没效果
然而真实使用的时候,我们会发现这个属性“时灵时不灵”,有些情况下我们加了这个属性之后仍然不见 img 或者 text 有任何的变化。那是因为 vertical-align 只作用在 inline-block 或者 inline,还有 table-cell 等元素内。同时这两种还有所不同。
对于行内元素
text-top
使元素的顶部与父元素的字体顶部对齐。
text-bottom
使元素的底部与父元素的字体底部对齐。
middle
使元素的中部与父元素的基线加上父元素 x-height(译注:”x”高度)的一半(交叉处)对齐。
对于 table-cell 的常用
top
使单元格内边距的上边缘与该行顶部对齐。
middle
使单元格内边距盒模型在该行内居中对齐。
bottom
使单元格内边距的下边缘与该行底部对齐。
vertical-align 并不是在高度内居中,而是对齐在行高内的 middle 线上。
所以我总结了两种使用 vertical-align 居中的方法:
第一种
文本居中
这种情况下图片和文字可以分行显示文字在图片下面同时图片和文字作为整体在元素内垂直居中。
第二种
这种情况下文字是因为 line-height 属性而居中,同时 img 上的 vertical-align:middle 属性使其对齐在 middle 线上,如果父盒子去掉了 line-height 属性的话那么文字将不会再垂直居中。(可以试试 bottom 和 text-bottom 的不同。)
Flex
flex 布局需要使用 align-items 来进行上下居中, 这个属性是父元素的属性, 可以统一设置子元素的上下对齐方式.
.box {
display:flex;
align-items: center
}
这样.box 元素内的子元素是可以垂直居中的。那么要单独设置某个子元素的上下对齐方式的话需要使用 align-self: center 这个属性. 它可以单独设置某个子元素的对齐方式, 且覆盖父元素的样式.
.box {
display:flex;
align-items: center
}
.child {
align-self: center
}
水平居中
text-align:center;
CSS 元素垂直居中的 6 种方法
利用 CSS 进行元素的水平居中,比较简单,
行级元素设置其父元素的 text-align center,
块级元素设置其本身的 left 和 right margins 为 auto 即可。
1、Line-Height Method
试用:单行文本垂直居中,demo
代码:
#child {line-height: 200px;}<div id="parent"><div id="child">Text here</div></div>
垂直居中一张图片,代码如下
#parent {line-height: 200px;}#parent img {vertical-align: middle;}<div id="parent"><img src="image.png" alt="" /></div>
2、CSS Table Method
适用:通用,demo
代码:
#parent {display: table;}#child {display: table-cell;vertical-align: middle;}<div id="parent"><div id="child">Content here</div></div>
低版本 IE fix bug:
#child {
display: inline-block;
}
3、Absolute Positioning and Negative Margin
适用:块级元素,demo
代码:
#parent {position: relative;}#child {position: absolute;top: 50%;left: 50%;height: 30%;width: 50%;margin: -15% 0 0 -25%;}<div id="parent"><div id="child">Content here</div></div>
4、Absolute Positioning and Stretching
适用:通用,但在 IE 版本低于 7 时不能正常工作,demo
代码:
#parent {position: relative;}#child {position: absolute;top: 0;bottom: 0;left: 0;right: 0;width: 50%;height: 30%;margin: auto;}<div id="parent"><div id="child">Content here</div></div>
5、Equal Top and Bottom Padding
适用:通用,demo
代码:
#parent {padding: 5% 0;}#child {padding: 10% 0;}<div id="parent"><div id="child">Content here</div></div>
6、Floater Div
适用:通用,demo
代码:
#parent {height: 250px;}#floater {float: left;height: 50%;width: 100%;margin-bottom: -50px;}#child {clear: both;height: 100px;}<div id="parent"><div id="floater"></div><div id="child">Content here</div></div>
css 文本和 div 垂直居中方法汇总
在样式布局中,我们经常碰到需要将元素居中。通过 css 实现元素的水平居中较为简单:对文本,只需要对其父级元素设置 text-align: center;,而对 div 等块级元素,只需要设置其 left 和 right 的 margin 值为 auto。要实现元素的垂直居中,有人会想到 css 中的 vertical-align 属性,但是它只对拥有 valign 特性的元素才生效,例如表格元素中的、、这样的元素是没有 valign 特性的,因此使用 vertical-align 对它们不起作用。因此我们需要通过别的方法去实现元素的垂直居中,下面我总结了几种了常用垂直居中方法。
单行文本垂直居中
对于单行文本,我们只需要将文本行高(line-height)和所在区域高度(height)设为一致即可:
<div id="div1">这是单行文本垂直居中</div>
/css 代码/
#div1{
width: 300px;
height: 100px;
margin: 50px auto;
border: 1px solid red;
line-height: 100px; /设置 line-height 与父级元素的 height 相等/
text-align: center; /设置文本水平居中/
overflow: hidden; /防止内容超出容器或者产生自动换行/
}

多行文本垂直居中
多行文本垂直居中分为两种情况,一个是父级元素高度不固定,随着内容变化;另一个是父级元素高度固定。
父级元素高度不固定
父级高度不固定的时,高度只能通过内部文本来撑开。这样,我们可以通过设置内填充(padding)的值来使文本看起来垂直居中,只需设置 padding-top 和 padding-bottom 的值相等:
<div id="div1">这是多行文本垂直居中,这是多行文本垂直居中,这是多行文本垂直居中,这是多行文本垂直居中。</div>
/css 代码/
#div1{
width: 300px;
margin: 50px auto;
border: 1px solid red;
text-align: center; /设置文本水平居中/
padding: 50px 20px;
}

父级元素高度固定
本文一开始就提到 css 中的 vertical-align 属性,但是它只对拥有 valign 特性的元素才生效,结合 display: table;,可以使得 div 模拟 table 属性。因此我们可以设置父级 div 的 display 属性:display: table;;然后再添加一个 div 包含文本内容,设置其 display:table-cell;和 vertical-align:middle;。具体代码如下:
<div id="outer"><div id="middle">这是固定高度多行文本垂直居中,这是固定高度多行文本垂直居中,这是固定高度多行文本垂直居中,这是固定高度多行文本垂直居中。</div></div>
/css 代码/
#outer{
width: 400px;
height: 200px;
margin: 50px auto;
border: 1px solid red;
display: table;
}
#middle{
display:table-cell;
vertical-align:middle;
text-align: center; /设置文本水平居中/
width:100%;
}

但是,在 IE7 中显示效果如下:
这是因为 IE7 及以下的版本并不能很好的支持 display:table 和 display:table-cell 属性,当然,如果你不考虑 IE7 以下的版本的浏览器,上述方法是可以实现垂直居中。如果把 IE7 及以下版本考虑进去,我们可以通过用到 CSS hack 的知识来设置针对不同浏览器的属性。
<div id="outer"><div id="middle"><div id="content">这是固定高度多行文本垂直居中(兼容IE7),这是固定高度多行文本垂直居中(兼容IE7),这是固定高度多行文本垂直居中(兼容IE7),这是固定高度多行文本垂直居中(兼容IE7)。</div></div></div>
/css 代码/
#outer{
width: 400px;
height: 200px;
margin: 50px auto;
border: 1px solid red;
display: table;
position:relative; //兼容 IE7 及以下版本
}
#middle{
display:table-cell;
vertical-align:middle;
text-align: center; /设置文本水平居中/
width:100%;
position:absolute; //兼容 IE7 及以下版本
top:50%;
}
#content {
position:relative; //兼容 IE7 及以下版本
*top:-50%;
}
子 div 垂直居中
1、根据子 div 具体大小设置偏移
如果子 div 固定大小,设定水平和垂直偏移父元素的 50%,再根据实际长度将子元素向上和向左挪回一半大小
<div id="outer"><div id="middle">子div(固定大小)垂直居中</div></div>
/css 代码/
#outer{
background-color: #13CDF4;
width: 300px;
height: 200px;
position: relative;
}
#middle{
background-color: #E41627;
width: 100px;
height: 100px;
margin: auto;
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
}

该方法兼容 IE7、IE6,但是只针对子 div 大小的固定的情况下才有效。大部分时候,子 div 的大小是不固定的,下面介绍子 div 大小不固定时的方法。由于显示效果与这个效果基本一样,效果图就不一一贴出来,读者可以自行复制代码验证。
2、利用 translate
针对第一种方法中水平和垂直偏移父元素的 50%后,不设置 margin 值,而是利用除 css3 中的 transform 属性设置 translate 的值,css 代码部分改成如下:
middle{
background-color: #E41627;
width: 100px;
height: 100px;
margin: auto;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
-webkit-transform: translateX(-50%) translateY(-50%);
}
这种方法需要注意 transform 是 css3 中的属性,使用时注意浏览器的兼容性,IE9 之前的版本不支持。
3、利用绝对布局 absolute
<div id="outer"><div id="middle">利用绝对定位实现子div大小不固定垂直居中</div></div>
/css 代码/
#outer{
background-color: #13CDF4;
width: 300px;
height: 200px;
position: relative;
}
#middle{
background-color: #E41627;
width: 100px; //子 div 大小可随意设置
height: 100px;
margin: auto;
position: absolute;
top: 0;left: 0;right: 0;bottom: 0;
}
该方法不兼容 IE7、IE6
4、利用 vertical-align
<div id="outer"><div id="middle">利用vertical-align属性实现子div大小不固定垂直居中</div></div>
/css 代码/
#outer{
background-color: #13CDF4;
width: 300px;
height: 200px;
display: table-cell;
vertical-align: middle;
}
#middle{
background-color: #E41627;
width: 100px;
height: 100px;
margin: 0 auto;
}
这种方法是将 div 转变成 table-cell 显示,然后通过 vertical-align: middle;再设置其子元素垂直居中,这种方法和上面设置父级元素高度固定时多行文本居中的方法一样,所以这种方法也不能兼容 IE7、IE6。如果需要兼容 IE7、IE6,可以参照上面的代码,上面设置父级元素高度固定时多行文本居中的方法其实就是将最里面的 div 垂直居中。这里我就不重述了。
5、利用 display: flex
<div id="outer"><div id="middle">利用display: flex实现子div大小不固定垂直居中</div></div>
/css 代码/
#outer{
background-color: #13CDF4;
width: 300px;
height: 200px;
display: flex;
justify-content: center;/实现水平居中/
align-items:center; /实现垂直居中/
}
#middle{
background-color: #E41627;
width: 100px;
height: 100px;
}
这种方法只需要在父级 div 中加上这三句话就行,但是在 IE 中兼容性不好,IE9 及以下 IE 浏览器版本都不支持。
6.怎样让一个元素居中?
块级元素水平垂直居中方案
- 通过定位实现水平垂直居中
/* 第一种*/div {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}/* 第二种 */div {position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto auto;}
- 弹性布局
.father {
display: flex;
justify-content: center;
align-items: center;
}
14.请列出至少三种垂直居中的方式?
两栏布局方式
1、Float
2、Display:flex
3、Position:absolute
Position 的属性值及区别:
1、relative 相对定位
2、Absolute 绝对定位
3、Fixed 固定定位
4、sticky
区别:relative 不会脱离文档流
Absolute 相对于父级进行定位
Flex 固定窗口定位
position 可取的值有
relative: 相对于自身原来位置
absolute: 相对于最近有定位的父级
fixed: 相对于视口
sticky: 相对于最近有滚动性质的父级
如何垂直居中
- line-height = height
- position: absolute; top: 50%; transform: translateY(-50%)
- 弹性盒子, display: flex; align-items: center;
- vertical-align: middle;
1. title 和 alt 的区别
首先 title 添加的是标题,鼠标在元素上停留时间较长时会显示出来的文字信息。
alt 是占位符,当元素内容资源没有加载过来的时候显示的文字
title 属性可以设置在所有标签元素上 包括 html 标签
alt 属性只能用在 img area input 这样加载资源的标签上,用于网页中图片无法正常显示时给用户提供文字说明使其了解图像信息
4. 盒模型
盒模型是指页面在渲染的时候每个元素是以类似盒子的形态进行排列展示,在 css 中与盒子大小相关的属性有 margin,border, padding, width, 由于 css 的发展 在 css 中盒模型又被分为两种:
一种是标准盒模型,标准盒模型内容区的宽高就是我们设置的 width 和 height 的高度
另一种是 IE6 混杂盒模型,在混杂盒模型中我们设置的 width height 包含了 padding 和 border 区域 也就是盒子真正的内容区的宽高 = width - padding - border
现在切换盒模型的方法是通过 c3 里面的 box-sizing 属性进行切换 content-box 是标准盒模型 border-box 是混杂盒模型
7. bfc
bfc: 块级格式化上下文对象,他并不是一个js对象而是一种渲染方式,按照从上到下从左到右,依次排列的方式进行渲染,该方式会进行计算元素的边界,这样就可以修复一些渲染问题例如margin 合并/塌陷的问题,float浮动元素撑不开父元素的问题等。触发bfc的方式有:1. 添加float浮动2. position的值为absolute / fixed3. overflow的值为hidden scroll 等非visible的值4. display: inline-block, table-ceil等
4. h5 新出的 api
| 新增元素 | 说明 |
|---|---|
| video | 表示一段视频并提供播放的用户界面 |
| audio | 表示音频 |
| canvas | 表示位图区域 |
| source | 为 video 和 audio 提供数据源 |
| track | 为 video 和 audio 指定字母 |
| svg | 定义矢量图 |
| code | 代码段 |
| figure | 和文档有关的图例 |
| figcaption | 图例的说明 |
| main | |
| time | 日期和时间值 |
| mark | 高亮的引用文字 |
| datalist | 提供给其他控件的预定义选项 |
| keygen | 秘钥对生成器控件 |
| output | 计算值 |
| progress | 进度条 |
| menu | 菜单 |
| embed | 嵌入的外部资源 |
| menuitem | 用户可点击的菜单项 |
| menu | 菜单 |
| template | |
| section | 标记定义一个区域,章节、页眉、页脚或文档中的其他部分。 |
| nav | 标记定义导航链接 |
| aside | 标记定义页面内容部分的侧边栏 |
| article | 标记定义一篇文章 |
| footer | 标记定义一个页面或一个区域的底部 |
| header | 标记定义一个页面或一个区域的头部 |
现在来说说图片中出现的标签:
结构标签:(块状元素) 有意义的 div
hgroup 标记定义文件中一个区块的相关信息
figure 标记定义一组媒体内容以及它们的标题
figcaption 标签定义 figure 元素的标题。
dialog 标记定义一个对话框(会话框)类似微信
多媒体交互标签
<video> 标记定义一个视频<audio> 标记定义音频内容<source> 标记定义媒体资源<canvas> 标记定义图片<embed> 标记定义外部的可交互的内容或插件 比如flash
HTML5 的多媒体标签的出现意味着富媒体的发展以及支持不使用插件的情况下即可操作媒体文件,极大地提升了用户体验
Web 应用标签
menu 命令列表
menuitem menu 命令列表标签 FF(嵌入系统)
command menu 标记定义一个命令按钮
meter 状态标签(实时状态显示:气压、气温)C、O
progress 状态标签 (任务过程:安装、加载) C、F、O
datalist 为 input 标记定义一个下拉列表,配合 option F、O
details 标记定义一个元素的详细内容 ,配合 dt、dd C
注释标签
ruby 标记定义 注释或音标
rp 告诉那些不支持 Ruby 元素的浏览器如何去显示
rt 标记定义对 ruby 的注释内容文本
其他标签
keygen 标记定义表单里一个生成的键值(加密信息传送)O、F
mark 标记定义有标记的文本 (黄色选中状态)
output 标记定义一些输出类型,计算表单结果配合 oninput 事件
time 标记定义一个日期/时间 目前所有主流浏览器都不支持
重新定义的 Html 标签
之后放上一个小 demo 展示页面效果


<style>/* 页面头部 header*/header {height:150px; background: #ABCDEF}nav {height:30px; background: #ff9900; margin-top: 100px}nav ul li {width: 100px; height:30px; float: left; line-height: 30px}/* 页面中部div*/div {margin-top: 10px; height: 1500px}section {height: 1500px; background: #ABCDEF; width: 70%; float: left}article {background: #F90; width: 500px; margin: 0 auto; text-align: center}aside {height: 1500px; background: #ABCDEF;width: 28%; float: right}/* 页面底部 footer*/footer {height: 100px; background: #ABCDEF; clear: both; margin-top: 10px}</style></head><body><header><p>这是一个header标签</p><nav><ul><li>首页</li><li>企业</li><li>商城</li><li>社区</li></ul></nav></header><div><section><p>这是一个section标签</p><article><h2>春晓</h2><p>春眠不觉晓,<br/>处处闻啼鸟,<br/>夜来风雨声,<br/>花落只多少.<br/><br/></p></article><article><h2>春晓</h2><p>春眠不觉晓,<br/>处处闻啼鸟,<br/>夜来风雨声,<br/>花落只多少.<br/><br/></p></article><article><h2>春晓</h2><p>春眠不觉晓,<br/>处处闻啼鸟,<br/>夜来风雨声,<br/>花落只多少.<br/><br/></p></article><hr/><figure><figcaption>UFO</figcaption><p>不明飞行物</p></figure><figure><dt>DDS</dt><dd>大屌丝</dd></figure><hr/><dialog><dt>问</dt><dd>答</dd><dt>再问</dt><dd>再达</dd></dialog><hr/><menu><li>点击</li><li>右键单击</li></menu><hr/><span contextmenu="candan">右键单击我试试</span><menu type="context" id="candan"><menuitem label="菜单1" onclick="alert('单击啦')" icon="http://www.baidu.com/img/baidu_sylogo1.gif"></menuitem></menu><meter min="0" max="10" value="5" low="3" high="7"></meter><progress max="100" value="0" id="pro"></progress><script>var pro = document.getElementById('pro');setInterval(function(){pro.value += 1;},100);</script><details><dt>这是一个问题</dt><dd>这是一个答案</dd><dt>这是一个问题</dt><dd>这是一个答案</dd><dt>这是一个问题</dt><dd>这是一个答案</dd></details><hr /><ruby>这<rp>(</rp><rt>zhe</rt><rp>)</rp></ruby><hr/>女人<mark>最喜欢的事情</mark>作</section><aside><p>这是一个aside标签</p><hgrounp><h3>徐亚飞为什么这么爱我?</h3><h3>徐亚飞为什么这么爱我?</h3><h3>徐亚飞为什么这么爱我?</h3><h3>徐亚飞为什么这么爱我?</h3></hgrounp></aside></div><footer><p>这是一个footer标签</p><hr/><small>法律条文</small><small>联系我们</small><small>客户意见</small><small>商户合作</small></footer></body>
- css3
css3 被划分为模块,最重要的几个模块包括:选择器、框模型、背景和边框、文本效果、2D/3D 转换、动画、多列布局、用户界面
选择器
框模型
背景和边框
border-radius、box-shadow、border-image、
background-size:规定背景图片的尺寸
background-origin:规定背景图片的定位区域
background-clip:规定背景的绘制区域
文本效果(常用)
text-shadow:设置文字阴影
word-wrap:强制换行
word-break
css3 提出@font-face 规则,规则中定义了 font-family、font-weight、font-style、font-stretch、src、unicode-range
2/3D 转换
transform:向元素应用 2/3D 转换
transition:过渡
动画
@keyframes 规则:
animation、animation-name、animation-duration 等
用户界面(常用)
box-sizing、resize
css3 新增伪类
:nth-child()
:nth-last-child()
:only-child
:last-child
:nth-of-type()
:only-of-type()
:empty
:target 这个伪类允许我们选择基于 URL 的元素,如果这个元素有一个识别器(比如跟着一个#),那么:target 会对使用这个 ID 识别器的元素增加样式。
:enabled
:disabled
:checked
:not
5. css 的标准化组织是?
W3C(万维网联盟)
1.对 WEB 标准以及 W3C 的理解与认识
(1)web 标准规范要求,书写标签必须闭合、标签小写、不乱嵌套,可提高搜索机器人对网页内容的搜索几率。—- SEO
(2)建议使用外链 css 和 js 脚本,从而达到结构与行为、结构与表现的分离,提高页面的渲染速度,能更快地显示页面的内容。
(3)样式与标签的分离,更合理的语义化标签,使内容能被更多的用户所访问、内容能被更广泛的设备所访问、更少的代码和组件, 从而降低维护成本、改版更方便
(4)不需要变动页面内容,便可提供打印版本而不需要复制内容,提高网站易用性
xhtml 和 html 有什么区别
HTML 是一种基本的 WEB 网页设计语言,XHTML 是一个基于 XML 的置标语言
最主要的不同:
XHTML 元素必须被正确地嵌套。
XHTML 元素必须被关闭。
标签名必须用小写字母。
XHTML 文档必须拥有根元素。
Doctype 严格模式与混杂模式-如何触发这两种模式,区分它们有何意义?
用于声明文档使用那种规范(html/Xhtml)一般为 严格 过度 基于框架的 html 文档
加入 XMl 声明可触发,解析方式更改为 IE5.5 拥有 IE5.5 的 bug
Doctype:(Document Type)文档类型,它位于文档中最前面的位置,处于标签之前。如果你想制作符合标准的页面,一个必不可少的关键组成部分就是 DOCTYPE 的声明。确定了正确的 Doctype,xhtml 里面的标识和 css 才能正常生效(也就是说它会很负责地告诉浏览器怎么解释你的这个标签和你写的 css)。既然它是一种声明,它的责任就是告诉浏览器文档使用哪种 html 或者 xhtml 规范。为了获取正确的 Doctype 声明,关键就是让 dtd 与文档所遵循的标准对应。例如:假定文档遵循的是 xhtml 1.0 strict,那么文档的 Doctype 声明就应该是相应的 dtd,如果 Doctype 声明指定的是 xhtml dtd,但文档包含的依旧是 html 标记就是不恰当的。类似的,如果文档包含的是 xhtml 1.0 strict 标记,但是 Doctype 声明指定是 html dtd 也是不恰当的。
一、选择什么样的 DOCTYPE?
xhtml 1.0 中有 3 种 dtd 声明可以选择,过渡性的(Transitional)、严格的(Strict)、框架的(Frameset)。下面我们来分别介绍:
1、过渡的:一种要求不很严格的,允许在 html 中使用 html 4.01 的标识(符合 xhtml 语法标准),过渡的 dtd 写法如下:
2、严格的:一种要求严格的 dtd,不允许使用任何表现层的标识和属性,严格的 dtd 写法如下:
3、框架的:一种专门针对框架页面所使用的 dtd,当页面中含有框架元素时,就要采用这种 dtd,写法如下:
PS:使用严格的 dtd 来制作页面当然是最理想的方式,但是对于还没有深入了解 web 标准的网页设计者,比较适合用过渡的 dtd,因为这种 dtd 还允许使用表现层的标识、元素和属性,比较适合大多数的网页制作人员(当然量力而为了!)
二、什么是标准模式与混杂模式?
不同文档模式主要影响 CSS 内容的呈现,尤其是浏览器对盒模型的解析,但在某些情况下也会影响到 JavaScript 的解释执行。
1、文档模式目前有四种:
混杂模式(quirks mode)
//让 IE 的行为与(包含非标准特性的)IE5 相同
标准模式(standards mode)
//让 IE 的行为更接近标准行为
准标准模式(almost standards mode)
//这种模式下的浏览器特性有很多都是符合标准的,不标准的地方主要体现在处理图片间隙的时候(在表格中使用图片时问题最明显)。
超级标准模式:
//IE8 引入的一种新的文档模式,超级文档模式可以让 IE 以其所有版本中最符合标准的方式来解释网页内容。
ps:
总的来看,混杂模式让 IE 像 IE5,标准模式使用 IE7 的呈现引擎,而超级标准模式则是 IE8 的默认文档模式。
什么是混杂模式呢?
混杂模式是一种比较宽松的向后兼容的模式。混杂模式通常模拟老式浏览器的行为,以防止老站点无法工作。
为什么会有混杂模式呢?
当年 Netscape4(译注:网景公司早期的浏览器)和 IE4(微软公司早期的浏览器)实现 CSS 机制时,并没有遵循 W3C 提出的标准。Netscape4 提供了糟糕的支持,而 IE4 虽然接近标准,但依旧未能完全正确的支持标准。尽管 IE 5 修复了 IE4 许多的问题(bugs),但是依然延续 CSS 实现中的其它故障(主要是盒模型(box model)问题)。为了保障自己的网站在各个浏览器上显示正确,网页开发者们不得不依据各个浏览器自身的规范来使用 css,因此大部分网站的 css 实现并不符合 W3C 规范的标准。然而随着标准一致性越来越重要,浏览器开发商不得不面临一个艰难的抉择:逐渐遵循 W3C 的标准是前进的方向。但是改变现有的 css,完全去遵循标准,会使许多旧网站或多或少受到破坏,如果浏览器突然以正确的方式解析现存的 css,陈旧的网站的显示必然会受到影响。所以,所有的浏览器都需要提供两种模式:混杂模式服务与旧式规则,而严格模式服务于标准规则
2、两种模式间的差异
对于这两种模式之间的差异,最显著的一个例子与 Windows 上 IE 专有的盒模型有关。在 IE 6 出现时,在标准模式中使用的是正确的盒模型,在混杂模式中使用的则是老式的专有盒模型。为了维持对 IE 5 和更低版本的向后兼容性,Opera 7 和更高版本也在混杂模式中使用有缺点的 IE 盒模型。
呈现方面的其他差异比较小,而且是与特定浏览器相关的,包括对于十六进制颜色值不需要#号、假设 CSS 中没有指定单位的长度的单位是像素,以及在使用关键字时将字号增加一级。
3、如何触发两种模式
DOCTYPE 不存在或形式不正确会导致 HTML 和 XHTML 文档以混杂模式呈现。
触发严格模式:
触发混杂模式
IE8 关闭超标准模式
行内元素有哪些?块级元素有哪些?CSS 的盒模型?
块级元素:div,p, h1-h6, form,ul , ol, body, hr
行内元素: a ,b ,br, i ,span, input, select
Css 盒模型:内容,border ,margin,padding 【 w3c 标准盒模型 content-box ie 怪异盒模型 border-box】
元素分类
行级元素 inline(display)
不独占一行,后面可跟其他
不可以通过 css 改变宽高
块级元素 block
独占一行
可以通过 css 改变宽高
行级块元素 inline-block
不独占一行,后面可跟其他
可以通过 css 改变宽高
5.CSS 引入的方式有哪些? link 和@import 的区别是?
内联 内嵌 外链 导入
区别 :同时加载
前者无兼容性,后者 CSS2.1 以下浏览器不支持
Link 支持使用 javascript 改变样式,后者不可
① 内联方式(很糟糕的书写方式)
直接在 html 标签中的 style 属性中添加 css。
② 嵌入方式
在 html 头部中的 < style >标签下书写 css 代码
③ 链接方式
在 html 的头部的 < head > 标签中使用 link 引入外部的 css 文件。
最常见也是最推荐的引入 css 的方式,使用这种方式,所有的 CSS 代码只存在于单独的 CSS 文件中,所以具有良好的可维护性。并且所有的 CSS 代码只存在于 CSS 文件中,CSS 文件会在第一次加载时引入,以后切换页面时只需加载 HTML 文件即可。
④ 导入样式
导入方式指的是使用 CSS 规则引入外部 CSS 文件。
link 和@import 的区别是?
区别 1:link 是 XHTML 标签,除了加载 CSS 外,还可以定义 RSS 等其他事务;
@import 属于 CSS 范畴,只能加载 CSS。
区别 2:link 引用 CSS 时,在页面载入时同时加载;@import 需要页面网页完全载入以后加载。
区别 3:link 是 XHTML 标签,无兼容问题;@import 兼容 IE5 以上
区别 4:link 支持使用 Javascript 控制 DOM 去改变样式;而@import 不支持改变样式。
6.CSS 选择符有哪些? 哪些属性可以继承? 优先级算法如何计算? 内联和 important 哪个优先级高?
- 选择符:id 选择器,元素选择器,class 选择器, 伪类选择器, 伪元素选择器, 标签选择器, 通配符选择器, 后代选择器,属性选择器([ ])根据属性名和属性值选中元素,伪类选择器 ( :)选中某些元素的某种状态
- 可以继承的属性: font-size font-family color
- 不可以继承的有: border padding margin background-color width height 等
- !important: infinity, 行内元素: 1000, id: 100, class|属性|伪类: 10, 标签|伪元素: 1, 通配符: 0
- !important 优先级最高
伪类
first-child(两个条件都满足)
选择元素的第一个子元素
选中 a 元素,并且 a 元素必须是第一个子元素
first-of-type
选中子元素中第一个指定类型的元素
选中的是子元素中第一个 a 元素
last-child
必须是 a 元素,必须是最后一个子元素
last-of-type
选中子元素中最后一个 a 元素
nth-child
选中指定的第几个子元素
必须是 a 元素,必须是第五个子元素
even:关键字,等同于 2n
odd: 关键字,等同于 2n+1
nth-of-type
选中指定的子元素中第几个某类型的元素
链接伪类选择器
顾名思义就是用于链接的伪类选择器。a 的伪类标签有:[a:link、a:visited、a:hover、a:active,]
1)link: 超链接未访问时的状态
2)visited: 超链接访问过后的状态
3)hover: 鼠标悬停状态
4)active:激活状态,鼠标按下状态
a:link
a:visited
a:hover
a:active
爱恨法则:love hate
link > visited > hover > active
伪元素( ::)
before
after
span::before
span::after
first-letter
选中元素中的第一个字母
first-line
选中元素中第一行的文字
selection
选中被用户框选的文字
他们不是真正的页面元素,html 没有对应的元素,但是其所有用法和表现行为与真正的页面元素一样,可以对其使用诸如页面元素一样的 css 样式,表面上看上去貌似是页面的某些元素来展现,实际上是 css 样式展现的行为,因此被称为伪元素
content 属性的值
字符串,字符串作为伪元素的内容添加到主元素中
注意:字符串中若有 html 字符串,添加到主元素后不会进行 html 转义,也不会转化为真正的 html 内容显示,而是会原样输出 - attr(attr_name), 伪元素的内容跟主元素的某个属性值进行关联,及其内容为主元素的某指定属性的值
好处:可以通过 js 动态改变主元素的指定属性值,这时伪元素的内容也会跟着改变,可以实现某些特殊效果,如图片加载失败用一段文字替换。 - url()/uri(), 引用外部资源,例如图片; - counter(), 调用计数器,可以不使用列表元素实现序号问题。
before 和:after 特点,用法?
伪元素不属于文档,所以 js 无法操作它
伪元素属于主元素的一部分,因此点击伪元素触发的是主元素的 click 事件
原文说块级元素才能有:before, :after,其实是不妥的,大部分行级元素也可以设置伪元素,但是像 img 可替换元素,因为其外观和尺寸有外部资源决定,那么如果外部资源正确加载,就会替换掉其内部内容,这时伪元素也会被替换掉,但是当外部资源加载失败时,设置的伪元素是可以起作用的。
伪元素的特点
优点:
减少 dom 节点数
让 css 帮助解决部分 js 问题,让问题变得简单
缺点:
不利于 SEO
无法审查元素,不利于调试
:before 和:after 常见使用场景
层叠
声明冲突:同一个样式,多次应用到同一个元素
层叠:解决声明冲突的过程,浏览器自动处理(权重计算)
- 比较重要性
重要性从高到底:
作者样式表:开发者书写的样式
1)作者样式表中的!important 样式 2) 作者样式表中的普通样式 3) 浏览器默认样式表中的样式
- 比较特殊性
看选择器
总体规则:选择器选中的范围越窄,越特殊
具体规则:通过选择器,计算出一个 4 位数(x x x x)
千位:如果是内联样式,记 1,否则记 0
百位:等于选择器中所有 id 选择器的数量
十位:等于选择器中所有类选择器、属性选择器、伪类选择器的数量
个位:等于选择器中所有元素选择器、伪元素选择器的数量 3. 比较源次序
代码书写靠后的胜出
!important +无穷
行间样式 1000
id 100
class/属性/伪类 10
标签/伪元素 1
通配符 0
7.前端页面有哪三层构成,分别是什么?作用是什么?
结构层 Html 表示层 CSS 行为层 js
8.css 的基本语句构成是?
选择器{属性 1:值 1;属性 2:值 2;……}
选择器:属性:属性值
9.你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么?
IE(IE内核) 火狐(Gecko) 谷歌(webkit) opear(Presto)
IE trident (三叉戟 )
Fire-fox Gecko(壁虎)
Google chrom webkit/blink(眨眼)
safari webkit
opera presto/blink
10.写出几种 IE6 BUG 的解决方法
1.双边距 BUG float 引起的 使用 display
2.像素问题 使用 float 引起的 使用 dislpay:inline -3px
3.超链接 hover 点击后失效 使用正确的书写顺序 link visited hover active
4.Ie z-index 问题 给父级添加 position:relative
5.Png 透明 使用 js 代码 改
6.Min-height 最小高度 !Important 解决’
7.select 在 ie6 下遮盖 使用 iframe 嵌套
8.为什么没有办法定义 1px 左右的宽度容器(IE6 默认的行高造成的,使用 over:hidden,zoom:0.08 line-height:1px)
11.标签上 title 与 alt 属性的区别是什么?
Alt 当图片不显示是 用文字代表。
Title 为该属性提供信息
1、含义不同
alt 是当图片不存在时的替代文字;title 是对图片的描述与进一步说明
2、在浏览器中的表现不同
在 firefox 和 ie8 中,当鼠标经过图片时 title 值会显示,而 alt 的值不会显示;只有在 ie6 中,当鼠标经过图片时 title 和 alt 的值都会显示。
对于网站 seo 优化来说,title 与 alt 还有最重要的一点:
搜索引擎对图片意思的判断,主要靠 alt 属性。所以在图片 alt 属性中以简要文字说明,同时包含关键词,也是页面优化的一部分。条件允许的话,可以在 title 属性里,进一步对图片说明。
12.描述 css reset 的作用和用途。
Reset 重置浏览器的 css 默认属性 浏览器的品种不同,样式不同,然后重置,让他们统一
每个浏览器都有一些自带的或者共有的默认样式,或造成一些布局上的困扰,css reset 的作用就是重置这些默认样式,使样式表现一致;
为了让页面获得浏览器跨浏览器的兼容性,需要用重置文件 css 代码覆盖浏览器默认的样式来统一样式。
13.解释 css sprites,如何使用。
Css 精灵 把一堆小的图片整合到一张大的图片上,减轻服务器对图片的请求数量
1、将用到的背景图片压缩为 zip 格式的压缩包
2、用 Css 图片拼合生成器将其拼合成一张图片,然后下载该图片
3、拼合完成后会生成相应的 Css 规则,该文件定位了每个图片的位置及图片的宽度和高度
4、在 Css 样式中定位背景图片,
5、需要 repeat 的图片也有可能不适合放在 sprite 中,当然如果只是单向的 repeat,如:repeat-x
或 repeat-y,只要保证图片在该方向铺满就可以了,但是如果 sprite 很大,会消耗较多
的内存,或者需要横向和纵向同时平铺的图片,也都不适合放在 sprite 中
14.浏览器标准模式和怪异模式之间的区别是什么?
盒子模型 渲染模式的不同
使用 window.top.document.compatMode 可显示为什么模式
(1 )盒模型:
在怪异模式下,盒模型为 IE 盒模型,
而在 W3C 标准的盒模型中为
(2)图片元素的垂直对齐方式:
对于inline元素和table-cell元素,标准模式下vertical-align属性默认取值为baseline,在怪异模式下,table单元格中的图片的vertical-align属性默认取值为bottom,因此在图片底部会有及像素的空间。
(3)
CSS 中,对于 font 的属性都是可以继承的,怪异模式下,对于 table 元素,字体的某些元素将不会从 body 等其他封装元素中继承得到,特别是 font-size 属性。
(4)内联元素的尺寸:
标准模式下,non-replaced inline 元素无法自定义大小,怪异模式下,定义这些元素的 width,height 属性可以影响这些元素显示的尺寸。
(5)元素的百分比高度:
a:CSS 中对于元素的百分比高度规定如下:百分比为元素包含块的高度,不可为负值,如果包含块的高度没有显示给出,该值等同于 auto,所以百分比的高度必须在父元素有高度声明的情况下使用。
b:当一个元素使用百分比高度时,标准模式下,高度取决于内容变化,怪异模式下,百分比高度被正确应用。
(6)元素溢出的处理:
标准模式下,overflow 取默认值 visible,在怪异模式下,该溢出会被当做扩展 box 来对待,即元素的大小由其内容决定,溢出不会裁减,元素框自动调整,包含溢出内容。
3. 屏幕正中间有一个元素 A,元素 A 中有文字 A,随着屏幕宽度的变化,始终需要满足下列要求:(请用 html css 实现)
1. A元素垂直居中于屏幕中央2. A元素距离屏幕左右边距各10px3. A元素里面的文字A front-size:20px;水平垂直居中4. A元素的高度始终是A元素宽度的50%(如果搞不定可以实现为a元素的高度,固定为200px)
<style>body {padding: 0;margin: 0;}.A {position: fixed;top: 0;bottom: 0;margin: auto 0;font-size: 20px;text-align: center;height: calc(50vw - 10px);left: 10px;right: 10px;background-color: aqua;}</style><div class="A">A</div>
4. ul 内部除最后一个 li 以外设置右边框效果
ul li:not(:last-child) {border-right: 1px solid #000;}
5. 一个标签的 class 样式的渲染顺序, ID, class,标签,伪类的优先级
id > class = 伪类 > 标签
内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 元素选择器 = 关系选择器 = 伪元素选择器 > 通配符选择器
5.如何用 jQuery 给一个标签添加一个属性?
$(selector).attr(key, value);$(selector).prop(key, value);// 最好把这两个方法的区别讲述一下
$(‘’).attr()返回的是 html 对象
$(‘’).prop()返回的是 DOM 对象
attr():
attr() 方法设置或返回被选元素的属性和值。
当该方法用于返回属性值,则返回第一个匹配元素的值。
当该方法用于设置属性值,则为匹配元素设置一个或多个属性/值对。
方法操作 adio(checkbox)之类的控件,让其选中的时候,其控件选中的值不会随之改变。即只能控制其选中,不能控制其取消选中;
类似于$(“…”).attr(“checked”);返回的是’checked’或者 undefined
如果有相应的属性,返回的是该属性,如果没有则返回 undefined
prop():
prop() 方法设置或返回被选元素的属性和值。
当该方法用于返回属性值时,则返回第一个匹配元素的值。
当该方法用于设置属性值时,则为匹配元素集合设置一个或多个属性/值对。
方法操作 radio(checkbox)之类的控件,让其选中的时候,其控件选中的值也会随之改变。即既可以控制其选中,也能控制其取消选中;
类似于$(“…”).attr(“checked”);返回的是 true 或者 false
如果有相应的属性,返回的是该属性,如果没有则返回空串
attr 和 prop 的使用场景:
1.添加属性名称该属性就会生效应该使用 prop();
2.是有 true,false 两个属性使用 prop();(如’checked’,’selected’,’disabled’等)
3.其他则使用 attr();
//全选反选$("#allcheck").click(function () {$("#last input:checkbox").each(function (index, domEle) {$(domEle).prop("checked", "true");});});$("#invertcheck").click(function () {$("#last input:checkbox").each(function (index, domEle) {$(domEle).prop("checked", !$(domEle).prop("checked"));});});$("#unallcheck").click(function () {$("#last input:checkbox").each(function (index, domEle) {$(domEle).removeAttr("checked");});});
10.虚线边框
border: 1px dashed #000;
11.怎么实现在 jQuery 中找到所在的同辈元素?
$(selector).siblings()
$(“#myELement”) 选择 id 值等于 myElement 的元素,id 值不能重复在文档中只能有一个 id 值是 myElement 所以得到的是唯一的元素
$(“div”) 选择所有的 div 标签元素,返回 div 元素数组
$(“.myClass”) 选择使用 myClass 类的 css 的所有元素
$(“*”) 选择文档中的所有的元素,可以运用多种的选择方式进行联合选择:例如$(“#myELement,div,.myclass”)
层叠选择器:$(“form input”) 选择所有的 form 元素中的 input 元素
$(“#main > *”) 选择 id 值为 main 的所有的子元素
$(“label + input”) 选择所有的 label 元素的下一个 input 元素节点,经测试选择器返回的是 label 标签后面直接跟一个 input 标签的所有 input 标签元素
$(“#prev ~ div”) 同胞选择器,该选择器返回的为 id 为 prev 的标签元素的所有的属于同一个父元素的 div 标签
基本过滤选择器:$(“tr:first”) 选择所有 tr 元素的第一个
$(“tr:last”) 选择所有 tr 元素的最后一个
$(“input:not(:checked) + span”)
过滤掉:checked 的选择器的所有的 input 元素$(“tr:even”) 选择所有的 tr 元素的第 0,2,4… …个元素(注意:因为所选择的多个元素时为数组,所以序号是从 0 开始)
$(“tr:odd”) 选择所有的 tr 元素的第 1,3,5… …个元素
$(“td:eq(2)”) 选择所有的 td 元素中序号为 2 的那个 td 元素
$(“td:gt(4)”) 选择 td 元素中序号大于 4 的所有 td 元素
$(“td:ll(4)”) 选择 td 元素中序号小于 4 的所有的 td 元素
$(“:header”)
$(“div:animated”)
内容过滤选择器:$(“div:contains(‘John’)”) 选择所有 div 中含有 John 文本的元素
$(“td:empty”) 选择所有的为空(也不包括文本节点)的 td 元素的数组
$(“div:has(p)”) 选择所有含有 p 标签的 div 元素
$(“td:parent”) 选择所有的以 td 为父节点的元素数组
可视化过滤选择器:$(“div:hidden”) 选择所有的被 hidden 的 div 元素
$(“div:visible”) 选择所有的可视化的 div 元素
属性过滤选择器:$(“div[id]”) 选择所有含有 id 属性的 div 元素
$(“input[name=’newsletter’]”) 选择所有的 name 属性等于’newsletter’的 input 元素
$(“input[name!=’newsletter’]”) 选择所有的 name 属性不等于’newsletter’的 input 元素
$(“input[name^=’news’]”) 选择所有的 name 属性以’news’开头的 input 元素
$(“input[name$=’news’]”) 选择所有的 name 属性以’news’结尾的 input 元素
$(“input[name*=’man’]”) 选择所有的 name 属性包含’news’的 input 元素
$(“input[id][name$=’man’]”) 可以使用多个属性进行联合选择,该选择器是得到所有的含有 id 属性并且那么属性以 man 结尾的元素
子元素过滤选择器:$(“ul li:nth-child(2)”),$(“ul li:nth-child(odd)”),$(“ul li:nth-child(3n + 1)”)
$(“div span:first-child”) 返回所有的 div 元素的第一个子节点的数组
$(“div span:last-child”) 返回所有的 div 元素的最后一个节点的数组
$(“div button:only-child”) 返回所有的 div 中只有唯一一个子节点的所有子节点的数组
表单元素选择器:
$(“:input”) 选择所有的表单输入元素,包括 input, textarea, select 和 button
$(“:text”) 选择所有的 text input 元素
$(“:password”) 选择所有的 password input 元素
$(“:radio”) 选择所有的 radio input 元素
$(“:checkbox”) 选择所有的 checkbox input 元素
$(“:submit”) 选择所有的 submit input 元素
$(“:image”) 选择所有的 image input 元素
$(“:reset”) 选择所有的 reset input 元素
$(“:button”) 选择所有的 button input 元素
$(“:file”) 选择所有的 file input 元素
$(“:hidden”) 选择所有类型为 hidden 的 input 元素或表单的隐藏域
表单元素过滤选择器:$(“:enabled”) 选择所有的可操作的表单元素
$(“:disabled”) 选择所有的不可操作的表单元素
$(“:checked”) 选择所有的被 checked 的表单元素
$(“select option:selected”) 选择所有的 select 的子元素中被 selected 的元素
选取一个 name 为”S_03_22″的 input text 框的上一个 td 的 text 值
$(”input[@name =S_03_22]“).parent().prev().text()
名字以”S_”开始,并且不是以”_R”结尾的
$(”input[@name ^=’S ‘]“).not(”[@name _ $=’ _R’]“)
一个名为 radio_01 的 radio 所选的值
$(”input[@name =radio_01][@checked]“).val();
$(“A B”) 查找 A 元素下面的所有子节点,包括非直接子节点
$(“A>B”) 查找 A 元素下面的直接子节点
$(“A+B”) 查找 A 元素后面的兄弟节点,包括非直接子节点
$(“A~B”) 查找 A 元素后面的兄弟节点,不包括非直接子节点
- $(“A B”) 查找 A 元素下面的所有子节点,包括非直接子节点例子:找到表单中所有的 input 元素 HTML 代码:
Name:
Newsletter:
jQuery 代码:$(“form input”)
结果:[ , ]
- $(“A>B”) 查找 A 元素下面的直接子节点例子:匹配表单中所有的子级 input 元素。
HTML 代码:
Name:
Newsletter:
jQuery 代码:$(“form > input”)
结果:[ ]
- $(“A+B”) 查找 A 元素后面的兄弟节点,包括非直接子节点例子:匹配所有跟在 label 后面的 input 元素
HTML 代码:
Name:
Newsletter:
jQuery 代码:$(“label + input”)
结果:[ , ]
- $(“A~B”) 查找 A 元素后面的兄弟节点,不包括非直接子节点
例子:找到所有与表单同辈的 input 元素
HTML 代码:
Name:
Newsletter:
jQuery 代码:$(“form ~ input”)
结果:[ ]
12. 简述 h5 中新增的 canvas,svg,audio 标签的作用
canvas 和 svg 是用于绘图的标签, canvas 绘图技术主要是通过 js 脚本语言进行绘制,而 svg 主要是 html 和 css 配合进行绘制,一般我们会用 canvas 绘制大型的图表,游戏等,而用 svg 绘制一些小图标,svg 绘制出来的是一个矢量图而 canvas 绘制出来的是位图
audio 是音频标签,主要用来做一些音频播放的效果,js 中还针对 audio 专门有一些控制播放的接口供我们使用,audio 标签在使用的时候如果设置自动播放在谷歌浏览器上面会又报错,解决这个问题可以等待音频资源加载过来之后通过 js 控制播放
13. 简述如何通过 css 实现响应式布局的方式
通过媒体查询,相对单位,以及弹性布局的方式可以实现响应式布局
通过媒体查询和弹性布局为不同宽度的设备设置不同的布局样式。
使用相对单位,为不同的设备设置不同的大小
栅格
网格
1.HTML、CSS、js作用是什么?
Html: 超文本标记语言 主要用来做页面结构划分
CSS: 层叠样式表 主要用来做页面的样式的渲染
Js: javascript 主要用来实现页面的交互行为效果
flex 有哪些属性
css 画一个三角形
<style type="text/css">/* css3绘制三角形 */.triangle{width: 0px; /*设置宽高为0,所以div的内容为空,从才能形成三角形尖角*/height: 0px;border-bottom: 200px solid #00a3af;border-left: 200px solid transparent; /*transparent 表示透明*/border-right: 200px solid transparent;}</style><div class="triangle"></div>
css 画等边三角形
我们首先绘制出一个一般的三角形,那么代码应该是如此的
<body><div class="triangle"></div></body>.triangle {border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 40px solid lightblue;width: 0px;height: 0px;}对于等边三角形我们知道,高是边的sqrt(3)/2倍,所以如果高为40的话,两边就是差不多23,所以我们可以得到.triangle {border-left: 23px solid transparent;border-right: 23px solid transparent;border-bottom: 40px solid lightblue;width: 0px;height: 0px;}<body><div class="triangle"></div></body>
画个菱形
[https://blog.csdn.net/qq_39897978/article/details/90172695]
11. 盒模型 top:50%,margin-top:50%,相对什么计算的
父元素的高度
12. 怎么求的盒子的总尺寸
计算整个盒子所占空间的总尺寸:
- 标准合模型:margin + border + padding + width/height
- ie6 混杂盒模型: margin + width/height
13. clientWidth,offsetWidth,scrollWidth 的区别
- clientWidth 元素可视区域的宽度 不包含滚动条
- offsetWidth 元素所占据的宽度 包含滚动条
- scrollWidth 元素没有滚动条时的宽度 包含里面所有内容展开的宽度
通过一个 demo 测试这三个属性的差别。
说明:
scrollWidth:对象的实际内容的宽度,不包边线宽度,会随对象中内容超过可视区后而变大。
clientWidth:对象内容的可视区的宽度,不包滚动条等边线,会随对象显示大小的变化而改变。
offsetWidth:对象整体的实际宽度,包滚动条等边线,会随对象显示大小的变化而改变。
该 demo 就在页面中放一个 textarea 元素,采用默认宽高显示。
情况 1:
元素内无内容或者内容不超过可视区,滚动不出现或不可用的情况下。
scrollWidth=clientWidth,两者皆为内容可视区的宽度。
offsetWidth 为元素的实际宽度。
情况 2:
元素的内容超过可视区,滚动条出现和可用的情况下。
scrollWidth>clientWidth。
scrollWidth 为实际内容的宽度。
clientWidth 是内容可视区的宽度。
offsetWidth 是元素的实际宽度。
js
1.javascript 的 typeof 返回哪些数据类型
Object number function boolean underfind
9. 数据类型
js 中基本数据类型有: Number, String, Undefined, Object, Function, Boolean 这六种
常用的数据类型有: 除了上面六个基本数据类型之外还有 null, array(这两种数据属于基本类型中的 object 类型)
2.例举 3 种强制类型转换和 2 种隐式类型转换?
强制(parseInt,parseFloat,number)
隐式(== – ===)
强制:String(),Boolean() Number()parseInt() parseFloat()
隐式 + - ==
数组常见 API
连接和拼接
concat() 连接一个或多个数组 返回新数组
var myArr1 = [1, 2, 3];var myArr2 = [4, 5, 6];var myArr3 = [7, 8, 9];console.log(myArr1.concat(myArr2, myArr3)); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
join() 将数组元素拼接成字符串(参数接收拼接符)
var arr1 = [1, 2, 3];console.log(arr1.join("到")); //'1 到 2 到 3'--以'到'对 arr1 数组的元素进行拼接var arr2 = ["a", "b", "c"];console.log(arr2.join("")); //'abc'--以空字符串为拼接符对 arr2 数组进行拼接
反转和排序
reverse() —将数组元素进行反转
var arr = [1, 2, 3, 4, 5, "a"];console.log(arr.reverse()); //['a',5,4,3,2,1]
sort()–数组元素排序(参数接收一个排序函数)
var arr= [11,99,33,4,4,35,3,46,9];console.log(arr.sort((a,b)=>a-b); //升序[3, 4, 4, 9, 11, 33, 35, 46, 99]console.log(arr.sort((a,b)=>b-a); //降序[99, 46, 35, 33, 11, 9, 4, 4, 3]
删除和添加
push()–向数组的最后面添加元素(返回数组长度, 会改变原数组)
//后插(返回数组长度,会改变原数组)var arr1 = [1, 2, 3];var arr2 = arr1.push(9, 10, 11);console.log(arr2); //6(返回数组长度)console.log(arr1); //改变原数组[1,2,3,9,10,11]
pop()–删除数组的最后面的元素(返回被删除的元素)
//后删(返回被删除的元素,会改变原数组)var arr1 = [1, 2, 3, 4, 5];var arr2 = arr1.pop();console.log(arr2); //5(返回被删除的元素)console.log(arr1); //[1,2,3,4]
unshift()–在数组的最前面插入元素(返回数组长度, 会改变原数组)
//前插(返回数组长度, 会改变原数组)var arr1 = [1, 2, 3, 4, 5];var arr2 = arr1.unshift(0, "a", "b");console.log(arr2); //8(返回新数组长度)console.log(arr1); //[0, 'a', 'b', 1, 2, 3, 4, 5]
shift()–删除数组的最前面的元素(返回被删除的元素)
//前删—返回被删除的元素,会改变原数组
var arr1 = [1, 2, 3, 4, 5];var arr2 = arr1.shift();console.log(arr2); //1(返回被删除的元素)console.log(arr1); //[2,3,4,5]
增删改查四合一
splice()法始终会返回一个数组,该数组包含从原始数组中删除的项
如果没有删除任何项,则返回一个空数组)
//删除:需指定 2 个参数,要删除的第一项位置和要删除的项数var colors = ["red", "green", "blue"];var removed = colors.splice(0, 1); //位置下标,删除个数console.log(colors); //green,blueconsole.log(removed); //red//返回被删除数据的数组//插入:需提供 3 个参数,起始位置、0(要删除的项数)和要插入的项,如要插入多个项// ,再传入第四,五...var removed = colors.splice(1, 0, "black"); //位置下标,删除个数,插入数据console.log(colors); //green,black,blueconsole.log(removed); // 返回空数组//替换:需指定 3 个参数,起始位置、要删除的项数和要插入的任意数量的项var removed = colors.splice(0, 2, "yellow", "white"); //位置下标,删除个数,插入数据console.log(colors); //yellow,white,blueconsole.log(removed); //red,green
截取数组
slice()含头不含尾 返回值为截取之后的数组
var arr = [1, 2, 3];var newArr = arr.slice(0, 2);console.log(newArr); //[1,2]console.log(arr); //[1,2,3]
重新包装数组
map()对元素重新组装,返回值生成新数组
var arr = [1, 2, 3, 5];var arr2 = arr.map(function (item, index) {// 将元素重新组装并返回return "<b>" + item + "</b>";});console.log(arr2); //["<b>1</b>", "<b>2</b>", "<b>3</b>", "<b>5</b>"]
过滤数组
filter()过滤符合条件的元素 返回过滤后的数组
var arr = [1, 2, 3, 5];var arr2 = arr.filter(function (item, index) {// 通过某个条件过滤数组if (item > 2) {return true;}});console.log(arr2);
6. 产生一个不重复的随机数组
参考答案:
function randomArr(len, min, max) {var result = [];while (result.length < len) {var num = Math.floor(Math.random() * (max - min) + min);if (result.indexOf(num) === -1) {result.push(num);}}return result;}
实现一个函数,给定一个非负数整数 num,反复将各个位上的数字相加,直到结果为一位数。
function addDigits(num) {if (!num) {return null;}const str = num.toString();let res = 0;for (let i = 0; i < str.length; i++) {res += +str.substring(i, i + 1);}if (res.toString().length === 1) {return res;} else {return addDigits(res);}}console.log(addDigits(546)); // 6 因为 5 + 4 + 6 = 15 => 1 + 5 = 6
1.独一无二的次数:
给你一个数组,计算每个数出现的次数,如果每个数组返回的数都是独一无二的就返回 true 相反则返回的 flase
var arr = [1, 2, 3, 4, 5, 6];function count(arr) {var result = [];var obj = {};arr.forEach(function (item) {if (!obj[item]) {obj[item] = 1;} else {obj[item]++;}});for (var prop in obj) {if (obj.hasOwnProperty(prop)) {result.push(obj[prop]);}}return result.length === arr.length;}console.log(count(arr));
2.字符串压缩
给你个字符串,利用反复出的字符串的 例如 aaabbbdddddfff 转化为 a3b3d4f3 假设只包含大小写字母
function zipStr(str) {var reg = /(\w)(\1*)/g;console.log(str.replace(reg, function ($, $1, $2) {console.log($1, $2);return $1 + ($2.length + 1);}));}
3.删除给定值 val
给你一个链表 1>2>3>6>4>5>6 删除 val=6 得到 1>2>3>4>5>
function deleteNode(node, targetVal) {var p = node;var result = node;if (p.val === targetVal && p.next) {result = p.next;} else if (p.val === targetVal) {result = null;}if (p.next.val === targetVal && p.next.next) {p.next = p.next.next;} else if (p.next.val === targetVal) {p.next = null;}while (p.next.next) {if (p.next.val === targetVal) {p.next = p.next.next;}p = p.next;}if (p.next.val === targetVal) {p.next = p.next.next ? p.next.next : null;}return result;}
2. var arr=[1,2,3,”1”,1,2,”a”,”b”] var brr=[1,2,”b”] 输出[3,”1’,”a”] 注:arr 先去重
var arr = [1, 2, 3, "1", 1, 2, "a", "b"];var brr = [1, 2, "b"];var result = [];// for (var i = 0; i < arr.length; i ++) {// if (brr.indexOf(arr[i]) === -1) {// result.push(arr[i]);// }// }var uniqueArr = [];for (var i = 0; i < arr.length; i++) {if (uniqueArr.indexOf(arr[i]) === -1) {uniqueArr.push(arr[i]);if (brr.indexOf(arr[i]) === -1) {result.push(arr[i]);}}}
3.split() join() 的区别
前者是切割成数组的形式,后者是将数组转换成字符串
定义和用法
split() 方法用于把一个字符串分割成字符串数组。
String.split() 执行的操作与 Array.join 执行的操作是相反的。
join() 方法用于把数组中的所有元素放入一个字符串。
元素是通过指定的分隔符进行分隔的。
实例:
var str1=”上海@北京”;
var str2=[“上海”,”北京”];
str1.split(“@”);//通过 “@”分割符把字符转数组 “上海@北京” ==》[“上海”,”北京”]
str2.join(“,”);//在通过” , “ 把数组转字符串 [“上海”,”北京”] ==》 “上海,北京”
13.map 和 forEach 的区别?
orEach(),map(),filter()和 reduce(),find()区别
这几种都是数组的遍历方法方法用法上容易混淆,这里写下我的理解
forEach()
支持:除去(ie678)
forEach 只是简单的将数组遍历,类似于军人接受检阅,但是检阅结束并不会返回任何东西,也不会改变原数组,forEach 的返回永远是 undefind
let total = null;let arr = [1, 2, 3];let result = arr.forEach((a) => {total += a;return a + 1;});console.log(result); //undefindconsole.log(total); //6console.log(arr); //[1,2,3]
forEach 与 for…in 在数组的处理方式上是一致的,区别在于 for…in 可以遍历对象上的所有可枚举属性,forEach 只是数组上的一个函数
map()
支持:除去(ie678)
map 可以比喻成去超市买东西,你拿了一个物品清单,然后服务员帮你拿好东西,返回给你一个物品的新数组,但是对清单原数组不发生改变
let goodsList = ["apple", "pear", "orange"];let goods = goodList.map((good) => {return superMarket.getGoods(good);});console.log(goodsList); //['apple','pear','orange']console.log(goods); //三个物品对象
reduce()
支持:除去(ie678)
reduce 主要是为了对所有数组进行累加,最后返回一个值,不改变原数组,类似让男生把排队把把身高加起来
let totalHight = boys.reduce((counetedHight /*当前的高度总和*/, boy /*数组当前遍历到的元素*/) => {return countedHigh + boy.hight;});
filter()
支持:除去(ie678)
filter()就是让男生排队身高高于 180 的就归入到新数组中,最后返回一个里面装着所有 180+的男生的数组,不改变原数组
let highBoys = boys.filter((boy/数组当前遍历到的元素/)=> {return boy.highy >= 180}console.log(highBoys) // [boy,boy,....]
find()
支持:ie 各版本都不支持
find()函数就是你去找人想找一个 180 的男生帮你去搬砖,只要找到第一个就停止遍历,因为只要找一个,为了减少无用的操作,虽然其他方法也能实现同样的要过,但是最好用 find()
let highBoy = boys.find((boy/数组当前遍历到的元素/)=> {return boy.high >= 180}console.log(highBoy) // boy
总结
数组以上的方法都不会该改变原数组,但是都不兼容 ie 的低版本,所以如果有兼容需求就避免使用,或者用 babal-polyfill 编译成替代写法
相同点:
1, 都是循环遍历数组中的每一项
2, 每次执行匿名函数都支持三个参数,参数分别为 item(当前的每一项),index(索引值),arr(原数组),
3, 匿名函数中的 this 都是指向 window
4, 只能遍历数组
不同点
1, map 会分配内存空间存储新数组并返回,forEach 不会返回数组
2, forEach 允许 callback 更改原始数组的元素,map 返回新的数组
forEach 针对每个元素执行提供的函数,对数组的操作会改变原数组
var arr1 = [0, 2, 4, 6, 8];var newArr1 = arr1.forEach(function (item, index, arr1) {console.log(this);console.log(arr1);arr1[index] = item / 2;}, this);console.log(arr1);console.log(newArr1);
使用场景:并不打算改变数据的时候,而只是想用数据做一些事情,比如存入数据库或者打印出来;
Map
Map 不会改变原数组的值,返回一个新数组,新数组的值为原数组调用函数处理之后的值
var arr = [0, 2, 4, 6, 8];var newArr = arr.map(function (item, index, arr) {console.log(this);console.log(arr);return item / 2;}, this);console.log(newArr);
使用场景:map 更适用于要改变数据值的时候,不仅仅在于它更快,而是返回一个新的数组
判断数组的几种方法:
1、Instanceof 运算符(例:console.log(arr instanceof Array))
2、 Constructor 这个属性返回对象相应的构造函数(…(arr.constructor == Array))
3、调用原型链上的 toString 方法.call 截取一段判断是否为数组类型
4、数组自带的 isArray(…(Array.isArray(arr)))
let arr = []1. instanceofarr isntanceof Array2. __proto__arr.__proto__ === Array.prototypeArray.prototype.isPrototypeOf(arr) ===Array.prototypeObject.getPrototypeOf(arr) === Array.prototypeObject.getPrototypeOf()3. constructorarr.constructor === Array4. Object.prototype.toStringObject.prototype.toString.call(arr) === '[object Array]'5. Array.isArrayArray.isArray(arr)
其中方法 1,2,3 主要是通过原型去判断的, 4 是通过 object 类型的副属性 class 去判断的,其中函数的 class 是 Function,结果是[object Function], 普通的对象是 Object,结果是[object Object],5 是 es6 新增的方法
在 javascript 中什么是伪数组?如何将伪数组转换为标准数组
- 按照索引排序的
- 具有 length 属性
- 没有数组的 push pop shift 等方法
将伪数组转换为标准数组: Array.prototype.slice.call() 或者 Array.form()
伪数组(类数组):
无法直接调用数组方法或期望 length 属性有什么特殊的行为,不具有数组的 push,pop 等方法,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的 argument 参数,还有像调用 getElementsByTagName,document.childNodes 之类的,它们都返回 NodeList 对象都属于伪数组。可以使用 Array.prototype.slice.call(fakeArray)将数组转化为真正的 Array 对象。
function log() {var args = Array.prototype.slice.call(arguments); //为了使用unshift数组方法,将argument转化为真正的数组args.unshift("(app)");console.log.apply(console, args);}
这里把符合以下条件的对象称为伪数组:
1,具有 length 属性
2,按索引方式存储数据
3,不具有数组的 push,pop 等方法
如
1,function 内的 arguments 。
2,通过 document.forms,Form.elements,Select.options,document.getElementsByName() ,
document.getElementsByTagName() ,childNodes/children 等方式获取的集合(HTMLCollection,NodeList)等。
3,特殊写法的对象 ,如
Js 代码 收藏代码
var obj={};
obj[0] = “一”;
obj[1] = “二”;
obj[2] = “三”;
obj.length = 3;
它们不具有数组的一些方法如 push, pop, shift, join 等。有时候需要将这些伪数组转成真正的数组,这样可以使用 push, pop 等方法。以下是工具函数 makeArray
Js 代码
function makeArray(obj) {var rs = [],len = obj.length;try {rs = [].slice.call(obj, 0);} catch (e) {//for IEfor (var i = 0; (j = obj[i++]); ) {rs.push(j);}}return rs;}
以下分别测试以上三种伪数组:
//定义一个函数 fun,内部使用 makeArray 将其 arguments 转换成数组//定义一个函数 fun,内部使用 makeArray 将其 arguments 转换成数组function fun() {var ary = makeArray(arguments);alert(ary.constructor);}//调用fun(3, 5);//假设页面上有多个段落元素 pvar els = document.getElementsByTagName("p");var ary1 = makeArray(els);alert(ary1.constructor);//特殊的 js 对象(如 jquery 对象)var obj = {};obj[0] = "一";obj[1] = "二";obj[2] = "三";obj.length = 3;var ary2 = makeArray(obj);alert(ary2.constructor);
6. array 和 object 的区别
数组和对象都属于引用类型的值,都存储在堆内存当中(这里很有可能被问到堆栈内存的区别,可以适当的扩展)
array 数组是有序的,而 object 是无序的。数组属于特殊的对象,因此对象含有的方法,数组也拥有,而数组拥有的方法,对象不一定含有
例如:
数组默认含有长度属性length, 含有很多遍历方法例如forEach map filter reduce 等方法,对象没有对象调用属性的方式有两: obj.propName / obj[propName]数组调用属性的方式只有一种: arr[index]
一、区别:
数组表示有序数据的集合,对象表示无需数据的集合。如果数据顺序很重要的话,就用数组,否则就用对象的好。
数组的数据没有名称’name’ 对象的数据有名称 ‘name’ 但是在很多编程语言中有个叫关联数组的,这种数组中的数据是有名称的。
二、如何区分 array 和 object:
var obj = {“k1”:”v1”};
var arr = [1,2];
1:通过 isArray 方法
使用方法: Array.isArray(obj); //obj 是检测的对象
2:通过 instanceof 运算符来判断
instanceof运算符左边是子对象(待测对象),右边是父构造函数(这里是Array),
具体代码:
console.log(“对象的结果:”+(obj instanceof Array));
console.log(“数组的结果:”+(arr instanceof Array));
3::使用 isPrototypeOf()函数
原理:检测一个对象是否是 Array 的原型(或处于原型链中,不但可检测直接父对象,还可检测整个原型链上的所有父对象)
使用方法: parent.isPrototypeOf(child)来检测 parent 是否为 child 的原型;
isPrototypeOf()函数实现的功能和 instancof 运算符非常类似;
具体代码:
Array.prototype.isPrototypeOf(arr) //true 表示是数组,false 不是数组
4:利用构造函数 constructor
具体代码:
console.log(obj.constructor == Array); //falseconsole.log(arr.constructor == Array); //true
5:使用 typeof(对象)+类型名结合判断:
具体代码:
function isArrayFour(arr) {if (typeof arr === "object") {if (arr.concat) {return "This is Array";} else {return "This not Array";}}}console.log(typeof obj); //"object"console.log(typeof arr); //"array"console.log(isArrayFour(obj)); //This not Arrayconsole.log(isArrayFour(arr)); //This is Array
1.请实现一个模块 math,支持链式调用 math.add(2,4).minus(3).times(2);
class Math {constructor(value) {let hasInitValue = true;if (value === undefined) {value = NaN;hasInitValue = false;}Object.defineProperties(this, {value: {enumerable: true,value: value,},hasInitValue: {enumerable: false,value: hasInitValue,},});}add(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv + cv, init);return new Math(value);}minus(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv - cv, init);return new Math(value);}times(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv * cv, init);return new Math(value);}divide(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv / cv, init);return new Math(value);}toJSON() {return this.valueOf();}toString() {return String(this.valueOf());}valueOf() {return this.value;}[Symbol.toPrimitive](hint) {const value = this.value;if (hint === "string") {return String(value);} else {return value;}}}export default new Math();
3.请实现一个深拷贝、浅拷贝
[https://www.jianshu.com/p/35d69cf24f1f]
深浅拷贝主要是针对对象来说的, 因为对象是一个引用值, 如果拷贝的时候直接赋值的话就会对原始对象有影响深拷贝是说复制出来的内容完全独立,而浅拷贝是表面上一样,对于属性类型为对象类型的数据并不是独立的.**深浅拷贝的实现方式说一下伪代码**
手撕深拷贝
<!--普通的深层拷贝函数: -->function deepCopy( source ) {if (!isObject(source)) return source; //如果不是对象的话直接返回let target = Array.isArray( source ) ? [] : {} //数组兼容for ( var k in source ) {if (source.hasOwnProperty(k)) {if ( typeof source[ k ] === 'object' ) {target[ k ] = deepCopy( source[ k ] )} else {target[ k ] = source[ k ]}}}return target}function isObject(obj) {return typeof obj === 'object' && obj !== null}// 缺点:(1)无法保持引用(2)当数据的层次很深,会栈溢出<!--防栈溢出函数-->function cloneLoop(x) {const root = {};// 栈const loopList = [{parent: root,key: undefined,data: x,}];while(loopList.length) {// 深度优先const node = loopList.pop();const parent = node.parent;const key = node.key;const data = node.data;// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素let res = parent;if (typeof key !== 'undefined') {res = parent[key] = {};}for(let k in data) {if (data.hasOwnProperty(k)) {if (typeof data[k] === 'object') {// 下一次循环loopList.push({parent: res,key: k,data: data[k],});} else {res[k] = data[k];}}}}return root;}<!--最简单的深拷贝方式-->function clone(obj) {return JSON.parse(JSON.stringify(obj));}
function deepClone(origin, target) {var target = target || {};var toStr = Array.prototype.toString;var strArr = "[object Array]";for (var prop in origin) {if (origin.hanOwnProperty(prop)) {if (origin[prop] !== null && typeof origin[prop] == "object") {target[prop] = toStr.call(origin[prop]) === strArr ? [] : {};deepClone(origin[prop], target[prop]);} else {target[prop] = origin[prop];}}}return target;}
6.[“1”,”2”,”3”,”4”,”5”,6,7,8,9,10,11,12,13,14,15].map(parselnt);
[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19]<!--parseInt(数字, 该数字为几进制的数)-->
数组去重, 能写几种写几种
function oSort(arr) {var result = {};var newArr = [];for (var i = 0; i < arr.length; i++) {if (!result[arr]) {newArr.push(arr);result[arr] = 1;}}return newArr;}
//遍历数组法: 最简单数组去重法function unique1(array){var result = []; //一个新的临时数组//遍历当前数组for(var i = 0; i < array.length; i++){//如果当前数组的第i已经保存进了临时数组,那么跳过,//否则把当前项push到临时数组里面if (result.indexOf(array[i]) == -1) result.push(array[i]);}return result;}// 对象键值对法: 速度最快, 占空间最多(空间换时间)function unique2(array){var obj = {}, result = [], len = array.length, val, type;for (var i = 0; i < len; i++) {val = array[i];type = typeof val;if (!obj[val]) {obj[val] = true;result.push(val)}}return result;}// 数组下标判断法function unique3(array){var n = [array[0]]; //结果数组//从第二项开始遍历for(var i = 1; i < array.length; i++) {//如果当前数组的第i项在当前数组中第一次出现的位置不是i,//那么表示第i项是重复的,忽略掉。否则存入结果数组if (array.indexOf(array[i]) == i) n.push(array[i]);}return n;}//排序后相邻去除法 将相同的值相邻,然后遍历去除重复值function unique4(array){array.sort();var re=[array[0]];for(var i = 1; i < array.length; i++){if( array[i] !== re[re.length-1]){re.push(array[i]);}}return re;}// 优化遍历数组法 思路:获取没重复的最右一值放入新数组function unique5(array){var r = [];for(var i = 0, l = array.length; i < l; i++) {for(var j = i + 1; j < l; j++)if (array[i] === array[j]) j = ++i;r.push(array[i]);}return r;}// es6 set去重funciton unique6(arr) {return Array.from(new Set(arr))<!--或者-->return [...new Set(arr)]}// 利用includes实现去重function unique7(arr) {var result = [];for (var i = 0; i < arr.length; i ++) {if (!result.includes(arr[i)) {result.push(arr[i);}}return result;}// 利用reduce + includes 去重function unique8(arr) {return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);}// 利用Map结构去重function unique9 (arr) {let map = new Map();let result = [];for (var i = 0; i < arr.length; i++) {if (map.has(arr[i])) {map.set(arr[i], true);} else {map.set(arr[i], false);result.push(arr[i])}}return result;}function duplicateArr(arr) {var obj = {};var result = [];for (var i = 0; i < arr.length; i++) {var prop = typeof arr[i] != 'object' ? arr[i] : JSON.stringify(arr[i]);if (!obj[prop) {obj[prop] = true;result.push(arr[i]);}}return result;}
方法实现
双循环去重
双重 for(或 while)循环是比较笨拙的方法,它实现的原理很简单:先定义一个包含原始数组第一个元素的数组,然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组;因为它的时间复杂度是 O(n^2),如果数组长度很大,那么将会非常耗费内存
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}let res = [arr[0]];for (let i = 1; i < arr.length; i++) {let flag = true;for (let j = 0; j < res.length; j++) {if (arr[i] === res[j]) {flag = false;break;}}if (flag) {res.push(arr[i]);}}return res;}
indexOf 方法去重 1
数组的 indexOf()方法可返回某个指定的元素在数组中首次出现的位置。该方法首先定义一个空数组 res,然后调用 indexOf 方法对原来的数组进行遍历判断,如果元素不在 res 中,则将其 push 进 res 中,最后将 res 返回即可获得去重的数组
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}let res = [];for (let i = 0; i < arr.length; i++) {if (res.indexOf(arr[i]) === -1) {res.push(arr[i]);}}return res;}
indexOf 方法去重 2
利用 indexOf 检测元素在数组中第一次出现的位置是否和元素现在的位置相等,如果不等则说明该元素是重复元素
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}return Array.prototype.filter.call(arr, function (item, index) {return arr.indexOf(item) === index;});}
相邻元素去重
这种方法首先调用了数组的排序方法 sort(),然后根据排序后的结果进行遍历及相邻元素比对,如果相等则跳过改元素,直到遍历结束
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}arr = arr.sort();let res = [];for (let i = 0; i < arr.length; i++) {if (arr[i] !== arr[i - 1]) {res.push(arr[i]);}}return res;}
利用对象属性去重
创建空对象,遍历数组,将数组中的值设为对象的属性,并给该属性赋初始值 1,每出现一次,对应的属性值增加 1,这样,属性值对应的就是该元素出现的次数了
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}let res = [],obj = {};for (let i = 0; i < arr.length; i++) {if (!obj[arr[i]]) {res.push(arr[i]);obj[arr[i]] = 1;} else {obj[arr[i]]++;}}return res;}
set 与解构赋值去重
ES6 中新增了数据类型 set,set 的一个最大的特点就是数据不重复。Set 函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}return [...new Set(arr)];}
Array.from 与 set 去重
Array.from 方法可以将 Set 结构转换为数组结果,而我们知道 set 结果是不重复的数据集,因此能够达到去重的目的
function unique(arr) {if (!Array.isArray(arr)) {console.log("type error!");return;}return Array.from(new Set(arr));}
总结
数组去重是开发中经常会碰到的一个热点问题。我们可以根据不同的应用场景来选择不同的实现方式。
第一种:遍历数组法
这种方法最简单最直观,也最容易理解,代码如下:
var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2];var newArr = [];for (var i = 0; i < arr.length; i++) {if (newArr.indexOf(arr[i]) === -1) {newArr.push(arr[i]);}}console.log(newArr); // 结果:[2, 8, 5, 0, 6, 7]
这种方法很好理解,利用了 indexOf()方法(indexOf()方法如果查询到则返回查询到的第一个结果在数组中的索引,如果查询不到则返回-1)。先创建一个新的空数组用来存储新的去重的数组,然后遍历 arr 数组,在遍历过程中,分别判断 newArr 数组里面是不是有遍历到的 arr 中的元素,如果没有,直接添加进 newArr 中,如果已经有了(重复),那么不操作,那么从头到尾遍历一遍,正好达到了去重的目的。
第二种:数组下标判断法
这种方法也比较好理解,代码如下:
var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2];var newArr = [];for (var i = 0; i < arr.length; i++) {if (arr.indexOf(arr[i]) === i) {newArr.push(arr[i]);}}console.log(newArr); // 结果:[2, 8, 5, 0, 6, 7]
这和第一种方法有重叠,不说多余的,直接看 if 这里,在遍历 arr 的过程中,如果在 arr 数组里面找当前的值,返回的索引等于当前的循环里面的 i 的话,那么证明这个值是第一次出现,所以推入到新数组里面,如果后面又遍历到了一个出现过的值,那也不会返回它的索引,indexof()方法只返回找到的第一个值的索引,所以重复的都会被 pass 掉,只出现一次的值都被存入新数组中,也达到了去重的目的。
第三种:排序后相邻去除法
这种方法用到了 sort()方法,代码如下:
var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2];arr.sort();var newArr = [arr[0]];for (var i = 1; i < arr.length; i++) {if (arr[i] !== newArr[newArr.length - 1]) {newArr.push(arr[i]);}}console.log(newArr); // 结果:[0, 2, 5, 6, 7, 8]
这种方法的思路是:先用 sort()方法把 arr 排序,那么排完序后,相同的一定是挨在一起的,把它去掉就好了,首先给新数组初始化一个 arr[0],因为我们要用它和 arr 数组进行比较,所以,for 循环里面 i 也是从 1 开始了,我们让遍历到的 arr 中的值和新数组最后一位进行比较,如果相等,则 pass 掉,不相等的,push 进来,因为数组重新排序了,重复的都挨在一起,那么这就保证了重复的这几个值只有第一个会被 push 进来,其余的都和新数组的被 push 进来的这个元素相等,会被 pass 掉,也达到了去重的效果。
第四种:优化的遍历数组法
var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8];var newArr = [];for (var i = 0; i < arr.length; i++) {for (var j = i + 1; j < arr.length; j++) {if (arr[i] === arr[j]) {i++;j = i;}}newArr.push(arr[i]);}console.log(newArr); // 结果:[0, 5, 6, 7, 2, 8]
思路:两层 for 循环,外面一层是控制遍历到的前一个 arr 中的元素,里面一层控制的是第一层访问到的元素后面的元素,不断的从第 0 个开始,让第 0 个和他后面的元素比较,如果没有和这个元素相等的,则证明没有重复,推入到新数组中存储起来,如果有和这个元素相等的,则 pass 掉它,直接进入下一次循环。从第 1 个开始,继续和它后面的元素进行比较,同上进行,一直循环到最后就是:不重复的都被推入新数组里面了,而重复的前面的元素被 pass 掉了,只留下了最后面的一个元素,这个时候也就不重复了,则推入新数组,过滤掉了所有重复的元素,达到了去重的目的。
第五种:数组遍历法
var arr = ["a", "a", "b", "c", "b", "d", "e", "a"];var newArr = [];for (var i = 0; i < arr.length; i++) {var bl = true;for (var j = 0; j < newArr.length; j++) {if (arr[i] === newArr[j]) {bl = false;break;}}if (bl) {newArr.push(arr[i]);}}console.log(newArr); // 结果:["a", "b", "c", "d", "e"]
思路:也是两层 for 循环,外层 for 循环控制的是 arr 数组的遍历,内层 for 循环控制的是新数组的遍历,从第 0 位开始,如果新数组中没有这个 arr 数组中遍历到的这个元素,那么状态变量 bl 的值还是 true,那么自然进入到了 if 中把这个值推入到新数组中,如果有这个元素,那么代表重复,则把状态变量 bl 取值改为 false,并且跳出当前循环,不会进入到 if 内部,而进入下一次外层开始的循环。这样循环往复,最后也达到了去重的效果。
重写数组 7 个会改变原数组的方法,只有用这 7 种方法才会触发数组对应的 watcher 进行更新
$set()核心内部用的是 splice 方法
为什么不用 definedProperty 如果数组的项太多,很浪费性能
const data = { arr: [1, 2, 3] };const arrayOrigin = Array.protptype;const arrayMethods = Object.create(arrayOrigin); // 不是深拷贝function definedReactive(obj) {for (const prop in obj) {let value = obj[prop];if (a.constructor === Array) {arrayReactive(value); }else/ 非数组响应式用defineProperty}}function arrayReactive(arr) {['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => { arrayMehods[method] = function (...args) {// 函数劫持后(即执行原生的数组方法)arrayOrigin[method].apply(this, args);// 再执行自己的函数render();}});arr.**proto** = arrayMethods;}function render () {app.innerHTML = data.arr;}render();data.arr.push(4);
Vue.$set()
对于数组,是用重写的 splice 来实现
对于对象,就是对新增的属性或者修改的属性,重新进行一次 defineReactive()
模板渲染(能答出大概流程和 with 语法就好了,还要了解虚拟 dom)(有余力的可以把 diff 算法也说一下)
通过 vue-template-compiler 包,先将代码解析成 AST 语法树
优化代码,对静态节点进行标记
生成代码字符串,通过 with + new Functoin 实现生成 render 方法,render 执行后生成的是虚拟 dom 1.通过 vue-template-compiler 包,将代码生成 ast 语法树,对代码进行优化,标记静态节点(如) 2.将 ast 语法树生成代码字符串,通过 with + new Function()实现生成 render 函数,render 执行后生成一个虚拟 dom(本质就是一个 js 对象)
- with(vm) {}这样在内部就可以直接使用c,_v,_s,和 data 中的属性 _with(this){return _c(‘div’,[_v(“zf”)])} this 就是 vm
从虚拟 dom 到页面的真实渲染,通过_updateComponent 包的 vm._update 函数传入 vnode,利用 patch 方法生成真实 dom 节点并渲染到页面
初次渲染时生成的真实 dom 结构渲染到容器中,re-render 时,利用 diff 算法比对新旧节点的差异,生成需要更新的真实 dom,渲染到容器对应的位置
附加分
vue 的 diff 算法是平级(父级比完比儿子,儿子比完比孙子)比较,不考虑跨级比较(即不考虑父亲和儿子的比对);内部采用深度递归的方式+双指针的方式进行比较
先比较是否是相同节点(主要比较 key 和标签名)
相同节点则比较属性,并复用老节点
比较儿子节点,考虑老节点和新儿子节点的情况
优化比较:先比较头头,尾尾,头尾,尾头
比对查找进行复用
核心:使用 key,key 相同直接复用;找不到 key 则创建元素,多的就删除(vue2,vue3,react 都是这样)
labal 的使用?
男
女
男
女
画 0.5px 直线;
transform:scale(0.5)
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.half-px{/*height为0.5px的直线时,浏览器会四舍五入为1px的直线 */background-color: aqua;height: 0.5px;}.half-px1{background-color: aqua;height: 1px;transform: scale(0.5);/*使用缩放功能将直线缩放0.5倍*/}</style></head><body><div style="width: 20px; height: 20px;"></div><div class="half-px"></div><div style="width: 200px; height: 20px;"></div><div class="half-px1"></div></body></html>
canvas
<body><canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas><script>var drawing = document.getElementById("drawing");if(drawing.getContext){var context = drawing.getContext("2d");context.lineWidth = 0.5;context.beginPath();context.moveTo(30, 30);context.lineTo(200, 100);context.stroke();}</script></body>
1. 讲一讲继承的所有方式都有什么 手写一个寄生组合式继承
- 1).原型链继承: 将父类的实例作为子类的原型
function Cat() {}Cat.prototype = new Animal();Cat.prototype.name = "cat";// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.eat("fish"));console.log(cat.sleep());console.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //true
特点:1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例2. 父类新增原型方法/原型属性,子类都能访问到3. 简单,易于实现缺点:1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中2. 无法实现多继承3. 来自原型对象的所有属性被所有实例共享4. 创建子类实例时,无法向父类构造函数传参
- 2).构造函数的继承: 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat() {}Cat.prototype = new Animal();Cat.prototype.name = "cat";// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.eat("fish"));console.log(cat.sleep());console.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //true
特点:1. 解决了1中,子类实例共享父类引用属性的问题2. 创建子类实例时,可以向父类传递参数3. 可以实现多继承(call多个父类对象)缺点:1. 实例并不是父类的实例,只是子类的实例2. 只能继承父类的实例属性和方法,不能继承原型属性/方法3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
- 3).实例继承: 为父类实例添加新特性,作为子类实例返回
function Cat(name) {var instance = new Animal();instance.name = name || "Tom";return instance;}// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); // false
特点:1. 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果缺点:1. 实例是父类的实例,不是子类的实例2. 不支持多继承
- 4).拷贝继承
function Cat(name) {var animal = new Animal();for (var p in animal) {Cat.prototype[p] = animal[p];}Cat.prototype.name = name || "Tom";}// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // falseconsole.log(cat instanceof Cat); // true
特点:1. 支持多继承缺点:1. 效率较低,内存占用高(因为要拷贝父类的属性)2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
- 5).组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name) {Animal.call(this);this.name = name || "Tom";}Cat.prototype = new Animal();Cat.prototype.constructor = Cat;// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); // true
特点:1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法2. 既是子类的实例,也是父类的实例3. 不存在引用属性共享问题4. 可传参5. 函数可复用缺点:1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
- 6). 寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name) {Animal.call(this);this.name = name || "Tom";}(function () {// 创建一个没有实例方法的类var Super = function () {};Super.prototype = Animal.prototype;//将实例作为子类的原型Cat.prototype = new Super();})();// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); //trueCat.prototype.constructor = Cat; // 需要修复下构造函数
特点:堪称完美缺点:实现较为复杂
7. this 指向问题
[https://blog.csdn.net/foreverwy/article/details/78150563]
8. call apply bind 区别
都是用来改变this指向的call,apply 与bind 的区别是返回值不同call apply执行的结果返回值由改变this指向的函数返回值决定 而bind方法执行完之后返回一个新的改变this指向之后的函数call apply的区别在于传参形式不同 call的第二个参数开始是传递到函数体内的实参,apply的第二个参数是数组,数组的每一项是函数体内的实参
- call
call、apply、bind 都是改变 this 指向的方法
fn.call
当前实例(函数 fn)通过原型链的查找机制,找到 function.prototype 上的 call 方法,function call(){[native code]}
fn.call()
把找到的 call 方法执行
当 call 方法执行的时候,内部处理了一些事情 1.首先把要操作的函数中的 this 关键字变为 call 方法第一个传递的实参 2.把 call 方法第二个及之后的实参获取到 3.把要操作的函数执行,并且把第二个以后传递进来的实参传递给函数
fn.call([this],[param]…)
call 中的细节
非严格模式
如果不传参数,或者第一个参数是 null 或 nudefined,this 都指向 window
let fn = function (a, b) {console.log(this, a, b);};let obj = { name: "obj" };fn.call(obj, 1, 2); // this:obj a:1 b:2fn.call(1, 2); // this:1 a:2 b:undefinedfn.call(); // this:window a:undefined b:undefinedfn.call(null); // this=window a=undefined b=undefinedfn.call(undefined); // this=window a=undefined b=undefined
严格模式
第一个参数是谁,this 就指向谁,包括 null 和 undefined,如果不传参数 this 就是 undefined
"use strict";let fn = function (a, b) {console.log(this, a, b);};let obj = { name: "obj" };fn.call(obj, 1, 2); // this:obj a:1 b:2fn.call(1, 2); // this:1 a:2 b=undefinedfn.call(); // this:undefined a:undefined b:undefinedfn.call(null); // this:null a:undefined b:undefinedfn.call(undefined); // this:undefined a:undefined b:undefined
- apply
apply:和 call 基本上一致,唯一区别在于传参方式
apply 把需要传递给 fn 的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给 fn 一个个的传递
fn.call(obj, 1, 2);
fn.apply(obj, [1, 2]);
- bind
bind:语法和 call 一模一样,区别在于立即执行还是等待执行,bind 不兼容 IE6~8
fn.call(obj, 1, 2); // 改变 fn 中的 this,并且把 fn 立即执行
fn.bind(obj, 1, 2); // 改变 fn 中的 this,fn 并不执行
this 改变为 obj 了,但是绑定的时候立即执行,当触发点击事件的时候执行的是 fn 的返回值 undefined
document.onclick = fn.call(obj);
bind 会把 fn 中的 this 预处理为 obj,此时 fn 没有执行,当点击的时候才会把 fn 执行
document.onclick = fn.bind(obj);
1. 我们来假设一个场景,就比如说部门给我们一个任务,让我们做一个上传文件的区域,该怎么做啊
该问题需要注意,问题不明确,要问面试官是什么样式的,普通的文件上传,还是类似于组件库里面上传文件组件的效果.如果是简单按钮的文件上传直接用input type="file"就可以了如果是非普通的,再问一下需要兼容什么样的文件, 如果是图片的化需要预览嘛? 可以用组件库嘛? 等等一系列的问题如果上传的文件是图片需要做预览, 可以用div配合着点击事件配合着input type="file" 配合着fileReader实现具体思想input标签隐藏显示(设置hidden属性就好) div区域里面画一个十字表示可点击上传文件,点击这个区域时手动触发input的点击事件这样就可以上传文件了,再上穿文件的过程中可以用fileReader的相应一些事件进行监听读取的文件进度等到文件读取完成之后,上传至服务器端(调用指定接口) 如果上传成功,可以使用一些动画效果文字显示,并且将图片显示再指定位置上
7. 构造函数给我讲讲可以吗,最好加上你自己对于他的理解
首先构造函数的作用主要是为了封装一个工具函数可以快速的创建出一类的实例对象供我们使用.构造函数本身也是函数,所以他也可以按照函数的方式进行执行, 构造函数和普通的函数的区别在于1, 函数名字的写法不同 构造函数的名称首字母大写(但并不是说首字母不大写的函数就不能作为构造函数使用,这只是一个规范性的写法)2, 使用不同构造函数的一般使用方式是 通过new关键字进行构建实例对象普通函数就直接函数名() 直接会执行函数3, 函数内部this指向不同构造函数内部的this指向的是由构造函数构造出来的实例对象普通函数的this指向一般为全局对象构造函数里面可以定义所有实例对象公共的属性方法, 构造函数有prototype属性代表了所有由构造函数构造出来的实例对象的共有属性或方法,实例对象身上相应的也有__proto__属性指向的是构造出这个实例对象的构造函数的prototype属性构造函数也是es6中类实现的基础
为什么要去匹配&符号
如果攻击者输入了 script 标签的话,怎么做
原型,prototype,constructor,proto
箭头函数
没有自己的 arguments 那怎么拿到 arguments
let,const,var
请说一下响应式数据的理解
对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持,并且只会劫持已经存在的属性,对于数组则是通过重写数组方法来实现数组的响应式
并且只能是数组常用的方法(push,pop,shift,unshift,sort,splice,reverse),对于直接通过下标或者数组长度修改数组并不会产生响应式,或者用$set(数组,下标,更新后的值),$set(对象,属性名,更新后的值)来增加对象属性,直接添加的话不会被 defineProperty 劫持到(因为初始化 data 的时候就对数据进行响应式劫持,后续增加的属性不会被劫持到)
对象有多层次时使用递归对每一层进行响应式劫持,而 vue3 中响应式实现使用的是 proxy
每个属性都有自己的 dep 属性,用来存放它所依赖的 watcher,当属性变化时,会通知到依赖的每个 watcher 去执行
性能方面
对象层级越深,性能会越差
不需要响应的数据可以不用放到 data 中
可以用 Object.freeze()来冻结对象
const data = { count: 1 };// 获取属性时,暂时存放当前依赖let acitve = null;// 每个属性都有自己的依赖集 dep,当属性变化时,执行自己的 dep 中所有的依赖const dep = [];// data 属性的数据劫持,只会劫持已经存在的属性function defineProperty(obj) {for (const prop in obj) {let value = obj[prop];Object.defineProperty(obj, prop, {get() {// 获取属性时,判断当前属性有无依赖,有的话就添加到依赖集 dep 中if (active) {dep.push(acitve);}return value;},set(newValue) {value = newValue;// 当属性变化时,执行当前属性的所有依赖dep.forEach((watcher) => watcher());},});}}defineProperty(data);// 监听属性变化的依赖const wathcer = (fn) => {// 将依赖函数保存到 active 中,为了获取数据时能存到 dep 中active = fn;fn();// active 设为 null,可能有的属性不需要依赖 active = null;};// count 属性变化时,重新设置 app.innerHtml 的值watcher(() => {// 这个就是 count 的依赖,当后续更改 count 时就需要为 app.innerHtml 重新赋值 app.innerHTML = data.count;});
响应数组
数组响应式
12.讲一下虚拟 dom
虚拟 dom 就是用 js 对象来描述真实 dom,是真实 dom 的抽象,因为直接操作 dom 的话会带来性能消耗,所以通过 diff 算法来对比差异进行更新 dom,可以减少对真实 dom 的操作;并且虚拟 dom 不依赖真实平台环境,可以实现跨平台使用。
虚拟 dom 的实现就是普通对象包含 tag,data,children 等属性对真实节点的描述本质就是在 js 和 dom 之间的一个缓存
webpack 编译原理
初始化
读取配置文件,合并配置对象
编译
检查模块文件是否有记录,有则用记录的,没有则继续
读取文件内容
构建 ast 语法树
记录依赖,并存入 dependencies 依赖集中
替换依赖函数
将转换后的模块代码保存起来(每个模块代码对应一个唯一的模块 id)
递归加载依赖集中的模块
输出
生成相应的资源文件
webpack 性能优化
构建性能
减少模块解析,如一些没有依赖的模块,比如 jquery,lodash
配置 module.noParse,被匹配到的模块不会解析,如
module: {
noParse: /jquery/
}
优化 loader 性能,如 lodash,jquery 不需要转换,所以他们不需要 loader
热替换,降低代码改动到页面呈现的时间
devServe: {
hot: true
}
传输性能
减少打包后的 js 代码传输到浏览器的时间,即压缩打包体积或者分块打包
分包
手动分包将公共模块单独打包出来
自动分包,配置合理的分包策略,webpack 内部是使用 SplitChunksPlugin 进行分包的
单模块体积优化,代码压缩(UglifyJsPlugin),tree-shaking(消除模块中未引用的依赖和代码)
运行性能
书写高性能代码(一般在项目完成后进行此优化)
class 和原型继承的区别
这个我考虑了一下说没有区别,就是代码简洁了
讲一下闭包
应用
自执行函数
坏处
内存泄漏
js 的垃圾回收机制
标记
清除
如何解决内存泄漏,大型项目又咋办
控制台工具有个 memory 还是 performance 来着
6. 填充字符串(编程)
7. 管道函数(编程)
参考答案:
// 管道是从左往右函数执行,组合是从右往左执行。function pipeFun(...fns) => (value) => fns.reverse().reduce((acc, fn) => fn(acc), value);
8. 箭头函数和普通函数的区别
参考答案:
* 箭头函数内部没有独立的this指向* 箭头函数可以直接写返回值(省略return)
7. continue 和 break 的区别
参考答案:
- continue 结束当前循环继续下面一次循环
- break 结束循环
10. 遍历一个目录,将文件中所有的标签改成<&b>
11. 文件流 steam
把对象数据转换成流数据的对象, 在 nodejs 中 fs 模块表现,
fs 模块中可以对文件进行读写等操作,相应有读取流和写入流
此处可以把你会的 node 中对文件系统的理解全部描述以下
12. git 回退操作
git reset --hard 版本号
13. translate 的优点
- 不会脱离文档流
- 对 gpu 也比较友好
14. 实现一个球
使用阴影实现
15. 一个白板,实现画笔的功能
使用canvas 结合着事件实现
8. 图片的一些格式,以及它们的区别,和其中的算法
5.事件绑定和普通事件有什么区别
6.IE 和 DOM 事件流的区别
1.执行顺序不一样、 2.参数不一样 3.事件加不加 on
4.this 指向问题
7.IE 和标准下有哪些兼容性的写法
Var ev = ev || window.eventdocument.documentElement.clientWidth || document.body.clientWidthVar target = ev.srcElement||ev.target
9.call 和 apply 的区别
Object.call(this,obj1,obj2,obj3)
Object.apply(this,arguments)
11.b 继承 a 的方法
12.写一个获取非行间样式的函数
function getStyle(obj, attr, value) {if (!value) {if (obj.currentStyle) {return obj.currentStyle(attr);} else {obj.getComputedStyle(attr, false);}} else {obj.style[attr] = value;}}
16.添加 删除 替换 插入到某个接点的方法
obj.appendChidl()
obj.innersetBefore
obj.replaceChild
obj.removeChild
18.javascript 的本地对象,内置对象和宿主对象
本地对象为 array obj regexp 等可以 new 实例化
内置对象为 gload Math 等不可以实例化的
宿主为浏览器自带的 document,window 等
19.document load 和 document ready 的区别
Document.onload 是在结构和样式加载完才执行 js
Document.ready 原生种没有这个方法,jquery 中有 $().ready(function)
20.””和“=”的不同
前者会自动转换类型
后者不会
21.javascript 的同源策略
一段脚本只能读取来自于同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合
对于 bootstrap 的理解:
1 , bootstrap 默认的对越宽度的界限值,是可以根据效果图的实际情况,重置,甚至可以增加。例如,效果图为 2000px 宽度的情况,个人理解如下:
依据屏幕分辨率的不同,
可以采用 768,992,1200,1500,1920 的区分。
2 ,对于如何选择 col-的问题,可以依据效果图,算好比列,选择接近的 col,然后通过微调,即可。
3 ,页面图片元素少时,可选择 container-fluid 代替 container。图片 width 为 100%,高度为 auto。图片父元素高度,依据情况,选择固定值。通过 overflow 为 hidden。控制图片的显示,但是有显示不全的问题。
call、apply、bind 的区别
- 传参形式的区别:
- call 传递参数第一个参数为要绑定的 this, 剩余的参数可以传多个
- apply 只能传递两个参数 第一个参数为要绑定的 this,第二个传入参数
- bind 只能传递一个要绑定的 this
- 绑定方式不同
- call 和 apply 是在调用函数执行的时候使用,例
test()和test.call()test.apply() - bind 可以有多种绑定方式
test().bind()test.bind(this)()var str = test.bind() str();
- call 和 apply 是在调用函数执行的时候使用,例
- 作用方式不同
- call 和 apply 直接执行一个函数,并将里面的 this 替换成为了传入的 this
- bind 将 this 传入,并返回一个新的绑定了新的 this 的函数,而且 this 不可以被更改
1. 说一下减少 DOM 数量的办法,一次性给你大量的 DOM 怎么优化?
- 分页加载内容
- 按需加载内容
- 使用伪元素代替部分标签元素
2. 描述一下 script 标签的 async 和 defer 的区别
defer 会等到页面全部加载完成之后在进行脚本执行
async 下载完毕会立即执行
此处可以说一下时间线的问题
9.写出 setTimeout,SetInterval 和 requestAnimationFrame 的区别
- setTimeout 延迟执行回调函数,只执行一次
- setInterval 每隔一段时间执行一次回调函数
- requestAnimationFrame 下一次页面更新的时候执行回调函数 执行一次 如果页面更新的频率是 60hz/秒 那么等同于 setTimeout(fn, 1000 / 60);
15. 简述 DOM, HTML DOM 的区别跟联系
DOM 是指文档对象模型,是在 js 中对于整个页面进行操作的模块,该模块包含了文件里面所有的内容,主要用于在 js 中操作页面渲染功能
HTML DOM 是在 js 中 html 的一些标签进行对象化了,如 img 标签可以用 Image 对象创建等 一般用于操作对象的属性
HTML DOM 属于 DOM 的一部分
14. 什么是事件流
事件流也就是事件的流式,举个简单的例子,鼠标的事件,鼠标事件中有很多例如鼠标按下(mousedown),抬起(mouseup),移动(mousemove),移入(mouseenter,mouseover),移出(mouseleave,mouseout)等,当我使用鼠标执行一个行为之后,他可能触发的事件有多个,那么他们的先后顺序就是事件流
10.什么是事件委托?
让利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!
http://www.webasily.com/?p=78 例子可见此链接
事件委托是指将事件不直接绑定在自己的身上,而是绑定在父元素身上,他的原理是事件冒泡,当触发子元素的指定事件的时候,也会触发父元素的相同的事件,区分哪个元素出发的事件接口是事件源对象身上的 target 属性,代表的是出发事件目标元素。做事件委托的时候要注意的是 mouseenter mouseleave 事件不可以做事件委托,因为这两个事件不会触发是事件冒泡。
8. jq 事件委托
jq中的事件委托是通过on方法实现, $(parentNode).on(eventName, sonNode, function(){}) 执行事件委托的时候只有子元素(本文中的li)会触发事件,而代为执行的父元素(本文中为ul)不会触发事件,所以我们不需要盘判断触发事件的元素节点名,这一点明显优于原生的JavaScript。
通过哪个属性来获取目标对象
委托对象的 target 属性
5. 事件循环方面的执行顺序出了一个题
15.如何阻止事件冒泡和默认事件
canceBubble return false
性能优化
15.你如何对网站的文件和资源进行优化?期待的解决方案包括:
文件合并
文件最小化/文件压缩
使用 CDN 托管
缓存的使用
● 文件合并(目的是减少 http 请求):Web 性能优化最佳实践中最重要的一条是减少 HTTP 请求,减少 HTTP 请求的方案主要有合并 JavaScript 和 CSS 文件、CSS Sprites(雪碧图)、图像映射 (Image Map)和使用 Data URI 来编码图片。
● 文件压缩:目的是直接减少文件体积,减少文件加载时间
● 使用 CDN (内容分发网络)来托管资源。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。从而使用户就近取得数据,提升浏览网站的速度。
● 缓存的使用(并且多个域名来提供缓存),可以减少阿星服务器请求的次数,加快加载速度。
● 服务端开启 GZIP ,对用户请求的页面进行压缩处理,以达到节省网络带宽,提高浏览网站速度的作用
3. 性能优化
- 减少网络请求次数。
- 减少 dom 操作
- 静态资源压缩合并
- 静态资源做缓存
- 渲染优化: css 放在 head 中 js 放在 body 中,图片加载使用懒加载
- 事件节流处理
- 使用异步加载
一般性原则
依据数据而不是凭空猜测
这是性能优化的第一原则,当我们怀疑性能有问题的时候,应该通过测试、日志、profillig 来分析出哪里有问题,有的放矢,而不是凭感觉、撞运气。一个系统有了性能问题,瓶颈有可能是 CPU,有可能是内存,有可能是 IO(磁盘 IO,网络 IO),大方向的定位可以使用 top 以及 stat 系列来定位(vmstat,iostat,netstat…),针对单个进程,可以使用 pidstat 来分析。
在本文中,主要讨论的是 CPU 相关的性能问题。按照 80/20 定律,绝大多数的时间都耗费在少量的代码片段里面,找出这些代码唯一可靠的办法就是 profile,我所知的编程语言,都有相关的 profile 工具,熟练使用这些 profile 工具是性能优化的第一步。
忌过早优化
我并不十分清楚 Donald Knuth 说出这句名言的上下文环境,但我自己是十分认同这个观念的。在我的工作环境(以及典型的互联网应用开发)与编程模式下,追求的是快速的迭代与试错,过早的优化往往是无用功。而且,过早的优化很容易拍脑袋,优化的点往往不是真正的性能瓶颈。
忌过度优化
性能优化的目标是追求合适的性价比。
在不同的阶段,我们对系统的性能会有一定的要求,比如吞吐量要达到多少多少。如果达不到这个指标,就需要去优化。如果能满足预期,那么就无需花费时间精力去优化,比如只有几十个人使用的内部系统,就不用按照十万在线的目标去优化。
而且,后面也会提到,一些优化方法是“有损”的,可能会对代码的可读性、可维护性有副作用。这个时候,就更不能过度优化。
深入理解业务
代码是服务于业务的,也许是服务于最终用户,也许是服务于其他程序员。不了解业务,很难理解系统的流程,很难找出系统设计的不足之处。后面还会提及对业务理解的重要性。
性能优化是持久战
当核心业务方向明确之后,就应该开始关注性能问题,当项目上线之后,更应该持续的进行性能检测与优化。
现在的互联网产品,不再是一锤子买卖,在上线之后还需要持续的开发,用户的涌入也会带来性能问题。因此需要自动化的检测性能问题,保持稳定的测试环境,持续的发现并解决性能问题,而不是被动地等到用户的投诉。
选择合适的衡量指标、测试用例、测试环境
正因为性能优化是一个长期的行为,所以需要固定衡量指标、测试用例、测试环境,这样才能客观反映性能的实际情况,也能展现出优化的效果。
衡量性能有很多指标,比如系统响应时间、系统吞吐量、系统并发量。不同的系统核心指标是不一样的,首先要明确本系统的核心性能诉求,固定测试用例;其次也要兼顾其他指标,不能顾此失彼。
测试环境也很重要,有一次突然发现我们的 QPS 高了许多,但是程序压根儿没优化,查了半天,才发现是换了一个更牛逼的物理机做测试服务器。
性能优化的层次
按照我的理解可以分为需求阶段,设计阶段,实现阶段;越上层的阶段优化效果越明显,同时也更需要对业务、需求的深入理解。
需求阶段
不战而屈人之兵,善之善者也
程序员的需求可能来自 PM、UI 的业务需求(或者说是功能性需求),也可能来自 Team Leader 的需求。当我们拿到一个需求的时候,首先需要的是思考、讨论需求的合理性,而不是立刻去设计、去编码。
需求是为了解决某个问题,问题是本质,需求是解决问题的手段。那么需求是否能否真正的解决问题,程序员也得自己去思考,在之前的文章也提到过,产品经理(特别是知道一点技术的产品经理)的某个需求可能只是某个问题的解决方案,他认为这个方法可以解决他的问题,于是把解决方案当成了需求,而不是真正的问题。
需求讨论的前提对业务的深入了解,如果不了解业务,根本没法讨论。即使需求已经实现了,当我们发现有性能问题的时候,首先也可以从需求出发。
需求分析对性能优化有什么帮助呢,第一,为了达到同样的目的,解决同样问题,也许可以有性能更优(消耗更小)的办法。这种优化是无损的,即不改变需求本质的同时,又能达到性能优化的效果;第二种情况,有损的优化,即在不明显影响用户的体验,稍微修改需求、放宽条件,就能大大解决性能问题。PM 退步一小步,程序前进一大步。
需求讨论也有助于设计时更具扩展性,应对未来的需求变化,这里按下不表。
设计阶段
高手都是花 80%时间思考,20%时间实现;新手写起代码来很快,但后面是无穷无尽的修 bug
设计的概念很宽泛,包括架构设计、技术选型、接口设计等等。架构设计约束了系统的扩展、技术选型决定了代码实现。编程语言、框架都是工具,不同的系统、业务需要选择适当的工具集。如果设计的时候做的不够好,那么后面就很难优化,甚至需要推到重来。
实现阶段
实现是把功能翻译成代码的过程,这个层面的优化,主要是针对一个调用流程,一个函数,一段代码的优化。各种 profile 工具也主要是在这个阶段生效。除了静态的代码的优化,还有编译时优化,运行时优化。后二者要求就很高了,程序员可控性较弱。
代码层面,造成性能瓶颈的原因通常是高频调用的函数、或者单次消耗非常高的函数、或者二者的结合。
下面介绍针对设计阶段与实现阶段的优化手段。
一般性方法
缓存
没有什么性能问题是缓存解决不了的,如果有,那就再加一级缓存
a cache /kæʃ/ KASH,[1] is a hardware or software component that stores data so future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation, or the duplicate of data stored elsewhere.
缓存的本质是加速访问,访问的数据要么是其他数据的副本 — 让数据离用户更近;要么是之前的计算结果 — 避免重复计算.
缓存需要用空间换时间,在缓存空间有限的情况下,需要优秀的置换换算来保证缓存有较高的命中率。
数据的缓存
这是我们最常见的缓存形式,将数据缓存在离使用者更近的地方。比如操作系统中的 CPU cache、disk cache。对于一个 web 应用,前端会有浏览器缓存,有 CDN,有反向代理提供的静态内容缓存;后端则有本地缓存、分布式缓存。
数据的缓存,很多时候是设计层面的考虑。
对于数据缓存,需要考虑的是缓存一致性问题。对于分布式系统中有强一致性要求的场景,可行的解决办法有 lease,版本号。
计算结果的缓存
对于消耗较大的计算,可以将计算结果缓存起来,下次直接使用。
我们知道,对递归代码的一个有效优化手段就是缓存中间结果,lookup table,避免了重复计算。python 中的 method cache 就是这种思想.
对于可能重复创建、销毁,且创建销毁代价很大的对象,比如进程、线程,也可以缓存,对应的缓存形式如单例、资源池(连接池、线程池)。
对于计算结果的缓存,也需要考虑缓存失效的情况,对于 pure function,固定的输入有固定的输出,缓存是不会失效的。但如果计算受到中间状态、环境变量的影响,那么缓存的结果就可能失效,比如我在前面提到的 python method cache
并发
一个人干不完的活,那就找两个人干。并发既增加了系统的吞吐,又减少了用户的平均等待时间。
这里的并发是指广义的并发,粒度包括多机器(集群)、多进程、多线程。
对于无状态(状态是指需要维护的上下文环境,用户请求依赖于这些上下文环境)的服务,采用集群就能很好的伸缩,增加系统的吞吐,比如挂载 nginx 之后的 web server
对于有状态的服务,也有两种形式,每个节点提供同样的数据,如 mysql 的读写分离;每个节点只提供部分数据,如 mongodb 中的 sharding
分布式存储系统中,partition(sharding)和 replication(backup)都有助于并发。
绝大多数 web server,要么使用多进程,要么使用多线程来处理用户的请求,以充分利用多核 CPU,再有 IO 阻塞的地方,也是适合使用多线程的。比较新的协程(Python greenle、goroutine)也是一种并发。
惰性
将计算推迟到必需的时刻,这样很可能避免了多余的计算,甚至根本不用计算,这个在之前的《lazy ideas in programming》一文中举了许多例子。
CopyOnWrite 这个思想真牛逼!
批量,合并
在有 IO(网络 IO,磁盘 IO)的时候,合并操作、批量操作往往能提升吞吐,提高性能。
我们最常见的是批量读:每次读取数据的时候多读取一些,以备不时之需。如 GFS client 会从 GFS master 多读取一些 chunk 信息;如分布式系统中,如果集中式节点复杂全局 ID 生成,俺么应用就可以一次请求一批 id。
特别是系统中有单点存在的时候,缓存和批量本质上来说减少了与单点的交互,是减轻单点压力的经济有效的方法
在前端开发中,经常会有资源的压缩和合并,也是这种思想。
当涉及到网络请求的时候,网络传输的时间可能远大于请求的处理时间,因此合并网络请求就很有必要,比如 mognodb 的 bulk operation,redis 的 pipeline。写文件的时候也可以批量写,以减少 IO 开销,GFS 中就是这么干的
更高效的实现
同一个算法,肯定会有不同的实现,那么就会有不同的性能;有的实现可能是时间换空间,有的实现可能是空间换时间,那么就需要根据自己的实际情况权衡。
程序员都喜欢早轮子,用于练手无可厚非,但在项目中,使用成熟的、经过验证的轮子往往比自己造的轮子性能更好。当然不管使用别人的轮子,还是自己的工具,当出现性能的问题的时候,要么优化它,要么替换掉他。
比如,我们有一个场景,有大量复杂的嵌套对象的序列化、反序列化,开始的时候是使用 python(Cpython)自带的 json 模块,即使发现有性能问题也没法优化,网上一查,替换成了 ujson,性能好了不少。
上面这个例子是无损的,但一些更高效的实现也可能是有损的,比如对于 python,如果发现性能有问题,那么很可能会考虑 C 扩展,但也会带来维护性与灵活性的丧失,面临 crash 的风险。
缩小解空间
缩小解空间的意思是说,在一个更小的数据范围内进行计算,而不是遍历全部数据。最常见的就是索引,通过索引,能够很快定位数据,对数据库的优化绝大多数时候都是对索引的优化。
如果有本地缓存,那么使用索引也会大大加快访问速度。不过,索引比较适合读多写少的情况,毕竟索引的构建也是需有消耗的。
另外在游戏服务端,使用的分线和 AOI(格子算法)也都是缩小解空间的方法。
性能优化与代码质量
很多时候,好的代码也是高效的代码,各种语言都会有一本类似的书《effective xx》。比如对于 python,pythonic 的代码通常效率都不错,如使用迭代器而不是列表(python2.7 dict 的 iteritems(), 而不是 items())。
衡量代码质量的标准是可读性、可维护性、可扩展性,但性能优化有可能会违背这些特性,比如为了屏蔽实现细节与使用方式,我们会可能会加入接口层(虚拟层),这样可读性、可维护性、可扩展性会好很多,但是额外增加了一层函数调用,如果这个地方调用频繁,那么也是一笔开销;又如前面提到的 C 扩展,也是会降低可维护性、
这种有损代码质量的优化,应该放到最后,不得已而为之,同时写清楚注释与文档。
为了追求可扩展性,我们经常会引入一些设计模式,如状态模式、策略模式、模板方法、装饰器模式等,但这些模式不一定是性能友好的。所以,为了性能,我们可能写出一些反模式的、定制化的、不那么优雅的代码,这些代码其实是脆弱的,需求的一点点变动,对代码逻辑可能有至关重要的影响,所以还是回到前面所说,不要过早优化,不要过度优化。
来张脑图总结一下
4.什么是防抖和节流?有什么区别?如何实现?
防抖:
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次。
function debounce(fn, wait) {var timeout = null; //定义一个定时器return function () {if (timeout !== null) clearTimeout(timeout); //清除这个定时器timeout = setTimeout(fn, wait);};}// 处理函数function handle() {console.log(Math.random());}// 滚动事件window.addEventListener("scroll", debounce(handle, 1000));
节流:
当持续触发事件时,保证在一定时间内只调用一次事件处理函数,意思就是说,假设一个用户一直触发这个函数,且每次触发小于既定值,函数节流会每隔这个时间调用一次
var throttle = function (func, delay) {var timer = null;var startTime = Date.now(); //设置开始时间return function () {var curTime = Date.now();var remaining = delay - (curTime - startTime); //剩余时间var context = this;var args = arguments;clearTimeout(timer);if (remaining <= 0) {// 第一次触发立即执行func.apply(context, args);startTime = Date.now();} else {timer = setTimeout(func, remaining); //取消当前计数器并计算新的remaining}};};function handle() {console.log(Math.random());}window.addEventListener("scroll", throttle(handle, 1000));
用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
实现函数节流我们主要有两种方法:时间戳和定时器
防抖和节流都是指防止用户短时间内多次重复触发某一行为区别在于:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
<!--防抖-->function debounce (func, wait = 50, immediate = true) {let timer, context, args// 延迟执行函数const later = () => setTimeout(() => {// 延迟函数执行完毕,清空缓存的定时器序号timer = null// 延迟执行的情况下,函数会在延迟函数中执行// 使用到之前缓存的参数和上下文if (!immediate) {func.apply(context, args)context = args = null}}, wait);// 这里返回的函数是每次实际调用的函数return function(...params) {// 如果没有创建延迟执行函数(later),就创建一个if (!timer) {timer = later()// 如果是立即执行,调用函数// 否则缓存参数和调用上下文if (immediate) {func.apply(this, params)} else {context = thisargs = params}// 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个// 这样做延迟函数会重新计时} else {clearTimeout(timer)timer = later()}}}<!--节流-->function throttle(fn) {let canRun = true; // 通过闭包保存一个标记return function () {if (!canRun) return; // 在函数开头判断标记是否为 true,不为 true 则 returncanRun = false; // 立即设置为 false// 将外部传入的函数的执行放在 setTimeout 中setTimeout(() => {fn.apply(this, arguments);// 最后在 setTimeout 执行完毕后再把标记设置为 true(关键)//表示可以执行下一次循环了。当定时器没有执行的时候//标记永远是 false,在开头被 return 掉canRun = true;}, 500);};}
概念:
函数防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。
函数节流(throttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。
1、函数防抖(debounce)
实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法
缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟
//防抖 debounce 代码:function debounce(fn, delay) {var timeout = null; // 创建一个标记用来存放定时器的返回值return function (e) {// 每当用户输入的时候把前一个 setTimeout clear 掉clearTimeout(timeout);// 然后又创建一个新的 setTimeout, 这样就能保证 interval 间隔内如果时间持续触发,就不会执行 fn 函数timeout = setTimeout(() => {fn.apply(this, arguments);}, delay);};}// 处理函数function handle() {console.log("防抖:", Math.random());}//滚动事件window.addEventListener("scroll", debounce(handle, 500));
2、函数节流(throttle)
实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接 return
//节流 throttle 代码:function throttle(fn, delay) {let canRun = true; // 通过闭包保存一个标记return function () {// 在函数开头判断标记是否为 true,不为 true 则 returnif (!canRun) return;// 立即设置为 falsecanRun = false;// 将外部传入的函数的执行放在 setTimeout 中setTimeout(() => {// 最后在 setTimeout 执行完毕后再把标记设置为 true(关键)表示可以执行下一次循环了。// 当定时器没有执行的时候标记永远是 false,在开头被 return 掉fn.apply(this, arguments);canRun = true;}, delay);};}function sayHi(e) {console.log("节流:", e.target.innerWidth, e.target.innerHeight);}window.addEventListener("resize", throttle(sayHi, 500));
总结:
函数防抖:将多次操作合并为一次操作进行。原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否有延迟调用函数未执行。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
9. 函数防抖和节流是怎样优化性能的
一段时间内只进行一次网络请求
5.如何实现函数柯里化?
[https://www.jb51.net/article/178793.htm]
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。———— from 维基百科
<!--方法一-->const curry = ( fn, arr = []) => {return (...args) => {//判断参数总数是否和fn参数个数相等if([...arr, ...args].length === fn.length){return fn(...arr, ...args) //拓展参数,调用fn}else{return curry(fn,[...arr, ...args]) //迭代,传入现有的所有参数}}}<!--方法二-->const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])
VUE
5. 简述 vdom
[https://www.jianshu.com/p/af0b398602bc]
参考答案: Virtual Dom 可以看做一棵模拟了 DOM 树的 JavaScript 对象树,其主要是通过 vnode,实现一个无状态的组件,当组件状态发生更新时,然后触发 Virtual Dom 数据的变化,然后通过 Virtual Dom 和真实 DOM 的比对,再对真实 DOM 更新。虚拟 DOM 其实就是一种模拟 DOM 的 JavaScript 数据结构。
这个问题上可以引出 vue react 框架
3. vue 框架的优点
Vue.js 是一个轻巧、高性能、可组件化的 MVVM 库,同时拥有非常容易上手的 API;
我们都知道单页面应用:页面切换快 ,首屏时间稍慢,SEO 差 js 渲染
(多页面应用: 首屏时间快,SEO 好 切换慢 htttp 请求)
简而言之:Vue.js 是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
vue 两大特点:响应式编程、组件化
vue 的优势:轻量级框架、简单易学、双向数据绑定、组件化、视图、数据和结构的分离、虚拟 DOM、运行速度快
vue 是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和 dom,这样大大加快了访问速度和提升用户体验。而且他的第三方 ui 库很多节省开发时间。
请列举出三个 Vue 中常用的生命周期钩子函数
Vue 的生命周期钩子函数介绍
vue 生命周期共分为四个阶段
一:实例创建
二:DOM 渲染
三:数据更新
四:销毁实例
共有八个基本钩子函数
1.beforeCreate —创建前
触发的行为:vue 实例的挂载元素$el 和数据对象 data 都为 undefined,还未初始化。
在此阶段可以做的事情:加 loading 事件
2.created —创建后
触发的行为:vue 实例的数据对象 data 有了,$el 还没有
在此阶段可以做的事情:解决 loading,请求 ajax 数据为 mounted 渲染做准备
3.beforeMount —渲染前
触发的行为:vue 实例的$el 和 data 都初始化了,但还是虚拟的 dom 节点,具体的 data.filter 还未替换
在此阶段可以做的事情:。。。
4.mounted —渲染后
触发的行为:vue 实例挂载完成,data.filter 成功渲染
在此阶段可以做的事情:配合路由钩子使用
5.beforeUpdate —更新前
触发的行为:data 更新时触发
在此阶段可以做的事情:。。。
6.updated —更新后
触发的行为:data 更新时触发
在此阶段可以做的事情:数据更新时,做一些处理(此处也可以用 watch 进行观测)
7.beforeDestroy —销毁前
触发的行为:组件销毁时触发
在此阶段可以做的事情:可向用户询问是否销毁
8.destroyed —销毁后
触发的行为:组件销毁时触发,vue 实例解除了事件监听以及和 dom 的绑定(无响应了),但 DOM 节点依旧存在
在此阶段可以做的事情:组件销毁时进行提示
keep-alive 生命周期,和怎么在路由里面单独缓存一个组件
缓存文件标识符 Etag 是怎么生成的
promise A+规范也可以看看,
5.简述 VUE 的生命周期
vue 生命周期主要分为: 创建前后,挂载前后,更新前后,销毁前后
创建前后的钩子函数为 beforeCreate 和 created,在这两个生命周期之间,进行初始化事件,进行数据的检测,可以看到在 created 的时候数据已经和 data 属性进行绑定(放在 data 中的属性当值放生变化的同时,视图也会改变),这个时候还没有 el 选项
挂载前后的钩子函数为 beforeMount 和 mount
在 created 钩子函数和 beforeMount 钩子函数之间,会判断对象是否有 el 选项,如果有的话就继续向下编译,如果没有 el 选项,则停止编译,也就意味着停止了生命周期。
在 beforeMount 和 mounted 之间的生命周期之间,给 vue 私立对象添加$el 成员,并且会替换掉挂载的 dom 元素。
更新前后的钩子函数为 beforeUpdate 和 updated,当数据发生变化时,会触发对应组件的重新渲染,先后会调用这两个钩子函数,区别在于 beforeUpdate 发生在数据改变时,updated 发生在视图层改变之后
销毁前后的钩子函数为 beforeDestory 和 destoryed,beforeDestroy 是在实例被销毁之前调用,在这一步,实例仍然完全可以调用。
destroyed 函数在实例被销毁之后调用,此时 vue 实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。
vue 组件之间的传值
[https://www.cnblogs.com/LoveAndPeace/p/7273648.html]
7.组件有多少种传参方式?
vue 中组件传参方式有:
prop
父组件传递数据给子组件时,可以通过特性传递。
推荐使用这种方式进行父->子通信。
$emit
子组件传递数据给父组件时,触发事件,从而抛出数据。
推荐使用这种方式进行子->父通信。
v-model
.sync
$attrs
祖先组件传递数据给子孙组件时,可以利用$attrs 传递。
demo 或小型项目可以使用$attrs 进行数据传递,中大型项目不推荐,数据流会变的难于理解。
$attrs 的真正目的是撰写基础组件,将非 Prop 特性赋予某些 DOM 元素。
$listeners
可以在子孙组件中执行祖先组件的函数,从而实现数据传递。
demo 或小型项目可以使用$listeners 进行数据传递,中大型项目不推荐,数据流会变的难于理解。
$listeners 的真正目的是将所有的事件监听器指向这个组件的某个特定的子元素。
$root
可以在子组件中访问根实例的数据。
对于 demo 或非常小型的有少量组件的应用来说这是很方便的。中大型项目不适用。会使应用难于调试和理解。
$parent
可以在子组件中访问父实例的数据。
对于 demo 或非常小型的有少量组件的应用来说这是很方便的。中大型项目不适用。会使应用难于调试和理解。
$children
可以在父组件中访问子实例的数据。
对于 demo 或非常小型的有少量组件的应用来说这是很方便的。中大型项目不适用。会使应用难于调试和理解。
ref
可以在父组件中访问子实例的数据。
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的,适用于 demo 或小型项目。
provide & inject
祖先组件提供数据(provide),子孙组件按需注入(inject)。
会将组件的阻止方式,耦合在一起,从而使组件重构困难,难以维护。不推荐在中大型项目中使用,适用于一些小组件的编写。
eventBus(事件总线)
Vue.prototype.$bus = new Vue();Vue.component("cmp-a", {data() {return {a: "a",};},methods: {onClick() {this.$bus.$on("click", this.a);},},template: `<div><button @click="onClick">点击</button></div>`,});Vue.component("cmp-a", {mounted() {this.$bus.$on("click", (data) => {console.log(data);});},template: `<div>b</div>`,});
非父子组件通信时,可以使用这种方法,但仅针对于小型项目。中大型项目使用时,会造成代码混乱不易维护。
父子组件传递数据?
[https://www.cnblogs.com/heikedeblack/p/14411455.html?ivk_sa=1024320u]
<child :a=a @change="change">
- 属性:props
- vm.$attrs 子组件获取未注册的属性
- v-bind=”$attrs” 子组件传递给孙子组件
- this.$parent this.$children 麻烦效率也比较低 不推荐
<div id="app"><my-content :title="tilte" :content="content" ></my-content></div><script>const vm = new Vue({el: '#app',data: {content: 'content',title: 'title'},components: {myContent: {props: ['title'],created() {console.log(this.$attrs);},inheritAttrs: false,template: `<div> <h3>{{title}}</h3></div><my-p v-bind="$attrs"> </my-p></div>`,components: {myP: {props: ['content'],template: `<p>{{ content }}</p>`}}}}})</script>
- provide inject
<div id="app"><my-content ></my-content></div><script>const vm = new Vue({el: '#app',provide: {content: 'content',title: 'title'},components: {myContent: {inject: ['title'],created() {console.log(this.$attrs);},inheritAttrs: false,template: `<div> <h3>{{title}}</h3></div><my-p> </my-p></div>`,components: {myP: {inject: ['content'],template: `<p>{{ content }}</p>`}}}}})</script>
子父组件传递数据?
[https://www.jianshu.com/p/2670ca096cf8]
- $emit(‘change’, )
- ref 引用
- $listener 所有绑定事件的函数的集合
<div id="app"><my-cmp ref="cmp"></my-cmp></div><script>const vm = new Vue({el: '#app',mounted() {console.log(this.$refs.cmp.msg);},components: {myCmp: {data() {return {msg: 'hello world'}}}},methods: {cmpFunc() {console.log('cmp')}},template: `<div></div>`})</script>
子组件与子组件如何通信
[https://www.pianshen.com/article/5975260048/]
- vuex
- event bus 事件总线
Vue.prototype.bus = new Vue();
兄弟组件传递数据?
在 main.js 里面设置 data{eventHub:new Vue() }
new Vue({el: "#app",router,store,template: "<App/>",components: { App },data: {eventHub: new Vue(), // 在main.js设置所有组件都能用调用},});
我们再组件一设置一个事件调用组件二的事件,传递数据给组件二
<template><div v-on:click="on()"></div></template><script>export default {data(){return{datas:"数据"}},methods:{on(){this.$root.eventHunb.$emit("eventName",this.datas)//$emit是触发事件,当我们点击on事件的时候,$emit会触发其他组件的eventName事件, 把this.datas数据传递到其他组件中}}}</scrpt>
组件二被触发的事件,接受的参数
<template><div>{{datas}}</div></template><script>exports default{data(){return{datas:""}},created(){//组件一跟组件二都要绑定相同的eventNamethis.$root.eventHub.$on("eventName",(tar) = > {this.fn(tar)//$on是监听事件,如果组件一得$emit触发了,$on就会触发this.fn事件})}methods:{fn:function(tar){this.datas = tar}}}</script>
路由之间跳转, 如何携带参数, 有哪些方式
1、router-link (声明式路由)
1. 不带参数<router-link :to="{name:'home'}"><router-link :to="{path:'/home'}"> //name,path都行, 建议用name// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。2.带参数<router-link :to="{name:'home', params: {id:1}}">// params传参数 (类似post)// 路由配置 path: "/home/:id" 或者 path: "/home:id"// 不配置path ,第一次可请求,刷新页面id会消失// 配置path,刷新页面id会保留// html 取参 $route.params.id// script 取参 this.$route.params.id<router-link :to="{name:'home', query: {id:1}}">
2、router.push(编程式路由)
// 字符串router.push("home");// 对象router.push({ path: "home" });// 命名的路由router.push({ name: "user", params: { userId: "123" } });// 带查询参数,变成 /register?plan=privaterouter.push({ path: "register", query: { plan: "private" } });
注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:
const userId = "123";router.push({ name: "user", params: { userId } }); // -> /user/123router.push({ path: `/user/${userId}` }); // -> /user/123// 这里的 params 不生效router.push({ path: "/user", params: { userId } }); // -> /user
3、this.$router.push() (函数里面调用)
1. 不带参数this.$router.push('/home')this.$router.push({name:'home'})this.$router.push({path:'/home'})2. query传参this.$router.push({name:'home',query: {id:'1'}})this.$router.push({path:'/home',query: {id:'1'}})// html 取参 $route.query.id// script 取参 this.$route.query.id3. params传参this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,// 不配置path ,第一次可请求,刷新页面id会消失// 配置path,刷新页面id会保留// html 取参 $route.params.id// script 取参 this.$route.params.id4. query和params区别query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失4. this.$router.replace() (用法同上,push)5. this.$router.go(n) ()
this.$router.go(n)向前或者向后跳转n个页面,n可为正整数或负整数ps : 区别this.$router.push跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面this.$router.replace跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)this.$router.go(n)向前或者向后跳转n个页面,n可为正整数或负整数
注意:获取路由上面的参数,用的是$route,后面没有 r
params 是路由的一部分,必须要有。query 是拼接在 url 后面的参数,没有也没关系。
params 一旦设置在路由,params 就是路由的一部分,如果这个路由有 params 传参,但是在跳转的时候没有传这个参数,会导致跳转失败或者页面会没有内容。
params、query 不设置也可以传参,但是 params 不设置的时候,刷新页面或者返回参数会丢失,
两者都可以传递参数,区别是什么?
query 传参配置的是 path,而 params 传参配置的是 name,在 params 中配置 path 无效
query 在路由配置不需要设置参数,而 params 必须设置
query 传递的参数会显示在地址栏中
params 传参刷新会无效,但是 query 会保存传递过来的值,刷新不变
1. <router-link :to={params: {}} />传参方式:- params传参(路由的配置 路由配置 path: "/home/:id" 或者 path: "/home/:id" )- query传参 路由不需要额外配置2. this.$router.push() (函数里面调用)- this.$router.push({name:'home',query: {id:'1'}})- this.$router.push({path:'/home',query: {id:'1'}})// html 取参 $route.query.id// script 取参 this.$route.query.id
query和params区别query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
7.描述一下路由的功能
路由的作用主要是分组转发,他的出现是单页面应用之间的切换,因为是单页应用,路由的核心作用就是告诉代码,根据当前的 url,应该渲染哪个组件。
vuex 有哪几种属性? 不用 vuex 会带来什么问题?
有 5 种,分别是 state、getter、mutation、action、module
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹 mutations,使之可以异步。
modules => 模块化 Vuex
2、vuex 的 store 特性是什么
(1) vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
(2) state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新
(3) 它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性
3、 vuex 的 getter 特性是什么
(1) getter 可以对 state 进行计算操作,它就是 store 的计算属性
(2) 虽然在组件内也可以做计算属性,但是 getters 可以在多给件之间复用
(3) 如果一个状态只在一个组件内使用,是可以不用 getters
4、vuex 的 mutation 特性是什么
action 类似于 muation, 不同在于:action 提交的是 mutation,而不是直接变更状态
action 可以包含任意异步操作
5、vue 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 action 中
如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里
如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回
6、不用 vuex 会带来什么问题
可维护性会下降,你要修改数据,你得维护 3 个地方
可读性下降,因为一个组件里的数据,你根本就看不出来是从哪里来的
增加耦合,大量的上传派发,会让耦合性大大的增加,本来 Vue 用 Component 就是为了减少耦合,现在这么用,和组件化的初衷相背
VUEX共有五种属性,分别是 State、 Getter、Mutation 、Action、 Module不用vuex会带来的问题1、可维护性会下降,你要想修改数据,你得维护三个地方2、可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的3、增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。
vue 双向绑定原理
[https://blog.csdn.net/weixin_36852235/article/details/81434619?ops_request_misc=%7B%22request%5Fid%22%3A%22162633558116780274170365%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=162633558116780274170365&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-14-81434619.pc_search_result_before_js&utm_term=vue+%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A%E5%8E%9F%E7%90%86&spm=1018.2226.3001.4187]
[https://blog.csdn.net/wuxy720/article/details/80151610?ops_request_misc=%7B%22request%5Fid%22%3A%22162633558116780255295964%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=162633558116780255295964&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-8-80151610.pc_search_result_before_js&utm_term=vue+%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A%E5%8E%9F%E7%90%86&spm=1018.2226.3001.4187]
vue 实现数据双向绑定的原理就是用 Object.defineproperty()重新定义(set 方法)对象设置属性值和(get 方法)获取属性值的操纵来实现的
Object.property()方法的解释:Object.property(参数 1,参数 2,参数 3) 返回值为该对象 obj
其中参数 1 为该对象(obj),参数 2 为要定义或修改的对象的属性名,参数 3 为属性描述符,属性描述符是一个对象,主要有两种形式:数据描述符和存取描述符。这两种对象只能选择一种使用,不能混合使用。而 get 和 set 属于存取描述符对象的属性。
这个方法会直接在一个对象上定义一个新属性或者修改对象上的现有属性,并返回该对象
var keyvalue = 1;var obj = {};Object.defineproperty(obj, "key", {enumberable: true,configurable: true,set: function (newvalue) {keyvalue = newvalue;console.log("keyvalue的值已经发生了改变,目前的值是:${keyvalue}");},get: function () {return keyvalue;},});
对象 obj 获取 key 属性时,会触发 get 方法,得到是变量 keyvalue 的值。当重新设置 key 值时,会触发 set 方法,将变量 keyvalue 的值设置成已经变换的值。如此 就实现了一个简单的数据双向绑定,当改变变量 keyvalue 的值的时候,obj.key 也会发生改变,设置 obj.key 时,变量 keyvalue 也会改变。
vue 的数据双向绑定是通过数据劫持和发布-订阅者功能来实现的
实现步骤:1.实现一个监听者 Oberver 来劫持并监听所有的属性,一旦有属性发生变化就通知订阅者
2.实现一个订阅者 watcher 来接受属性变化的通知并执行相应的方法,从而更新视图
3.实现一个解析器 compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相对应的订阅者
<div><input type="text" id="text"><p id="show"></p></div><script>var obj = {};Object.defineProperty(obj, 'text', {get: () => {return obj['text'];},set: (val) => {show.innerText = val;text.value = val;}});text.oninput = (e) => {obj.text = e.target.value;}// 新版vue用了Proxy</script>
vue 如何优化首页加载?
vue 设置默认首页
在 router.js 设置如下:
index 就是默认页面
const routes = [// 公司项目{ path: "/", redirect: "/index" },{ path: "/index", component: index },{path: "/example",component: example,redirect: "/edetail",children: [{ path: "/edetail", component: edetail }],},{ path: "/login", component: login },];
不使用在根目录设置 router 的方法,跳转页面带不同的头部信息时容易出现问题
8.写出 VUEX 中,State,mutation 和 action 的联系
[https://blog.csdn.net/qq_41810005/article/details/84820710]
state 是真正的存储数据的属性,在 vuex 中只有 mutation 才能可以直接操作 state,Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,要唤醒一个 mutation handler 我们需要相应的 type 调用 store.commit(type),如果 mutation 的 handler 需要传递参数,则在 commit 的第二个参数开始是 handler 的值参
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。 而 mutation 不能(mutation 做异步操作的时候,检测不到实时的状态改变)
在开始接触 vuex 框架的时候对那些 state,action,mutation,getter 等理解的还挺顺利的,然后突然出来一种加了一个前缀的 mapState 等,这样的就有点蒙圈了。。。特别是官方的文档并没有给除详细的说明跟例子。。。然后就自己慢慢理解了一下。其实也就是一个重命名而已。。。以下就是例子,希望能帮助理解:
在 store 中代码
import Vuex from "vuex";import Vue from "vue";Vue.use(Vuex);const store = new Vuex.Store({state: {count: 10,numb: 10086,},getters: {add: (state, getter) => {state.count = getter.add;return state.count;},},mutations: {increment(state) {state.count += 2;},},actions: {actionA({ dispatch, commit }) {return commit("add");},},});export default store;
在调用的模块里面的代码如下:
<template><div class="hello"><button @click="increment">加{{count}}</button></div></template><script>import {mapState,mapActions} from 'vuex'export default {name: 'hello',data () {return {msg: 'Welcome to Your Vue.js App'}},methods:{increment(){this.$store.dispatch('incrementsync').then(() => {console.log('then');});}},computed: mapState({ // mapState相当于映射count: 'numb', //这个时候count应该等于多少?!! 是等于store文件里面的count呢还是等于numb?答案是等于numb!这边的意思是mapState把'numb'的值映射给了count,所以count等于10086})}</script>
这个时候按钮应该显示加 10 还是显示加 10086?答案是加 10086,所以 map 其实就是一个在 store 文件中的映射而已,就是不用让你要调用一个值需要敲这么多代码:this.$state.count;而只需要用 count。。。
界面效果:
好了,其他的 mapAction,mapMutations 的原理是一样样的
为什么 v-for 要带 key,key 的作用,
[https://blog.csdn.net/xukongjing1/article/details/81587549?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-3.control]
[https://www.jianshu.com/p/4bd5e745ce95]
用数组索引作为 key 好不好,为什么
[https://www.cnblogs.com/plBlog/p/13993817.html]
在进行数组下标操作的时候 数组的位置会发生变化
不到万不得已 不要使用 数组的 下标作为 遍历的 key 值
了解 diff 的讲一下 diff 中 key 的好处
一、有相同父元素的子元素必须有独特的 key,重复的 key 会造成渲染错误
例如:
let inputData = [1,2,3]
<div>
<input type="checkout" v-for="inputData">{{item}}</input>
</div><br><br>
```
解释:在没有key的情况下,默认所有的input 具有相同的key值,勾选前两个input后,inputData修改为 [0,1,2,3], 则会发现勾选的是 0 和 1,与之前的,1 和 2 不同,当出现这样的问题的时候,我们就会发现,key需要有唯一性
二、用于强制替换元素-组件 而不是重复使用它
```js
<transtion>
<span :key="text">{{text}}</span>
</transtion>
解释:1. 在没有 key 的情况下,transtion 下的 span 在最大程度的重用,相当与 key 相同,如果 key 相同,span 标签不会删除 ,只会更新 text,则不会触发过渡
- 当有 key 的情况下,text 发生改变时,总是会被替换而不是修改,因此触发过渡
这个 key 值能帮我们追踪 dom 树中的变化,让我们更新只发生变化的 dom 树,而不是只要有一个数据发生变化,我们就更新整个 dom 树.这个也算是 Vdom 的一大特点所在吧
解释:1. 在没有 key 的情况下,transtion 下的 span 在最大程度的重用,相当与 key 相同,如果 key 相同,span 标签不会删除 ,只会更新 text,则不会触发过渡
- 当有 key 的情况下,text 发生改变时,总是会被替换而不是修改,因此触发过渡
这个 key 值能帮我们追踪 dom 树中的变化,让我们更新只发生变化的 dom 树,而不是只要有一个数据发生变化,我们就更新整个 dom 树.
网络
3. http 常见的请求头都有什么啊 (能记住几个说几个)
content-Type 请求体的MIME类型 (用于POST和PUT请求中)
Accept-Language 可接受的响应内容语言列表。
If-Match 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。
host 表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。
referer 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer其实是Referrer这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer了
origin 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin的消息头,表示访问控制所允许的来源)
If-Modified-Since 允许在对应的资源未被修改的情况下返回304未修改
13. 说一下 http
HTTP: 超文本传输协议,
1. WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循的规则。
2. 它是TCP/IP 协议集中的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程以及数据本身的格式。
3. HTTP协议的版本 HTTP/1.0、HTTP/1.1、HTTP-NG,比较常用的是http/1.1
HTTP的会话方式分为四个过程:
1. 建立连接
2. 发出请求
3. 接收响应
4. 断开连接
(此处可以详细的说一下建立连接和断开连接的过程)
可以说说HTTP 报文结构 contentType不一样的话报文结构是不一样的可以进行加以区分
[https://blog.csdn.net/xiaoming100001/article/details/81109617]
6. 加密过程是什么
[https://blog.csdn.net/qq_32998153/article/details/80022489?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control]
数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的
5. http 状态码
常见的状态码 2xx 成功响应 3xx 重定向 4xx 客户端出现问题 5xx 服务端出现问题 经常看到的几个状态码的描述:
200 successs 请求成功
302 found 暂时性转移 (可能的原因接口暂时转到了另外一个url上)
304 not modified 客户端发送附带条件的请求时(if-matched,if-modified-since,if-none-match,if-range,if-unmodified-since任一个)服务器端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304 Modified(服务器端资源未改变,可直接使用客户端未过期的缓存)。304状态码返回时,不包含任何响应的主体部分。304虽然被划分在3xx类别中,但是和重定向没有关系。
404 Not Found 没有找到 (可能的原因路径写错了)
400 Bad Request 坏的请求 (可能的原因没有按照接口要求使用)
500 (服务器端出现解析问题)
4. 304 浏览器缓存
参考答案:
浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是向服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回 304。控制浏览器在什么情况下直接使用本地缓存而不向服务器发送 请求的字段相关的有以下几个
cache control,一般具有以下值:
- public: 所有内容都将被缓存
- private: 内容只缓存到似有缓存中
- no-cache: 所有内容都不会被缓存
- no-store: 所有内容都不会被缓存到缓存或者 internet 临时文件中
- must-revalidation/proxy-revalidation: 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证
- max-age=xxx( xxx is numeric ): 缓存的内容将在 xxx 秒后失效, 这个选项只在 HTTP 1.1 可用, 并如果和 Last-Modified 一起使用时, 优先级较高
其中最常用的属性便是 max-age, 这个字段很简单,就是浏览器在资源成功请求后的制定时间内,都将直接调用本地缓存和不会向服务器去请求数据。
(以上值能记住几个说几个)
Expires
Expires 头部字段提供一个日期和时间,在该日期前的所有对该资源的请求都会直接使用浏览器缓存而不用向服务器请求(注意:cache-control max-age 和 s-maxage 将覆盖 Expires 头部。)
Last-Modified/E-tag
- 若服务器在响应一个资源时添加了 Last-Modified 字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求 header 中包含 If-Modified-Since 字段,且值与服务器第一次响应给浏览器的 Last-Modified 字段一致
- 若服务器在响应一个资源时添加了 ETag 字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求 header 中包含 If-None-Match 字段,且值与服务器第一次响应给浏览器的 ETag 字段一致
Last-Modified 和 E-tag 的作用都是向服务器确认当前缓存文件是否为最新
那么上述是遵循了 Http 协议的浏览器会自动实现的,而要实现 304 的功能,就需要服务器(比如 Apache 对于静态资源会自动实现这两个字段的响应)或者我们手动在服务器端编写响应的逻辑来实现。
- 若服务器在收到的资源请求中发现含有 Last-Modified 字段,则说明浏览器中包含了该资源的某一版本的缓存,此时服务器端将根据该字段的值进行一定的逻辑判断,以决定让浏览器直接使用已有的缓存(返回 304)还是将最新的文件发送过去(200,发送新文件并更新 Last-Modified 字段)
- 若服务器在收到的资源请求中发现含有 If-None-Matc 字段,则说明浏览器中包含了该资源的某一版本的缓存,此时服务器端将根据该字段的值进行一定的逻辑判断,以决定让浏览器直接使用已有的缓存(返回 304)还是将最新的文件发送过去(200,发送新文件,并更新 ETag)
若同时使用了 Last-Modified 和 ETag,正确的做法应该是当两者都符合条件时,才返回 304
Etag 主要为了解决 Last-Modified 无法解决的一些问题。一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新 GET。这种情况下可以将某个能用来表明文件内容是否被更改的值(比如 md5)来作为 ETag
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是 s 级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)某些服务器不能精确的得到文件的最后修改时间
上述两段黑体部分能记就记 记不住就以理解为主
4. 了解定长包体吗
HTTP 包体:承载的消息内容
两种传输 HTTP 包体的方式
定长包体:
不定长包体:


HTML FORM 表单
HTML FORM 表单提交请求时的关键属性
Multipart 包体格式(RFC822)
5. 你对 refer 怎么理解的
Referer 是 HTTP 请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含 Referer
Referer 的正确英语拼法是referrer 。由于早期HTTP规范的拼写错误,为了保持向后兼容就将错就错了。其它网络技术的规范企图修正此问题,使用正确拼法,所以目前拼法不统一。还有它第一个字母是大写。
referer证明了请求的源自哪里发起的,能够明确的指向地址 我们可以用这个header 做防止恶意请求处理或者防盗链, 服务器端可以通过怕这个请求头判断请求的来源是不是被允许的,如果不是被允许的就不正常返回信息就好
例如京东的部分接口就是不被允许随意使用的,只有在京东的域名下面访问才可以
跨域
跨域请求资源
文件,css 文件,jpg,png 等,都允许被跨域,src 属性的资源也允许被跨域,href 的大部分资源都允许被跨域算跨域请求的资源:后端接口的数据、其他域的 cookie、其他域的缓存跨域行为发生在哪里:即使跨域了,请求也可以发出,服务器也可以收到请求并且正常处理,然后返回数据,浏览器也能正常接收数据,接收到之后,根据同源策略,检查当前域和请求的域是否相同,如果不同则为跨域,返回数据就不会传递给我们的代码解决跨域:jsonp(需要后端配合)、后端设置 Access-Control-Allow-Origin 属性以支持跨域;后端不配合时:iframe(只能显示,不能操作)、通过反向代理,比如 vue 的 proxy 虚拟代理,在本地模拟一个代理服务器
跨域,解决方案
跨域问题又分为客户端与服务器的跨域,客户端与客户端的跨域
客户端和服务器端的跨域解决方案: 1. 服务器代理中转
webpack: devServer: 做服务器代理的接口
nginx: 服务器代理 2. CORS 跨域: Cross origin resource sharing 统一资源共享
请求分类:
1) 简单请求
a. 请求方式只能是 get / post / head
b. 请求头字段
Access-Language
Content-Type: 值只能为
application/x-www-form-urlencoded
mutipart/form-data
text/plain
"Access-Control-Allow-Origin": "http://localhost:5500",
2) 非简单请求
"Access-Control-Allow-Origin": "\*",
"Access-Control-Allow-Methods": "GET,PUT",
"Access-Control-Allow-Headers": "x-requested-with , content-type, token", 3. jsonp:
json and padding
{}/[]/''
jquery 中提出的一种跨域的解决方案
原理是: script 标签身上的 src 这个属性是不受同源策略限制的
put / head / delete / options
客户端和客户端的跨域:
window.postMessage(要传递的数据, 允许拿到这个数据的源)
h5 之前的解决方案:
父窗口请求子窗口数据 iframe + window.name
子窗口请求父窗口的数据 iframe + location
[https://www.cnblogs.com/roam/p/7520433.html]
为什么 JSONP 可以实现跨域
7. jsonp 原理,手写 jsonp
function jsonp(options) {
var oScript = document.createElement("script");
oScript.src =
options.url + "?" + options.data + "&callback=" + options.callbackName;
document.body.appendChild(oScript);
document.body.removeChild(oScript);
}
17.解释 jsonp 的原理,以及为什么不是真正的 ajax
12.数据链路层
[https://blog.csdn.net/hansionz/article/details/86562339]
请简述一下 cookies,sessionStorage 和 localStorage 的区别?
四点:一、是否可以与浏览器进行交互。 二、存储空间的大小。 三、存储时长的限制。 四、是否可以被访问
- cookie 可以存储的内容比较少,可以很方便的向后台传值,灵活的设置存储时间,可以在同源的浏览器窗口中共享
- sessionStorage 存储的内容稍多,每次关闭窗口后自动清楚存储的数据,存储的数据不会发送,不能在其他浏览器窗口中访问。
- localStorage 存储的内容稍多,可以长期的存储数据,存储的数据不会发送出去,可以在同源的浏览器窗口中共享
sessionStorage 和 localStorage 是 HTML5 Web Storage API 提供的,可以方便的在 web 请求之间保存数据。有了本地数据,就可以避免数据在浏览器和服务器间不必要地来回传递。
sessionStorage、localStorage、cookie 都是在浏览器端存储的数据,其中 sessionStorage 的概念很特别,引入了一个“浏览器窗口”的概念。sessionStorage 是在同源的同窗口(或 tab)中,始终存在的数据。也就是说只要这个浏览器窗口没有关闭,即使刷新页面或进入同源另一页面,数据仍然存在。关闭窗口后,sessionStorage 即被销毁。同时“独立”打开的不同窗口,即使是同一页面,sessionStorage 对象也是不同的。
Web Storage 带来的好处:
减少网络流量:一旦数据保存在本地后,就可以避免再向服务器请求数据,因此减少不必要的数据请求,减少数据在浏览器和服务器间不必要地来回传递。
快速显示数据:性能好,从本地读数据比通过网络从服务器获得数据快得多,本地数据可以即时获得。再加上网页本身也可以有缓存,因此整个页面和数据都在本地的话,可以立即显示。
临时存储:很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用 sessionStorage 非常方便。
浏览器本地存储与服务器端存储之间的区别
其实数据既可以在浏览器本地存储,也可以在服务器端存储。
浏览器端可以保存一些数据,需要的时候直接从本地获取,sessionStorage、localStorage 和 cookie 都由浏览器存储在本地的数据。
服务器端也可以保存所有用户的所有数据,但需要的时候浏览器要向服务器请求数据。 1.服务器端可以保存用户的持久数据,如数据库和云存储将用户的大量数据保存在服务器端。 2.服务器端也可以保存用户的临时会话数据。服务器端的 session 机制,如 jsp 的 session 对象,数据保存在服务器上。实现上,服务器和浏览器之间仅需传递 session id 即可,服务器根据 session id 找到对应用户的 session 对象。会话数据仅在一段时间内有效,这个时间就是 server 端设置的 session 有效期。
服务器端保存所有的用户的数据,所以服务器端的开销较大,而浏览器端保存则把不同用户需要的数据分布保存在用户各自的浏览器中。
浏览器端一般只用来存储小数据,而服务器可以存储大数据或小数据。
服务器存储数据安全一些,浏览器只适合存储一般数据。
sessionStorage 、localStorage 和 cookie 之间的区别
共同点:都是保存在浏览器端,且同源的。
区别:
cookie 数据始终在同源的 http 请求中携带(即使不需要),即 cookie 在浏览器和服务器间来回传递。而 sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存。cookie 数据还有路径(path)的概念,可以限制 cookie 只属于某个路径下。
存储大小限制也不同,cookie 数据不能超过 4k,同时因为每次 http 请求都会携带 cookie,所以 cookie 只适合保存很小的数据,如会话标识。sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。
数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie 只在设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭。
作用域不同,sessionStorage 不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie 也是在所有同源窗口中都是共享的。
Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。
Web Storage 的 api 接口使用更方便。
sessionStorage 和 localStorage 之间的区别
见上面的区别 3、4
sessionStorage 与页面 js 数据对象的区别
页面中一般的 js 对象或数据的生存期是仅在当前页面有效,因此刷新页面或转到另一页面这样的重新加载页面的情况,数据就不存在了。
而 sessionStorage 只要同源的同窗口(或 tab)中,刷新页面或进入同源的不同页面,数据始终存在。也就是说只要这个浏览器窗口没有关闭,加载新页面或重新加载,数据仍然存在。
动态创建 script 标签,回调函数
Ajax 是页面无刷新请求数据操作
get 和 post 请求区别?
[https://www.cnblogs.com/logsharing/p/8448446.html]
14. 什么是 ajax
异步的JavaScript和xml(json),主要的用途就是做客户端与服务器端的数据交互。 在js中提供了一个为HTTP请求提供的接口XMLHTTPRequest对象
ajax 的全称是 AsynchronousJavascript+XML。异步传输+js+xml。所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果我们可以再来处理这个事。(当然,在其他语境下这个解释可能就不对了)这个很重要,如果不是这样的话,我们点完按钮,页面就会死在那里,其他的数据请求不会往下走了。这样比等待刷新似乎更加讨厌。(虽然提供异步通讯功能的组件默认情况下都是异步的,但它们也提供了同步选项,如果你好奇把那个选项改为 false 的话,你的页面就会死在那里)xml 只是一种数据格式,在这件事里并不重要,我们在更新一行字的时候理论上说不需要这个格式,但如果我们更新很多内容,那么格式化的数据可以使我们有条理地去实现更新。现在大部分人其实是用 JSON 这种格式来代替 XML 的,因为前者更加简洁,据说目前的解析速度也更快。多快好省,能省则省啊。总结:只要是 JS 调用异步通讯组件并使用格式化的数据来更新 web 页面上的内容或操作过程,那么我们用的方法就可算是 AJAX。
[https://segmentfault.com/a/1190000004322487]
axios 是什么?如何使用?
[https://cloud.tencent.com/developer/article/1498443]
刚刚接触 axios 有好多疑惑。它和 ajax 有什么关系呢和区别呢?接下来一起看下: 1.区别
axios 是通过 promise 实现对 ajax 技术的一种封装,就像 jQuery 实现 ajax 封装一样。
简单来说: ajax 技术实现了网页的局部数据刷新,axios 实现了对 ajax 的封装。
axios 是 ajax ajax 不止 axios。
下面列出代码来对比一下:
axios:
axios({
url: '/getUsers',
method: 'get',
responseType: 'json', // 默认的
data: {
//'a': 1,
//'b': 2,
}
}).then(function (response) {
console.log(response);
console.log(response.data);
}).catch(function (error) {
console.log(error);
})
ajax:
$.ajax({
url: '/getUsers',
type: 'get',
dataType: 'json',
data: {
//'a': 1,
//'b': 2,
},
success: function (response) {
console.log(response);
}
})
2.优缺点:
ajax:
本身是针对 MVC 的编程,不符合现在前端 MVVM 的浪潮
基于原生的 XHR 开发,XHR 本身的架构不清晰,已经有了 fetch 的替代方案
JQuery 整个项目太大,单纯使用 ajax 却要引入整个 JQuery 非常的不合理(采取个性化打包的方案又不能享受 CDN 服务
axios:
从 node.js 创建 http 请求
支持 Promise API
客户端支持防止 CSRF
提供了一些并发请求的接口(重要,方便了很多的操作)
10.ajax 请求时,如何解释 json 数据
json 是一种轻量级交互格式,本质上都是字符串,常用于前后端的数据交互,本质上就是字符串.
前端解析后端数据
前端在解析后端发来的数据,使用 JSON.parse()方法把字符串转为 json 对象.
前端向后端发送数据数据
前端在向后端发送数据,使用 JSON.stringify()方法把 json 对象转为字符串.
(使用 jquery 或者 axios 时,这些库内置了这些方法,只需设置配置项即可.)
var jsonDate = ‘{ “name”:”hello”,”age”:23 }’;
var jsonObj = JSON.parse( jsonDate );
// jsonObj 为{ “name”:alert(“hello”),”age”:23 }
//JSON.parse()与 JSON.stringify()实际功能一个去单引号,一个加单引号
使用 eval parse 鉴于安全性考虑 使用 parse 更靠谱
8.ajax 请求的时候 get 和 post 方式的区别
我们在使用 Ajax 时,当我们向服务器发送数据时,我们可以采用 Get 方式请求服务器,也可以使用 Post 方式请求服务器.那么,Get 请求和 Post 请求的区别到底在哪呢?
GET 请求
get 是最常见的请求,最常用于向服务器查询某些信息。必要时,可以将查询字符串参数追加到 URL 的末尾,以便将信息发送给服务器,对 XHR 而言,位于传入 open( )方法的 URL 末尾的查询字符串必须经过正确的编码才行,即查询字符串的每个参数的名称和值都 必须使用 encodeURIComponent()进行编码,然后才能放到 URL 的末尾,而且所有的名-值对都必须由&号分隔;
xhr.open( "get", "example.php?name1=value1&name2=value2",true );
下面这个函数整可以辅助向现有 URL 的末尾添加查询字符串参数:
function addURLParam(){
url += ( url.indexOf("?") == -1 ? "?" : "&" );
url += encodeURLComponent(name) + "=" + encodeURLComponent( value );
return url;
POST 请求
使用频率仅次于 GET 的是 POST 请求,通常用于向服务器发送应该被保存的数据。
POST 请求应该把数据作为请求的主体提交,其请求的主体可以包含非常多的数据,而且格式不限。
POST 请求必须设置 Content-Type 值为 application/x-form-www-urlencoded;如果不设置 Content-Type 头部信息,那么发送给服务器的的数据就不会出现在$_POST 超全局变量中。
发送请求时 POST 请求在使用 send 方法时,需赋予其参数;
Get 请求和 Post 请求的区别
(1)使用 Get 请求时,参数在 URL 中显示,而使用 Post 请求,则不会显示出来;
(2)Post 传输的数据量大,可以达到 2M,而 Get 方法由于受到 URL 长度的限制,只能传递大约 1024 字节.
(3)Get 请求请求需注意缓存问题,Post 请求不需担心这个问题;
(4)Post 请求必须设置 Content-Type 值为 application/x-form-www-urlencoded;
(5)发送请求时,因为 Get 请求的参数都在 url 里,所以 send 函数发送的参数为 null,而 Post 请求在使用 send 方法时,却需赋予其参数;
(6)GET 方式请求的数据会被浏览器缓存起来,因此其他人就可以从浏览器的历史记录中读取到这些数据,例如账号和密码等。在某种情况下,GET 方式会带来严重的安全问题。而 POST 方式相对来说就可以避免这些问题。
下面用代码来说明两者的区别:
get 请求
function getMethod() {
var xhr = new createXHR();
var userName = document.getElementById("userName").value;
var age = document.getElementById("age").value;
//添加参数,以求每次访问不同的url,以避免缓存问题
xhr.open(
"get",
"example.php?userName=" +
encodeURTComponent(userName) +
"&age=" +
encodeURTComponent(age) +
"&random=" +
Math.random(),
true
);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
document.getElementById("result").innerHTML = xhr.responseText;
}
};
//发送请求,参数为null
xhr.send(null);
}
GET 请求
function postMethod() {
var xhr = new createXHR();
var userName = document.getElementById("userName").value;
var age = document.getElementById("age").value;
var data =
"userName=" +
encodeURTComponent(userName) +
"&age=" +
encodeURTComponent(age);
//不用担心缓存问题
xhr.open("post", "example.php", true);
//必须设置,否则服务器端收不到参数
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if ((xhr.readyState = 4 && xhr.status == 200)) {
document.getElementById("result").innerHTML = xhr.responseText;
}
};
//发送请求,要data数据
xhr.send(data);
}
一个在 url 后面 一个放在虚拟载体里面
有大小限制
安全问题
应用不同 一个是论坛等只需要请求的,一个是类似修改密码的
9.ajax 发送请求出现乱码怎么解决?
使用 scriptCharset 即可解决问题,用 contentType 就不一定可以了。
代码如下:
$.ajax({
url: testUrl,
dataType: ‘jsonp’,
type: ‘post’,
scriptCharset: ‘utf-8’
});
上面的解决方案是最完美的,另外也附上网上的解决方式吧,是用 contentType 来处理的
代码如下:
jQuery(form).ajaxSubmit({
url: “ajax.aspx?a=memberlogin”,
type: “post”,
dataType: “json”,
contentType: “application/x-www-form-urlencoded; charset=utf-8”,
success: showLoginResponse
});
虽然也可以用,但是不推荐给大家哈。
设置请求头中的编码类型 contentType 属性后面加上字符集
解决方法:在 ajax 请求时添加
//解决编码问题
contentType: “application/x-www-form-urlencoded; charset=utf-8”
1
2
以下引用自点这里
今天闲的无聊,把以前遗留的问题解决一下,比如让人头痛的 Jquery 乱码问题。其实这方面文章已经很多了,但全面解决各种问题的很少,今天总结一下,方便自己也方便大家。
原因很简单: 其实他的中文乱码就是因为 contentType 没有指定编码,对于不同 Jquery 的版本中这个地方有不同的设置,就拿我遇到的,jquery-1.6.1 和 jquery-1.8.3 就有不同的定义。
解决办法:
在 jquery-1.6.1 文件中,搜索’contentType’
然后在 application/x-www-form-urlencoded 后面加上; charset=UTF-8
最终变成 contentType:”application/x-www-form-urlencoded; charset=UTF-8”即可。
这样通过 post 方法提交后会出现乱码的问题就可以完美解决。
如
果还有乱码现象,只能说你接收页面的编码也有问题,
这是由于异步对象 XMLHttpRequest 在处理返回的 responseText 的时候,是按 UTF-8 编码进行解码的。所以 post 方式的话,必须
把这个页面另存一下,将页面文件的编码改为 UTF-8 (请务必记住)。
JQuery Ajax 提交出现中文乱码的解决办法 2
前使用 Jquery 的时候一直没有发现,用 Ajax 提交的时候会出现乱码,我猜测可能是因为编码的原因
可能存在以下几点原因:
1.HTML 的编码不统一:如页面用的 GB2312,好像 JQuery 对它支持不太好。以前我一直都是用 UTF-8 的,一直都没有发现; 2.文件的编码,这个不好在表面上看到,简体中文版的操作系统存的文本格式的文件默认是 GB2312,建议把文件换成 UTF-8 格式的
最简单的解决办法,把提交的中文文本用 JS 的 escape 处理一下,就不会现出现乱码了。
解决的办法上用 js 的编码函数 encodeURIComponent(string)处理一下,把中文”王晓明”编码成”%E7%8E%8B%E6%99%93%E6%98%8E”,就 OK 了。
顺便说一下,我的 tomcat 下的 URIEncoding=UTF-8
今天在使用 jquery 检测用户名的时候,对英文和数字的用户名检测正确,但是对中文的时候,检测出错,经过在网上查询一段时间,终于找到了原因,是乱码问题,解决方法:
1、只要在 ajax 中有数据提交时,如果页面编码不是 utf-8 的,都应该对提交的数据进行编码,js 的编码函数为 escape()
2、在服务器端页接收数据后进行解码,然后对数据进行相关的处理后再编码
3、返回到客户端后再解码
4、如果没有提交数据,而是直接从服务器端获取数据,那直接在服务器页面设置 Response.Charset=”gb2312”即可,不用再编码解码
vbscript 中分别对应 js 中的 escape()和 unescape()函数 程序代码
通过以下处理方式得到解决:
传递参数的时候 对参数进行编码 priceName=”encodeURI(priceName)”,也可以用 encodeURIComponent();
服务器端无需做其他处理: String priceName = request.getParameter(“priceName”);
3.写出 axios 和 ajax 的区别
传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始 js 中,核心使用 XMLHttpRequest 对象,多个请求之间如果有先后关系的话,就会出现回调地狱(此处可以再介绍一下回调地狱,以及解决回调地狱的方法)。
axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生 XHR 的封装,只不过它是 Promise 的实现版本,符合最新的 ES 规范,它本身具有以下特征:
- 从浏览器中创建 XMLHttpRequest
- 支持 Promise API
- 客户端支持防止 CSRF
- 提供了一些并发请求的接口(重要,方便了很多的操作)
- 从 node.js 创建 http 请求
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换 JSON 数据
1. TCP 的拥塞控制
参考答案:
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做网络拥塞。https://blog.csdn.net/qq_41431406/article/details/97926927
2. DNS
参考答案:
域名解析服务器, 主要是用来做域名解析用的,在地址栏当中输入域名敲回车之后会先通过该服务器进行域名的解析,对应找到域名对应的服务器的 ip 地址,回传给服务器继续像这个 ip 地址发出请求(此处可以说一下浏览器里面输入地址之后发生的事情,可以具体描述一下五层网络模型)
防止 CSRF:就是让你的每个请求都带一个从 cookie 中拿到的 key, 根据浏览器同源策略,假冒的网站是拿不到你 cookie 中得 key 的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。
es6 里面出现的网络请求方案——fetch
fetch 号称是 AJAX 的替代品,使用了 ES6 中的 promise 对象。Fetch 是基于 promise 设计的。Fetch 的代码结构比起 ajax 简单多了,参数有点像 jQuery 的 ajax。但是,一定记住 fetch 不是 ajax 的进一步封装,而是原生 js,没有使用 XMLHttpRequest 对象。
我认为的 fetch 的优点是:
- 语法简洁,更加语义化
- 基于标准 Promise 实现,支持 async/await
- 同构方便,使用 isomorphic-fetch
- 更加底层,提供的 API 丰富(request, response)
5 .脱离了 XHR,是 ES 规范里新的实现方式
fetch 是一个低层次的 API,你可以把它考虑成原生的 XHR,所以使用起来并不是那么舒服,需要进行封装。
例如:
1)fetch 只对网络请求报错,对 400,500 都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
2)fetch 默认不会带 cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
3)fetch 不支持 abort,不支持超时控制,使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
4)fetch 没有办法原生监测请求的进度,而 XHR 可以
总结:axios 既提供了并发的封装,也没有 fetch 的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。
16.简述一下协商缓存和强缓存的区别?
[https://www.cnblogs.com/wonyun/p/5524617.html]
如何禁用缓存
cache-control 的 no-store(顺便讲一下 no-store 和 no-cache 的区别)
meta 标签的 content 属性设置为 no-cache
他们两者的优先级谁更高(这个我答不出了,网上暂时还没找到)
vue2 和 vue3 的响应式处理,有什么区别,从哪些方面进行了优化
先讲 vue2 响应式的缺点
vue3 用 proxy 代理对象,具体大家百度哈
这个问题连续两个公司的二面都问到,要关注 vue3 的变化
如何在 jquery 上扩展插件
jQuery.extend, jQuery.fn.extend
内部如何实现(原理,这个我不知道了,以下是百度内容)
通过 jQuery.extend 扩展 jQuery 时,方法被添加到了 jQuery 构造函数中,而当我们通过 jQuery.fn.extend 扩展 jQuery 时,方法被添加到了 jQuery 原型中
习题
以下代码执行输出结果是什么
function b () {
console.log(a);
var a = 10;
funciton a(){};
a = 100;
console.log(a);
}
b();
// function a(){}
// 100
设置元素浮动后,该元素的 display 值是多少()
- A.block
- B.inline
- C.inline-block
2.对权重排序正确的是()
a) .list .box p
b) #list .box div span
c) .list span
d) #list #box
e) p:last-child
f) style
- A.f>d>b>a>c>e
- B.f>d>b>a>e>c
- C.f>d>b>a>c=e
3.下列说法不正确的是()
- B.visibility:hidden;所占据的空间位置仍然存在,仅为视觉上的完全透明
- C.display:none;不为被隐藏的对象保留其物理空间;
- D.visibility:hidden;产生 reflow 和 repaint(回流与重绘);
4.下面哪组全时置换元素()
- A.img,input,select
- B.span.select.textarea
- C.select.p.form
5.新窗口打开网页,用到以下哪个值()
- A._self
- B._blank
- C._top
6.下面程序输出结果()
console.log('A')
setTimeout(()=>{
console.log('B')
},0);
new Promise((r)=>{
console.log('C')
r()
}).then(()=>{
console.log('D')
})
console.log('E')
- A.A C D B E
- B.A C B D E
- C.A C E D B
7.请写出下面程序的输出()
var a = {
x:1,
fun:function(){
this,x = 10
}
}
var b = a.fun;
b():
console.log(a.x);
- A.1
- B.10
- C.undefined
8.下边的代码输出的结果是()
var name = 'World!';
(function(){
if(typeof name ==='undefined'){
var name = 'Jack';
console.log('Goodbye'+name);
}else{
console.log('Hello'+name);
}
})();
- A.Goodbye Jack
- B.Hello Jack
- C.Hello World
9.以下运行结果()
for(var i = 0;i<10;i++){
setTimeout(function(){
console.log(i);
},1000);
}
- A.0—9
- B.10 个 10
- C.10 个 9
10.下列关于闭包描述不正确的是()
- A.(function(){})()理论上是一个闭包
- B.闭包不耗内存,可以随意使用
- C.比包内变量执行后不会被清除
- 11.HHTP 协议工作在()
- A、应用层
- B、传输层
- C、网络层
6. 写出代码的执行结果,并解释为什么
function a() {
console.log(1);
}
(function () {
if (false) {
function a() {
console.log(2);
}
}
console.log(typeof a); // undefined
a(); // 报错
})();
解析: 立即执行函数里面的作用域原本全局中有一个 a 变量值为 function(){console.log(1)} 由于立即执行函数里面还存在函数声明因为此 a 的值改变但是并不为 function a() {console.loh(2)}是因为 if 判断句并不能执行里面的声明语句,因此这个 a 的值变为 undefined,即内部函数声明提升没有将函数完全提升,而是局部提升,因此后面打印的 a 为 undefined 类型为 undefined a() 报错
7. 请写出弹出值,并解释为什么
alert(a); // function a() {alert(10);}
a(); // 10
var a = 3;
function a() {
alert(10);
}
alert(a); // 3
a = 6;
a(); // 报错
原因:执行这段代码的时候经过了变量声明提升,以及函数声明提升的过程,首先变量声明提升 作用域中存在变量 a 值为 undefined,其次函数声明提升,作用域中 a 的值变成了 function a() {alert(10)}, 接下来从上到下执行,执行 aler(a)的时候作用域中的 a 为 function a(){alert(10)} 接下来执行了 a(); 打印 10,在接下来赋值语句 var a = 3 因此作用域中的 a 的值变为 3 在接下来打印 a 的值也就为 3,接着 a 又被赋值为 6,因此后面执行 a()的时候会报错
8. 写出下面程序的打印顺序,并简要说明原因
setTimeout(function () {
console.log("set1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
new Promise(function (resolve) {
console.log("pr1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("set2");
});
console.log(2);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
答案:pr1 2 then1 then3 set1 then2 then4 set2
解析: promise 对象里面的函数会立即执行,而定时器的 function 会插入到任务队列的最后执行
var F = function () {};
Object.prototype.a = function () {};
Function.prototype.b = function () {};
var f = new F();
console.log(f.a, f.b, F.a, F.b);
// fn undefined fn fn
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a); //1
console.log(new B().a); //undefined
console.log(new C(2).a); //2
function User() {}
User.prototype.sayHello = function () {};
var u1 = new User();
var u2 = new User();
console.log(u1.sayHello === u2.sayHello); //true
console.log(User.prototype.constructor); //User Function
console.log(User.prototype === Function.prototype); // false
console.log(User.__proto__ === Function.prototype); // true
console.log(User.__proto__ === Function.__proto__); // true
console.log(u1.__proto__ === u2.__proto__); // true
console.log(u1.__proto__ === User.__proto__); // false
console.log(Function.__proto__ === Object.__proto__); // true
console.log(Function.prototype.__proto__ === Object.prototype.__proto__); // false
console.log(Function.prototype.__proto__ === Object.prototype); // true
var foo = 1;
function bar() {
console.log(foo); //undefined
if (!foo) {
var foo = 10;
}
console.log(foo); //10
}
bar();
var a = 1;
function b() {
console.log(a); // fn
a = 10;
return;
function a() {}
}
b();
console.log(a); //1
console.log(foo); //fn C
var foo = "A";
console.log(foo); //A
var foo = function () {
console.log("B");
};
console.log(foo); //fn B
foo(); // B
function foo() {
console.log("C");
}
console.log(foo); //fn B
foo(); // B
var foo = 1;
function bar(a) {
var a1 = a;
var a = foo;
function a() {
console.log(a); //1
}
a1();
}
bar(3);
console.log("global 1");
function A() {
console.log("A1");
function B() {
console.log("B");
}
B();
console.log("A2");
}
A();
console.log("global 2");
console.log(this.name);
var a = 2;
function A() {
this.abc = 123;
function B() {
console.log(this);
}
B();
}
var a = new A();
console.log(a.abc);
console.log(this.name);
var a = 2;
function A() {
this.abc = 123;
function B() {
console.log(this);
}
B();
}
var a = new A();
console.log(a.abc);
function A(a, b) {
console.log(a, b);
var b = 123;
console.log(a, b);
function b() {
var d = 123;
}
}
A(1, 2);
var g1 = 123;
var a = 2;
function A(a, b) {
console.log(a, b, g1);
var b = 123;
function b() {}
var a = function () {};
console.log(a, b);
}
var g2 = 456;
var g3 = function () {};
A(1, 2);
function A() {
var a = 1;
A();
}
A();
var g = 0;
function A() {
var a = 1;
function B() {
var b = 2;
var C = function () {
var c = 3;
console.log(c, b, a, g);
};
C();
}
B();
}
A();
var count = 100;
function A() {
var count = 0;
return function () {
count++;
console.log(count);
};
}
var test = A();
test();
test();
test();
console.log(count);
var a = 1;
function A() {
console.log(a);
}
function Special() {
var a = 5;
var B = A;
B();
}
Special();
17.算法:统计一个字符串中出现最多的字符?
18.算法:求一个数组中最大值和最小值的差?
12. 快速排序思想与实现
19.算法:给 n 个元素的数组,元素取值只有 0,1,2 三种可能,请为这个数组进行排序?
9. 排序算法
太多了 详见算法课程中的排序算法课程源码
给个数字,判断它的二进制有没有相邻位,如 5 是 101,没有相邻位,返回 false;6 是 110,有相邻位,返回 true
最简单做法是转二进制然后逐位判断,转二进制可用 toString(2),这个 api 只能转数字类型的数字,不能转字符串类型的数字
更优算法—位运算
利用 num & 1 得到 num 的最后一位二进制为 0 或者 1,依次判断
补充:& 1 能用来判断奇偶数;| 0 和& -1 能用来向下取整(详细见一大点中的位运算)
其他
1.什么是优雅降级和渐进增强?
优雅降级是指:向下兼容。 一开始针对高版本浏览器构建页面,先完善所有功能,然后针对低版本浏览器进行调整完善
渐进增强是指:向上兼容。一开始就针对低版本浏览器进行构建页面,满足最基本的功能要求,然后再针对高级浏览器进行效果,交互,追加各种功能以达到更好用户体验。
2.同源策略是什么?
同源策略是浏览器的一种安全机制,同源是指协议域名端口号都相同,该机制是说一般来说只有在同源下的网络交互才被认可,不同源的网络交互在响应的过程中浏览器会阻碍
3.浏览器是如何渲染页面的?
浏览器的渲染引擎会按照 html 和 css 代码一行一行的绘制页面,绘制之前浏览器会先解析 html 和 css 代码
- 解析 dom 元素生成 dom 树:浏览器会对内核进行一步一步的检索,首先识别 html 代码 他会把你的 html 代码挂载到树形结构上,形成一个 DOM 树。dom 树会遵循深度优先原则(一条枝干走完了之后才会形成下一个枝干) 生成 dom 树的过程叫做 dom 节点的解析。所有 dom 的加载完一定在 dom 解析完毕之后。即加载和解析是异步发生的。
- 解析 css 代码 生成 css 树:类似于 dom 树 与 dom 树是一一对应的 css 树就是系统根据 css 代码挂在树上
- css 树 + dom 树组成一个新的树 叫做 renderTree 即渲染树, 渲染树是页面开始绘制的前提
时间复杂度
[https://blog.csdn.net/qq_41523096/article/details/82142747]
[https://blog.csdn.net/user11223344abc/article/details/81485842?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-2.control]
4.SCSS 可以用什么代替?
scss 是一种 css 扩展语言,所以肯定能用 css 实现。
其次还可以用 less,sass 等 css 预处理语言代替
sass 和 scss 的区别在于扩展名不同,部分语法不同。
scss 是 sass 的一个升级版本,所以 scss 兼容了所有 sass 之前的功能,又有些新增的功能,包括能做运算、写函数啥的
模拟场景,我是黑客,面试官是用户,我如何进行 XSS 攻击
我答了 XSS 两种类型,存储型和反射型,从这两个类型去思考,一个是利用网站漏洞去注入脚本,一个是诱导用户点击到其他网站
具体可以看一大点的 XSS 攻击(一大点真的有用)(一大点真的有用)(一大点真的有用)
node 可以做大型服务器吗,为什么
看袁老师 node 课第一章
用 express 还是 koa
继续追问中间件(讲中间件的原理)
有没有自己封装过中间件,有(袁老师 node 课 express 中间件课)
用 node 写过哪些接口,登录验证是怎么做的
别问,问就是袁老师的 node 课(狗头)
XSS 攻击和 CSRF 攻击
[https://www.cnblogs.com/itsuibi/p/10752868.html]
6.写出 es6 都有哪些新特性
- 新增了变量声明的方式
- 箭头函数
- 对象的 defineProperty 方法
- 类的概念
- 解构赋值
- promise 异步处理方案
- ajax 的接口 fetch
- 迭代器 api
- 代理 Proxy
以上接口中哪个熟悉详细的介绍一下,最好所有的都进行描述
webpack 热更新原理,底层实现(主要是我简历写了熟悉 webpack,问的有点深,懵逼了)
[https://www.jianshu.com/p/652fbae768bf]
我只答了缓存
欲了解的自己百度一下 hh
8. webpack 中 devserve
[https://www.cnblogs.com/xiaokebb/p/9448734.html]
参考答案:
devserver 是 webpack 的开发服务器。当运行 webpack-dev-server 的时候,devServer 首先会在内存中创建类似的 dist 目录,在由浏览器打开进行预览。
devServer 的配置项有
open: 第一次构建完成时,自动用浏览器打开网页
port: 配置项用于配置DevServer服务器监听的端口号。
hot: 是否开启热更新(DevServer默认的行为是在发现源代码被更新后会通过自动刷新整个页面来做到实现预览,开启模块热替换功能后在不刷新整个页面的情况下通过用心模块替换老模块来实现实时预览。)
host: 配置项用于配置DevServer服务器监听的地址。
proxy: 当您有一个单独的API后端开发服务器,并且想要在同一个域上发送API请求时,则代理这些 url 。
10. 什么是 webpack,为什么要用它
[https://blog.csdn.net/zwbHUST/article/details/77484976]
webpack 是一种前端自动化构建工具.
随着前端的发展,为了更加方便快捷的开发前端涉及到的文件种类越来越多,但是浏览器可识别的文件只有html,css , js
那么其他种类的文件就需要进行编译转化程浏览器可识别的文件,webpack就可以完成这个过程,
并且webpack本身支持模块化的思想,所以用它构建项目我们可以进行模块化的开发(此处如果面试官问你模块化怎么回事,一定要扩展一下),webpack支持的模块化标准有common.js 以及es6模块化(es6模块化标准需要进行单独的配置)(此处可以扩展模块化的不同标准的特点)
webpack中还支持很多便于我们开发的插件,例如压缩插件,模板渲染插件等等(此处可以将你熟悉的插件详细的描述一下)
2.请简述 ES6 代码转成 ES5 代码的实现思路。
- 将代码字符串解析成抽象语法树,即所谓的 AST
- 对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
- 根据处理后的 AST 再生成代码字符串
比如,可以使用 @babel/parser 的 parse 方法,将代码字符串解析成 AST;使用 @babel/core 的 transformFromAstSync 方法,对 AST 进行处理,将其转成 ES5 并生成相应的代码字符串;过程中,可能还需要使用 @babel/traverse 来获取依赖文件等。
6. es6 之前如何模拟类的
通过构造函数的方式进行模拟
构造函数的封装就可以体现类的封装特点
构造函数的prototype属性就可以模拟继承的关系
构造函数的在不同作用域下使用也就类似于类的多态的特点
11. web socket 和 socket io
websocket 的产生是为了解决事实数据接收的问题,没有websocket之前的解决方案是用ajax轮询 和 http长连接的方式解决.
这两种方式从整个交互的过程来看,都是非常消耗资源的。
轮询,需要服务器有很快的处理速度和处理器资源。
HTTP长连接,需要有很高的并发,也就是说并行处理的能力。
这两种采用HTTP的方式都不是最好的方式,体现在:
1. HTTP的被动性:需要很多服务资源。一种需要有更快的速度,一种需要更多的响应时间。这两种都会导致对服务资源的需求越来越高。
2. HTTP的无状态性: 只有客户端发出请求,服务器端接收到才知道他是谁,否则不会知道客户端上一次请求了哪些信息。
WebSocket是HTML5提出的一个协议规范(2011年)
通过一个握手的机制,客户端(如浏览器)和服务器(WebServer)之间能建立一个类似Tcp的连接,从而方便客户端和服务器端之间的通信。
WebSocket协议的特点
1. 建立在 TCP 协议之上,它需要通过握手连接之后才能通信,服务器端的实现比较容易。
2. 与 HTTP 协议有着良好的兼容性。默认端口也是80或443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3. 数据格式比较轻量,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。
4. 没有同源限制,客户端可以与任意服务器通信。
5. 协议标识符是ws(如果加密,则为wss),服务器网址就是URL。(例如:ws://www.example.com/chat)
6. 它是一种双向通信协议,采用异步回调的方式接受消息,当建立通信连接,可以做到持久性的连接,WebSocket服务器和Browser都能主动的向对方发送或接收数据,实质的推送方式是服务器主动推送,只要有数据就推送到请求方
实际应用中,如果需要Websocke进行双向数据通信,Socket.io是一个非常好的选择。其实github上面也有通过JS封装好的Websocket库,ws可用于client和基于node搭建的服务端使用,但是用起来相对繁琐,star相对Socket.io较少,所以不推荐使用。
Socket.io不是Websocket,它只是将Websocket和轮询 (Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。也就是说,Websocket仅仅是 Socket.io实现实时通信的一个子集。因此Websocket客户端连接不上Socket.io服务端,当然Socket.io客户端也连接不上Websocket服务端。
16. 什么是 promise,手写 promise
es6中的一种异步处理方案
const MyPromise = (() => {
const PENDING = "pending",
RESOLVED = "resolved",
REJECTED = "rejected",
PromiveValue = Symbol("PromiseValue"), //状态数据
PromiseStatus = Symbol("PromiseStatus"),
thenables = Symbol("thenables"), //thenable
catchables = Symbol("catchbles"), //catchables
changeStatus = Symbol("changeStatus"), //当前状态
settleHandle = Symbol("settleHandle"), //后续处理的通用函数
linkPromise = Symbol("linkPromise"); //创建串联的Promise
return class MyPromise {
/**
* 改变当前Promise的状态
* @param {*} newStatus
* @param {*} newValue
* @param {*} queue 执行的作业队列
*/
[changeStatus](newStatus, newValue, queue) {
if (this[PromiseStatus] !== PENDING) {
//状态无法变更
return;
}
this[PromiseStatus] = newStatus;
this[PromiveValue] = newValue;
//执行相应队列中的函数
queue.forEach((handler) => handler(newValue));
}
/**
*
* @param {*} executor 未决阶段(pending状态)下的处理函数
*/
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiveValue] = undefined;
this[thenables] = []; //后续处理函数的数组 -> resolved
this[catchables] = []; //后续处理函数的数组 -> rejected
const resolve = (data) => {
this[changeStatus](RESOLVED, data, this[thenables]);
};
const reject = (reason) => {
this[changeStatus](REJECTED, reason, this[catchables]);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
/**
* 处理 后续处理函数
* @param {*} handler 后续处理函数
* @param {*} immediatelyStatus 需要立即执行的状态
* @param {*} queue 作业队列
*/
[settleHandle](handler, immediatelyStatus, queue) {
if (typeof handler !== "function") {
return;
}
if (this[PromiseStatus] === immediatelyStatus) {
//直接运行
setTimeout(() => {
handler(this[PromiveValue]);
}, 0);
} else {
queue.push(handler);
}
}
[linkPromise](thenalbe, catchable) {
function exec(data, handler, resolve, reject) {
try {
const result = handler(data); //得到当前Promise的处理结果
if (result instanceof MyPromise) {
result.then(
(d) => {
resolve(d);
},
(err) => {
reject(err);
}
);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
}
return new MyPromise((resolve, reject) => {
this[settleHandle](
(data) => {
exec(data, thenalbe, resolve, reject);
},
RESOLVED,
this[thenables]
);
this[settleHandle](
(reason) => {
exec(reason, catchable, resolve, reject);
},
REJECTED,
this[catchables]
);
});
}
then(thenable, catchable) {
return this[linkPromise](thenable, catchable);
}
catch(catchable) {
return this[linkPromise](undefined, catchable);
}
static all(proms) {
return new Promise((resolve, reject) => {
const results = proms.map((p) => {
const obj = {
result: undefined,
isResolved: false,
};
p.then(
(data) => {
obj.result = data;
obj.isResolved = true;
//判断是否所有的全部完成
const unResolved = results.filter((r) => !r.isResolved);
if (unResolved.length === 0) {
//全部完成
resolve(results.map((r) => r.result));
}
},
(reason) => {
reject(reason);
}
);
return obj;
});
});
}
static race(proms) {
return new Promise((resolve, reject) => {
proms.forEach((p) => {
p.then(
(data) => {
resolve(data);
},
(err) => {
reject(err);
}
);
});
});
}
static resolve(data) {
if (data instanceof MyPromise) {
return data;
} else {
return new MyPromise((resolve) => {
resolve(data);
});
}
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
};
})();
17. 什么是 less 和 sass,为什么用
less 和sass 都是css的预处理语言
预处理语言就是对css的一种封装优化,
主要的目的还是更加方便我们书写css样式。
18. jquery,vue,react,angular 区别
jquery是一个js工具库,主要是简化了js的使用以及操作
react,vue,angular是前端框架, 这个框架的出现主要也是简化我们日常的项目开发,这里面简化的是整体项目的开发而不是单纯的js部分,还包含html css等
vue,react,angular 的区别:
1. 语法的不同
2. 部分功能思想实现的不同
3. vue的语法简单,react偏于多端项目的开发,angular每个版本差异比较大
4. vue继承了react和angular的优点
项目
8. 在你学习数据结构的时候,有没有觉得有什么很难,但是很有趣的地方呢
这里就是比较开放性的题了, 可以说数据结构我觉得并不难, 他的难点在于算法的实现.
算法我的理解就是解决问题的思维逻辑, 比较抽象化, 个人觉得一个问题可能很多种思维方式都可以实现,但是想要找得到最优的,这就是个难点,
如果找到了最优解的时候是特别有成就感的,这个过程就比较好玩
1. 对技术面的体验,哪些方面需要提升,自己做过哪些努力
19. 项目中遇到过什么难题
2. 讲一讲你平时怎么学习前端的
参考话术:
在网上找了一些前端技术栈的知识架构图, 根据结构路程图买了一些响应的书籍,以及看了一些博客文章,在github上找一些成型的项目, 先看了一下别人的代码,觉得自己也能实现,就自己写了一下,写的过程中出现了xxxxxxxx问题, 网上找问题的原因,以及解决方案, 有一些基础知识网上找了一些教学视频, 看了一下,做了一些笔记...
3. 都喜欢看些什么技术类的书
就说你看过的就行
3. 介绍最让我有收获的一个项目
4. 介绍音乐播放器项目,音频加载不出来时的优化处理
2. 对前端的认识,学习了哪些技术
3. 你的这些项目都有用户吗
5. 做过哪些项目
6. 项目上的提升,成就感的事
15. 什么时候开始学的,怎么学的
一个什么什么机会接触到了前端,那个时候就觉得前端是做网页的,会做一些简单的页面,就觉得前端真简单就一些标签样式事件啥的
后来又看某个某个网站,就有点好奇某个某个功能的实现,自己就实现了一下,遇到了什么难点,再后来就一点一点的深入(这里可以详细的说怎么深入的)
