let和var、const的区别
var 用于声明全局变量,变量提升只是声明提升,并不是赋值提升
console.log(a);//undefined
var a = 1;
var a;
console.log(a);//undefined
a = 1;
let 用于声明局部变量,块级作用域,不允许重复定义;不具有变量提升,变量未声明前不能使用,否则报错,就是let声明存在暂时性死区
let a = 1;
console.log(a);//1
console.log(b);//Uncaught ReferenceError: b is not defined
let b = 2;
function foo(){
let a = 1;
let a = 2;//Uncaught SyntaxError: Identifier 'a' has already been declared
}
const 用于声明常量,块级作用域,一旦赋值不能修改
const a = 1;
console.log(a);//1
a = 2;
console.log(a);//Uncaught TypeError: Assignment to constant variable.
const obj = {a:1,b:2};
console.log(obj.a);//1
obj.a = 3;
console.log(obj.a);//3
const 声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。
箭头函数的使用场景
1. 不适用场景
- 对象中的方法
var cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
当调用cat.jumps时,lives的值并没有递减,因为this没有绑定值
- 带有动态上下文的回调函数
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
点击按钮后会报错,typeError的错误,因为this并没有绑定到button上
- 当箭头函数影响到代码的可读性时 普通函数可以知道预期的返回等,但是对于箭头函数却很难从代码上进行解读。
2.适用场景
- 在任何需要绑定this到当前上下文,而不是函数本身时。
- 无复杂逻辑的纯函数场景,例如用在 map、reduce、filter 的回调函数定义中
promise的all方法和race方法的使用场景和主要功能
1. promise.all
使用场景:多个异步结果合并到一起,比如一个页面需等待多个异步的ajax返回数据来显示,在此之前只能显示正在加载的图标
promise.all可以将多个promise实例包装到一起组成一个新的实例
promise.all([p1,p2,p3])
- 它接受一个数组作为参数
- 数组可以是promise对象也可以是其他的值,只有promise对象会等待状态的改变
- 当所有子promise完成,该promise完成,返回的是全部值得数组
- 如果有任何一个promise失败,则该promise失败,返回第一个失败的子promise的值
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
ps:promise.all成功数组里面返会的数据的顺序,和接收到的数组的顺序是一致的
2. promise.race
使用场景:把异步操作和定时器结合到一起,如果定时器先触发认为超时告知用户,比如点击一个查询按钮,超过几秒接口还没有返回数据我们就提示用户“您的查询已超时!”
promise.race和promise.all一样都是把多个实例包装到一起,但是不同的是,promise.race()中任意一个返回成功后,就算完成,但是进程不会立刻停止
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功了')
}, 2000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 5000);
})
Promise.race([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
race顾名思义比赛,里面哪一个结果先返回,就返回那个结果,不管结果是成功或者失败。
js的继承,组合继承和寄生组合继承的优缺点
快排原理
var quickSort = function (arr) {
if(arr.length <=1) return arr;
var pivot = arr.slice(0,1);
var leftArr = [],rightArr =[];
for(var i = 1 ; i < arr.length ; i++){
if(arr[i] < pivot){
leftArr.push(arr[i]);
}else{
rightArr.push(arr[i]);
}
}
return quickSort(leftArr).concat(pivot,quickSort(rightArr));
}
思路:快速排序是对冒泡排序的一种改进,第一趟排序时将数据分成两部分,一部分比另一部分的的所有数据都要小,然后进行递归调用,在两边都实行快速排序
css可以继承和不可以继承的属性各列举5种
- 可继承的:font,font-size,font-weight,text-align,text-indent,line-height,visibility,color,list-style,cursor
- 不可继承的:display,vertical-align,width,height,marign,border,padding,background,float,position
vue的路由
1. vue-router是什么?有哪些组件
路由管理器,用来构建单页面应用的,实现不同组件之间的跳转
常用组件:router-link(设置路由的跳转)、router-view(根据路由显示组件)
2.active-class是哪个组件的属性
active-class是router-link用来作选中样式的切换
在vue-router中使用active-class有两种方式:
- 直接在路由js文件中设置
export default new Router({
linkActiveClass: 'active'
})
- 在router-link中直接写入active-class
<router-link to="/home" class="menu-home" active-class="active">首页</router-link>
ps:如果使用在router-link中加入active-class的方法,可能会出现两个路由都有选中的样式,
<div class="menu-btn">
<router-link to="/" class="menu-home" active-class="active">
首页
</router-link>
</div>
<div class="menu-btn">
<router-link to="/my" class="menu-my" active-class="active">
我的
</router-link>
</div>
- 解决方案A
export default new Router({
linkExactActiveClass: 'active',
})
<router-link to="/" class="menu-home" active-class="active" exact>首页</router-link>
现在路由的js文件中配置linkExactActiveClass,在router-link中加入exact
- 解决方案B
<router-link to="/home" class="menu-home" active-class="active" exact>首页</router-link>
路由中加入重定向
{
path: '/',
redirect: '/home'
}
3.怎么定义vue-router的动态路由?怎么获取传过来的值
{
path: '/details/:id'
name: 'Details'
components: Details
}
动态路由的创建主要是通过使用path属性过程是,使用动态路径参数以冒号开头 当匹配到/details的时候就会把参数设置到this.$route.params上,可以通过
this.$route.params.id
来获取传递过来的参数
4.vue-router有哪几种导航钩子?
- 全局导航钩子 router.beforeEach(to,from,next)路由改变前的钩子
const router = new Router({ ... })
router.beforeEach((to, from, next) => {
//
})
to:即将要进入的路由目标对象
from:当前正要离开的路由,也是一个路由对象
next:一定要调用该方法来resolve这个钩子
router.beforeResolve在导航被确认前,同时在所有组件内守卫和异步路由组件被解析之后,该钩子就被调用
router.afterEach路由改变后的钩子
- 路由独享钩子
可以在路由配置上直接定义beforeEnter
cont router = new Router({
routes: [
{
path: '/home',
component: Home,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
- 组件内的导航钩子
beforeRouterEnter在进入当前组件对应的路由前调用
beforeRouterUpdate在当前路由改变,但是该组件被复用时调用
beforeRouterLeave在离开当前组件对应的路由前调用
export default {
data() { ... },
beforeRouteEnter(to, from, next) {
... ...
}
}
ps:beforerRouterEnter不能获取组件实例this,因为这个守卫执行前,组件实例还没有被创建出来,剩下两个可以正常获取
5.router的区别
router是vueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
route是路由跳转的对象,每一个路由都会有一个route对象,是一个局部对象,包括path、params、hash、query、name等路由信息参数
6.vue-router响应路由参数的变化
- 用watch检测路由的变化
watch: {
$route(to, from){
console.log(to.path) // 对路由变化做出响应
}
}
- 组件内导航钩子函数
beforeRouteUpdate(to, from, next){
// to do somethings
}
7.vue-router传参
- params
this.$router.push({
name: Home,
params: { number: 1 , code: '999' }
})
接收参数this.$route.params
ps:只是用name,不使用path,参数不会显示在路径上,浏览器强制刷新参数会被清空
- query
this.$router.push({
name: Home,
query: { number: 1 , code: '999' }
})
接收参数this.$route.query
ps:参数会显示在路径上,刷新不会清空参数,name可以使用path路径
- router-link标签
<router-link target="_blank" :to="{path:'/login',query:{userId: "33333"}}"></router-link>
8.vue-router实现路由懒加载
把不同路由对应的组件分割成不同的代码块,当路由被访问时才加载对应组件,即为路由的懒加载。
- vue的异步组件
{
path: '/home',
name: 'home',
component: resolve => require(['@/components/home'],resolve)
}
- import引入
const router = new VueRouter({
routes: [
{
path: '/home',
name: 'Home',
component:() = import('../views/home')
}
]
})
- webpack提供的require.ensure()
{
path: '/home',
name: 'home',
component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
}
vue的双向绑定如何实现,原理是啥
vue的双向数据绑定是通过数据劫持结合发布订阅的方式来实现的,数据和视图同步,数据发生变化视图跟着变化,视图变化数据也随之发生改变。
关于数据双向绑定,核心就是object.defineProperty()方法。
- object.defineProperty()方法:
object.defineProperty(obj,prop,descriptor),这个方法接收三个参数:
obj:要定义其上属性的对象
prop:要定义或修改的属性
descriptor:具体的改变方法
可以理解为用这个方法来定义一个值,用它里面的get方法获取对应内容,用set方法给对应内容赋值。
var obj = {}
Object.defineProperty({},'hello',{
get:function(){
console.log('调用了get方法')
},
set:function(newVal){
console.log('调用了set方法,新的值是'+newVal)
}
})
obj.hello;//调用了get方法
obj.hello = 'hi';//调用了set方法,新的值是‘hi’
svg和canvas的区别
- svg
是一种使用XML描述2d图形的语言
SVG基于xml这也就意味着SVG DOM中的每个元素都可用
在svg中每个被绘制的图形都被视为对象,如果svg对象的属性发生变化,那么浏览器能够自动重现图形
- canvas
canvas通过JavaScript来绘制的2d图形,是逐像素进行渲染的
在canvas中一旦图形被绘制完成,他就不会得到浏览器的继续关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。
-
区别
SVG:
不依赖分辨率
支持事件处理
最适合带有大型渲染区域的应用程序(例如谷歌地图)
复杂度高会减慢渲染速度
不适合游戏应用
canvas:
依赖分辨率
不支持事件处理
弱的文本渲染能力
能够以.png或.jpg格式保存结果图像
最适合图像密集型的游戏,其中的许多对象会被频繁重绘
节流和防抖你是如何实现的,他们两个根本的区别在哪里,代码上如何体现
- 参考函数节流和防抖
css的bfc你了解吗,他们在哪些场景可以实现,你还知道关于哪些清除浮动的方法
BFC(BLOCK formatting context)块级格式化上下文,他是一个独立的渲染区域,这个区域与外部毫不相干
BFC的布局规则
- 内部的box会在垂直方向上,一个接着一个的放
- box垂直方向上的距离由margin决定,属于同一个BFC的两个相邻的box的margin会重叠
- 每个盒子(块盒与行盒)的margin box的左边,与包含border-box的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也如此
- 计算BFC的高度时,浮动元素也参与计算
如何创建BFC
- float的值不是none
- position的值不是relative或static
- display的值是inline-block,table-cell,flex,inline-flex,table-caption
- overflow的值不是visible
BFC的作用
- 利用BFC来避免margin重叠
- 自适应两栏布局
- 清除浮动
可以理解为BFC就是页面中的一个独立的容器,容器里面的子元素不会影响到外面的元素。
因为BFC内部的元素和外部的元素绝对不会互相影响,所以, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。
前端跨域了解多少,jsonp如何实现,有啥缺陷
什么是跨域?
不满足同源策略(协议、域名、端口号任意一个不同)即跨域。
解决跨域的方法
- jsonp解决
//原生
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.demo2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
alert(JSON.stringify(res));
}
//jquery
$.ajax({
url: 'http://www.demo2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "onBack", // 自定义回调函数名
data: {}
});
//vue
this.$http.jsonp('http://www.demo2.com:8080/login', {
params: {},
jsonp: 'onBack'
}).then((res) => {
console.log(res);
})
缺点:只能实现get一种请求
- document.domain+iframe
- location.hash +iframe
- window.name+iframe
- postMessage
- 跨域资源共享(cors)
- ngnix代理
- node中间件代理
- websocket协议跨域
实现一个滚动条你如何实现
深浅拷贝,各自原理,深拷贝有啥缺点
如何用原生js实现一个拖拽
性能优化有哪些?
- 减少http请求
- 使用服务端渲染
- 静态资源使用cdn
- 将css放在文件头部,js放在文件底部
- 使用字体图标iconfont代替图片图标
- 善用缓存不重复加载重复资源
- 压缩文件
- 图片优化(图片延迟加载,响应式图片,调整图片大小,降低图片质量,尽可能使用css3效果代替图片,使用webp格式图片)
- 按需加载(通过webpack等)
- 减少重绘重排
- 使用事件委托
- 使用web worker
- 降低css选择器的复杂性
- 合理使用规则,避免过度优化
数组去重列举2种方法
- 循环遍历
function removeRepeat(arr){
if(arr.length < 1) return arr;
var arr2 = [];
for(let i = 0 ; i < arr.length; i ++){
if(arr2.indexOf(arr[i]) == -1){
arr2.push(arr[i]);
}
}
return arr2;
}
- ES6中的new Set()方法
function removeRepeat(arr){
return Arrar.from(new Set(arr))
}
function removeRepeat(arr){
var newArr = new Set(arr)
return [...newArr]
}
- filter()方法
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
css的盒模型
- 标准盒模型
box-sizing:content-box;
宽度:内容宽+padding+margin+border
- 怪异盒模型
box-sizing:border-box;
宽度:内容宽+margin
block inlineblock,inline的区别
- block 块级元素独占一行,默认情况下块级元素宽度会自动填满父级元素的宽度; 块级元素可以设置宽高,可以设置margin和padding; 块级元素内可以写块级和行内元素;
- inline-block 行内块元素不会独占一行; 行内块可以设置宽高和margin及padding
- inline 行内元素不会独占一行,一行可以有多个行内元素,宽度随内容的大小决定; 行内元素设置宽高不生效; 行内元素的padding-top,padding-bottom,margin-top,margin-bottom不生效
flex布局
display:flex;
设置为flex布局后,设置float,clear,vertical-align不生效
- flex-direction 决定主轴的排列方向,即项目的排列方向
.box {
flex-direction: row | row-reverse | column | column-reverse;
}
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。
- flex-wrap 默认项目都展示在一条线上,如果战士不下要如何显示
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}
no-wrap:默认不换行
wrap:换行,第一行在上方
wrap-reverse:换行,第一行在下方
- ** flex-flow**
flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。 - justify-content 定义了项目在主轴上的展示方式
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
flex-start(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
- align-items 属性在交叉轴上怎么展示
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
- align-content 属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.box {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。
实现一个单行文本溢出显示
单行文本溢出省略号显示:
.div{
overflow:hidden;
tex-overflow:ellipsis;
white-space:nowrap;
}
多行文本溢出省略号显示:
.mult_line_ellipsis{//多行文本溢出
overflow: hidden;
text-overflow:ellipsis;//文本溢出显示省略号
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
width:130px;
background-color:cornflowerblue;
}
重排重绘,如何减少
- 重排 当页面布局和几何属性改变就会发生重排 以下情况会导致重排:
- 添加或者删除可见的dom元素
- 元素位置改变,display、position、float、overflow等
- 元素尺寸改变,边距,填充,高度,宽度
- 内容改变,比如文本内容改变或者图片大小改变而引起的宽度和高度的改变
- 页面渲染初始化
- 浏览器窗口改变----resize事件发生时
- 重绘 当页面的样式发生改变就会发生重绘,背景色、边框色、可见性等
- 重排重绘的代价 导致浏览器很卡很慢
- 减少重排重绘
- 减少直接操作dom元素,改用className用于控制
- 尽量减少table的使用,table的属性发生变化会直接导致布局发生重排或重绘
- 当dom元素的position属性的值为fixed或absolute,可通过css形变触发动画效果,此时是不会触发reflow
- 不要把dom节点的属性值放在一个循环里当成循环变量
- 如果需要创建多个dom节点,可以使用documentFragment创建完成后一次性加入document
浏览器解析渲染过程
- 构建dom
- 构建CSSOMTree
- 渲染树的构建
- 布局
- 渲染 构建对象模型(DOM,CSSOM)->构建渲染树(RenderTree)->布局->渲染
什么会阻塞浏览器的解析
- 输入url地址到页面加载都发生了什么?
- 根据网址进行DNS解析,将相应的域名解析成对应的IP地址
- 客户端根据对应的IP地址去寻找对应的服务器进行TCP三次握手,建立TCP连接
- 客户端发起http请求,请求对应资源
- 服务器响应并返回相应数据(例如:html)
- 浏览器将获取到的html文档由html解析器解析成dom树
- 同时由css解析器将css样式解析成css规则树
- 将生成的dom树和css规则树合并成rendering tree(渲染树)
- 根据渲染树,在屏幕上对元素进行布局
- 根据渲染树将各个元素绘制到屏幕
- 客户端与服务器进行四次挥手
- 浏览器渲染网页阻塞顺序
- 构建dom树时,如遇到js元素时,会阻塞dom树和css规则树的构建,优先执行js文件
- 构建dom树时,如遇到css元素,会开启异步请求线程,该线程会先下载css文件,在构建css规则树,该线程会阻塞JavaScript引擎线程,但不会阻塞dom树的构建
- css解析和js解析互斥,也就是说js解析时会阻塞css解析,css解析时会阻塞js解析
- js解析时,如果js还操作了css,而这个css还没构建或解析,则会延迟执行js,直到完成css的下载解析构建,再继续执行js
js的defer和async属性
<script src='example.js'></script>
浏览器会立即加载并执行js脚本,此标签后的文件需等待该文件执行完毕,那么无疑会阻塞后面文件的加载。
<script async src='example.js'></script>
增加了async属性,该文件会和后面的文件内容并行加载执行,即异步执行。
<script defer src='example.js'></script>
增加了defer属性,加载后续的文档和js脚本的加载是并行的,此时只是并行加载并不执行,js的执行需要等到所有文档解析完成在开始。
defer 和 async 的共同点是都是可以并行加载JS文件,不会阻塞页面的加载,不同点是 defer的加载完成之后,JS会等待整个页面全部加载完成了再执行,而async是加载完成之后,会马上执行JS,所以假如对JS的执行有严格顺序的话,那么建议用 defer加载。
浏览器缓存
浏览器分为强缓存和协商缓存,缓存的作用是为了提高客户端速度,节省网络流量,降低服务器压力。
- 强缓存:浏览器请求资源,如果header中Cache-control和Expries没有过期,直接从缓存(本地)读取资源,不需要再向服务器请求资源。
- 协商缓存:浏览器请求的资源如果是过期的,就会向服务器发送请求,header中带有etag字段。服务器在进行判断,如果etag匹配就给客户端返回300的状态码,客户端继续使用本地缓存,否则客户端会重新获取数据资源。