前端面试
一.闭包与原型:
1.什么是闭包?闭包的用途?
1)闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
2)js中闭包的优点和缺点:
优点:
1.保护函数内的变量安全
2.在内存中维持一个变量(用的太多就变成了缺点,占内存) ;
1. 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
2. 方便调用上下文的局部变量。
3. 加强封装性,可以达到对变量的保护作用。
缺点:
1.常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
2.还有有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生。
闭包的特性:
1. 函数嵌套函数
2. 内部函数可以访问外部函数的变量
3. 参数和变量不会被回收。
4. 闭包的典型框架应该就是jquery了。
二.js基础
1.JS执行上下文
- 全局执行上下文:这是默认或基本执行上下文。不在任何函数内的代码都属于全局上下问,一个程序中只能由一个全局执行上下文。他要做的有两件事:
- 创建一个全局对象,在浏览器中为(
window
)。 - 将
this
的值设置为全局对象。
- 创建一个全局对象,在浏览器中为(
- 函数执行上下文:每次调用函数时,都会为该函数创建一个全新的执行上下文。每个函数都有自己的执行上下文,但它是在调用函数时创建的,这个上下文可以保护里面的私有变量和外界互不干扰。
- Eval执行上下文:在
eval
函数执行代码也会产生一种特殊的执行上下文,但由于我们通常不会使用它,因此就不讨论了。 - 每当一个函数执行完毕,则这个函数的执行上下文也将从栈中弹出,等到所有函数都运行完毕,要关闭页面的时候,全局上下文也将出栈释放,程序运行结束。如果当前上下文中的某些内容,被当前上下文以外的东西占用,那么当前上下文是不能被释放的,这就是我们熟知的闭包(Closure)。
2.const定义的对象属性可以被修改吗?
答:const定义的对象属性可以被修改,但是const定义即基本能数据类型string,number等,定义之后不能背修改,修改之后会报错。
原因:
const定义中的不变指的时对象的指针不变,因为修改对象那个中的属性并不会让指向对象的指针发生变化,所以指向对象属性可以被修改。
3.数据类型检测的方式有哪些?
typeof:其中数组、对象、null都会被判断为object,其他判断都正确。
1
2
3
4
5
6
7
8console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // objectinstanceof
可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。可以看到,instanceof
只能正确判断引用数据类型,而不能判断基本数据类型。instanceof
运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的prototype
属性。1
2
3
4
5
6
7console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // trueconstructor
有两个作用,一是判断数据的类型,二是对象实例通过constrcutor
对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor
就不能用来判断数据类型了1
2
3
4
5
6
7
8function Fn(){};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // trueObject.prototype.toString.call()
使用 Object 对象的原型方法 toString 来判断数据类型:
4.js为什么要进行变量提升?会导致什么问题?
变量提升的表现是,无论在函数中何处位置声明的变量,好像都被提升到了函数的首部,可以在变量声明前访问而且不会报错。
为什么执行变量提升?
提高性能:解析和预编译过程中的声明提升可以提高性能,让函数可以在执行时预先为变量分配栈空间。
容错性更好:声明提升还可以提高JS代码的容错性,使一些不规范的代码也可以正常执行
变量提升虽然有一些优点,但是他也会造成一定的问题,在ES6中提出了let、const来定义变量,它们就没有变量提升的机制。下面看一下变量提升可能会导致的问题:
1
2
3
4
5
6
7
8
9
10var tmp = new Date();
function fn(){
console.log(tmp);
if(false){
var tmp = 'hello world';
}
}
fn(); // undefined在这个函数中,原本是要打印出外层的tmp变量,但是因为变量提升的问题,内层定义的tmp被提到函数内部的最顶部,相当于覆盖了外层的tmp,所以打印结果为undefined。
1
2
3
4
5
6
7var tmp = 'hello world';
for (var i = 0; i < tmp.length; i++) {
console.log(tmp[i]);
}
console.log(i); // 11由于遍历时定义的i会变量提升成为一个全局变量,在函数结束之后不会被销毁,所以打印出来11。
三.js提高
1.简述事件循环:
先同步再异步,先微任务再宏任务。
js是一个单线程的。函数执行的过程是将函数执行上下文压入栈中,直到栈中清空,表示这个任务执行结束。为了保证异步的函数按顺序执行,出现了task queue(任务队列)。任务队列的作用是将各种事件或异步的操作进行通知时加入其回调函数。js引擎会不停的从任务队列中取出任务,压入到栈中执行,执行完成后再取下一个任务执行,如此一直到任务队列为空。如果为空,会一直等待新的任务出现。这种不停的等待处理事件的循环称之为事件循环。
2.虚拟dom是什么? 原理? 优缺点?
虚拟dom:虚拟dom的本质上是js对象或者是对真实DOM的抽象,状态变更时,记录新书和旧树的差异,最后把差异更新在真正的dom中。(定义及原理)
优点:
- 保证性能下限:虚拟DOM可以通过diff算法找出最小差异,然后批量的进行patch,这种操作虽然比不上手动优化,但是比起出包的DOM操作性能要好很多,因此虚拟DOM可以保证性能的下限。
- 无需手动操作DOM:虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大的提高了开发效率。
- 跨平台:虚拟dom的本质是js对象,而DOM与平台强相关,相比之下虚拟dom可以进行更方便地跨平台操作,例如服务器的渲染,移动端开发等等。
缺点:
无法进行极致优化:在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优
四.vue2
1.vue双向绑定的原理是什么?
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
2.Computed和Watch的区别
对于Computed:
它支持缓存,只有依赖的数据发生了变化,才会重新计算
不支持异步,当Computed中由异步操作时无法监听数据的变化
computed的值默认走缓存,计算属性是基于他们的响应式依赖进行缓存的,
也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。
如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed
如果computed属性的属性值是函数,那么默认使用get方法,函数的返回值就是属性的属性值;在computed中,属性有一个get方法和一个set方法,当数据发生变化时,会调用set方法。
对于Watch:
- 它不支持缓存,数据变化时,它就会触发相应的操作
- 支持异步监听
- 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
- 当一个属性发生变化时,就需要执行相应的操作
- 监听数据必须是data中声明的或者父组件传递过来的props中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
- immediate:组件加载立即触发回调函数
- deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。
当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch。
总结:
- computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
- watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
运用场景:
- 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。
- 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
3.vue中的keep-alive的作用是什么?怎么使用?
概念
keep-alive是Vue的内置组件,当它包括动态组件时,会缓存不活动的组件实例,该组件不会被销毁。
作用
用来缓存组件,避免多次加载相同的组件,减少性能的消耗,提高用户体验
属性
- include:字符串或正则表达式。只有匹配的组件会被缓存
- exclude:字符串或正则表达式。任何匹配的组件都不会被缓存
使用场景
比如:有一个列表页面和一个详情页面,那么用户可能会经常执行打开详情 => 返回列表 => 打开详情,那么就可以对列表组件使用进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染页面,从而节省内存开销。
使用方式
- 在App.vue中使用keep-alive组件缓存页面
- 按条件缓存使用include,excode判断是否存在缓存
- 将缓存name作为keep的组件,如果多个可以用逗号分开
- 将不换存的name为nokeep的组件
- 还可以使用属性绑定动态判断
- 在router目录的index.js中
- 使用meta:{keepAlive=true},表示需要缓存
- 在App.vue中进行判断
五.html
1. src和href的区别
src和href都是用来引用外部的资源,它们的区别如下:
- src: 表示对资源的引用,它指向的内容会嵌入到当前标签所在的位置。src会将其指向的资源下载并应⽤到⽂档内,如请求js脚本。当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执⾏完毕,所以⼀般js脚本会放在页面底部。
- href: 表示超文本引用,它指向一些网络资源,建立和当前元素或本文档的链接关系。当浏览器识别到它指向的⽂件时,就会进行下载资源,不会停⽌对当前⽂档的处理。 常用在a、link等标签上。
2.HTML5有哪些更新?
- 语义化标签:如:header–>定义文档的头部,nav,footer,article,section,aside
- 媒体标签:audio–>音频,video视频,source
- 数据存储:localStorage、sessionStorage
- 表单
- 进度条,度量器
- DOM查询操作
- Web存储
- input标签新增属性:placeholder、autocomplete、autofocus、required
- 其他:拖放,canvas(画布)、Geolocation(地理定位)、websocket(通信协议)
移除的元素有:
- 纯表现的元素:basefont,big,center,font, s,strike,tt,u;
- 对可用性产生负面影响的元素:frame,frameset,noframes;
3.对HTML语义化的理解
语义化是指根据内容的结构化(内容语义化),选择合适的标签(代码语义化)。通俗来讲就是用正确的标签做正确的事情。
语义化的优点如下:
- 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;
- 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护。
六.Ts(TypeScript)
TS是微软开发的一个开源的编程语言,通过在js的基础上添加静态类型定义构建而成。
1.TS常用类型:
- JS中已有类型
- 原始类型:number,string,boolean,null,undefined,symbol
- 对象类型:object(数组,函数,对象等)
- TS新增类型
- 联合类型,自定义类型,接口,元组,字面量类型,枚举,void,any等
2.ts中type和Interface的异同:
type:是类型别名,给一些类型的组合起别名,方便使用,例如type ID = string | number;
Interface:是接口。有点像type,可以用来代表一种类型组合,但它的范围更小一些,只能描述对象的结构。
它们写法有一点区别,type 后面需要用 = ,interface 后面不需要 = ,直接就带上 { 。
==type 和 interface 的不同点有:==
type 后面有 = ,interface 没有。
type 可以描述任何类型组合,interface 只能描述对象结构。
interface 可以继承自(extends)interface 或对象结构的 type。type 也可以通过 & 做对象结构的继承。
多次声明的同名 ,interface 会进行声明合并,type 则不允许多次声明。
七.css
1.浮动
1.为什么使用浮动。
很多网页布局效果,标准流是做不到,所以就要是用浮动来完成布局,浮动可以改变排列方式。
2.浮动引起的问题。
父元素高度塌陷,高度无法撑开。
与元素同级的非浮动元素会紧随其后(遮盖现象)。
如果一个元素浮动,则该元素之前的元素也需要浮动;否则会影响页面显示的结构
3.解决方案(清除浮动)。
给父元素固定高度。没有设置浮动时,父元素的高度是height:auto 高度是由子元素撑开的;这个方法的缺点是,因为设置了固定高度,它的维护性是很差的,适用于固定高度布局。
在浮动元素增加一个空div元素,设置样式 clear:both 要放在空元素上。缺点:布局添加了毫无意义的标签,如果有大量的使用无意义的div标签那么就造成很大的冗余。
为父元素设置 overflow:hidden 。缺点:受限于overflow:hidden的主要功能,如果子元素的尺寸大于父元素,或者子元素中的内容过多,那么多出来的这一部分将不会溢出,也不会显示出来,会直接被hidden起来;因此这个方法不适用于子元素的内容过多;
使用伪类 :after 只需要在父元素上添加一个class;类使用after伪元素,在父元素现有内容的末尾添加新的内容。添加的内容是一个display: block; clear: both;的空元素;
4.什么是重绘和重排?它们的区别是什么?
重绘:当页面元素样式的改变不影响布局时,浏览器重新对元素进行更新的过程叫做重绘。
重排:当页面元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程叫做重排也叫做回流。重绘不一定需要重排,重排必然会导致重绘
1、重排:当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。
1)添加、删除可见的dom
2)元素的位置改变
3)元素的尺寸改变(外边距、内边距、边框厚度、宽高等几何属性)
4)页面渲染初始化
5)浏览器窗口尺寸改变
2、重绘:是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。
4.dom树是怎么生成的?
页面得渲染流程总得俩说就五步,创建 DOM 树——创建 StyleRules——创建 Render 树——布局Layout(重排)——绘制 Painting(重绘)
第一步,用 HTML 分析器,分析 HTML 元素,构建一颗 DOM 树(标记化和树构建)。
第二步,用 CSS 分析器,分析 CSS 文件和元素上的 inline 样式,生成页面的样式表。
第三步,将 DOM 树和样式表,关联起来,构建一颗 Render 树(这一过程又称为 Attachment)。每个 DOM 节点都有attach 方法,接受样式信息,返回一个render对象(又名renderer)。这些 render 对象最终会被构建成一颗 Render 树。
第四步,有了 Render 树,浏览器开始布局,为每个 Render 树上的节点确定一个在显示屏上出现的精确坐标。
第五步,Render 树和节点显示坐标都有了,就调用每个节点 paint 方法,把它们绘制出来。
5. display的block、inline和inline-block的区别
(1)block: 会独占一行,多个元素会另起一行,可以设置width、height、margin和padding属性;
(2)inline: 元素不会独占一行,设置width、height属性无效。但可以设置水平方向的margin和padding属性,不能设置垂直方向的padding和margin;
(3)inline-block: 将对象设置为inline对象,但对象的内容作为block对象呈现,之后的内联对象会被排列在同一行内。
6.隐藏元素的方法有哪些
display: none:渲染树不会包含该渲染对象,因此该元素不会在页面中占据位置,也不会响应绑定的监听事件。
visibility: hidden:元素在页面中仍占据空间,但是不会响应绑定的监听事件。
opacity: 0:将元素的透明度设置为 0,以此来实现元素的隐藏。元素在页面中仍然占据空间,并且能够响应元素绑定的监听事件。
position: absolute:通过使用绝对定位将元素移除可视区域内,以此来实现元素的隐藏。
z-index: 负值:来使其他元素遮盖住该元素,以此来实现隐藏。
clip/clip-path :使用元素裁剪的方法来实现元素的隐藏,这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。
**transform: scale(0,0)**:将元素缩放为 0,来实现元素的隐藏。这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。
7.link和@import的区别
两者都是外部引用CSS的方式,它们的区别如下:
- link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
- link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
- link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
- link支持使用Javascript控制DOM去改变样式;而@import不支持。
8.CSS3中有哪些新特性
新增各种CSS选择器 (: not(.input):所有 class 不是“input”的节点)
圆角 (border-radius:8px)
多列布局 (multi-column layout)
阴影和反射 (Shadoweflect)
文字特效 (text-shadow)
文字渲染 (Text-decoration)
线性渐变 (gradient)
旋转 (transform)
增加了旋转,缩放,定位,倾斜,动画,多背景
9.Sass、Less 是什么?为什么要使用他们?
他们都是 CSS 预处理器,是 CSS 上的一种抽象层。他们是一种特殊的语法/语言编译成 CSS。 例如 Less 是一种动态样式语言,将 CSS 赋予了动态语言的特性,如变量,继承,运算, 函数,LESS 既可以在客户端上运行 (支持 IE 6+, Webkit, Firefox),也可以在服务端运行 (借助 Node.js)。
为什么要使用它们?
- 结构清晰,便于扩展。 可以方便地屏蔽浏览器私有语法差异。封装对浏览器语法差异的重复处理, 减少无意义的机械劳动。
- 可以轻松实现多重继承。 完全兼容 CSS 代码,可以方便地应用到老项目中。LESS 只是在 CSS 语法上做了扩展,所以老的 CSS 代码也可以与 LESS 代码一同编译。
10. 单行、多行文本溢出隐藏
单行文本溢出
1
2
3overflow: hidden; // 溢出隐藏
text-overflow: ellipsis; // 溢出用省略号显示
white-space: nowrap; // 规定段落中的文本不进行换行多行文本溢出
1
2
3
4
5overflow: hidden; // 溢出隐藏
text-overflow: ellipsis; // 溢出用省略号显示
display:-webkit-box; // 作为弹性伸缩盒子模型显示。
-webkit-box-orient:vertical; // 设置伸缩盒子的子元素排列方式:从上到下垂直排列
-webkit-line-clamp:3; // 显示的行数注意:由于上面的三个属性都是 CSS3 的属性,没有浏览器可以兼容,所以要在前面加一个
-webkit-
来兼容一部分浏览器。
11.为什么需要清除浮动?清除浮动的方式?
浮动元素引起的问题?
- 父元素的高度无法被撑开,影响与父元素同级的元素
- 与浮动元素同级的非浮动元素会跟随其后
- 若浮动的元素不是第一个元素,则该元素之前的元素也要浮动,否则会影响页面的显示结构
清除浮动的方式如下:
- 给父级div定义
height
属性 - 最后一个浮动元素之后添加一个空的div标签,并添加
clear:both
样式 - 包含浮动元素的父级标签添加
overflow:hidden
或者overflow:auto
- 使用 :after 伪元素。由于IE6-7不支持 :after,使用 zoom:1 触发 hasLayout**
12.display、float、position的关系
(1)首先判断display属性是否为none,如果为none,则position和float属性的值不影响元素最后的表现。
(2)然后判断position的值是否为absolute或者fixed,如果是,则float属性失效,并且display的值应该被设置为table或者block,具体转换需要看初始转换值。
(3)如果position的值不为absolute或者fixed,则判断float属性的值是否为none,如果不是,则display的值则按上面的规则转换。注意,如果position的值为relative并且float属性的值存在,则relative相对于浮动后的最终位置定位。
(4)如果float的值为none,则判断元素是否为根元素,如果是根元素则display属性按照上面的规则转换,如果不是,则保持指定的display属性值不变。
总的来说,可以把它看作是一个类似优先级的机制,”position:absolute”和”position:fixed”优先级最高,有它存在的时候,浮动不起作用,’display’的值也需要调整;其次,元素的’float’特性的值不是”none”的时候或者它是根元素的时候,调整’display’的值;最后,非根元素,并且非浮动元素,并且非绝对定位的元素,’display’特性值同设置值。
13.css3新增的选择符有哪些?
css3新增的选择器主要分为属性选择器,关系选择器,结构化伪类选择器,伪元素选择器四类,具体介绍如下:
1.属性选择器
属性选择器,可以根据网页标记的属性及属性值来标记。
属性选择器主要包括E[att^=value]
、E[att$=value]
和E[att*=value]
这三种性选择器。
1 | 1. 属性选择符 |
2.关系选择器
CSS3中的关系选择器主要包括子代选择器和兄弟选择器。
(1)关系选择器
(2)临近兄弟选择器
(3)普通兄弟选择器
3.结构化伪类选择器
结构化伪类选择器可以减少文档内class属性和id属性的定义,使文档变得更加简洁。
1 | 3.1 伪类选择符 |
4.伪元素选择器
伪元素选择器一般使一个标记后紧跟英文冒号”:”,英文冒号后是伪元素名。
伪元素选择器可以帮助我们利用CSS创建新标签元素,而不需要HTML标签,从而简化HTML结构。
::before 在元素前面插入内容
::after 在元素后面插入内容
注意
before和after创建一个元素 ,但是属于行内元素
新创建的这个元素在文档树中是找不到的,所以我们称为伪元素
语法: element:before
before和after必须有content属性
before在父元素内容的前面创建元素, after 在父元素内容的后面插入元素
伪元素选择器和标签选择器一 样,权重为1
八.ES6
1.如果new一个箭头函数会怎么样?
箭头函数是由ES6提出来的,他并没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。
new操作符的实现步骤如下:
创建一个对象
将构造函数的作用域给新对象
指向构造对象中的代码,构造函数中的this指向该对象
返回新的对象
所以,上面的第二,三步,箭头函数都是没有办法执行的
2.箭头函数和普通函数的区别
- 箭头函数比普通函数更加简洁
- 如果没有参数,就直接写一个空括号即可
- 如果只有一个参数,可以省去参数的括号
- 如果有多个参数,用逗号分割
- 如果函数体的返回值只有一句,可以省略大括号
- 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常见的就是调用一个函数:
- 箭头函数没有自己的this
- 箭头函数继承来的指向永远不会发生改变
- call(),apply(),bind()等方法不能改变箭头函数的this指向
- 箭头函数不能作为构造函数使用
- 箭头函数没有自己的arguments
- 箭头函数没有prototypes
- 箭头函数不能做Generator函数,不能使用yeild关键字