1、JS的数据类型
JavaScript的数据类型主要分为两类:原始类型,引用数据类型
- number,string,Boolean,undefined,null,symbol object,array,function
两种类型的存储方式:
- 原始类型:会被保存到栈内存中
- 引用类型:会被保存到堆内存中
两种类型之间的赋值方式:
- 原始类型:对值类型的拷贝、
- 引用类型:对引用地址的拷贝
判断js数据类型的方法有那些
- typeof
- typeof 返回数据类型,除了数组,对象,null是object外,其余数据类型都能正确判断
- instanceof
- instanceof返回布尔值,只能正确判断引用数据类型,而不能判断基本数据类型;instanceof可以用来测试一个对象在其原型链中是否存在一个构造函数的prototype属性
- constructor
- Object.prototype.toString.call()
- Object.prototype.toString.call方法能判断所有的数据类型
-
console.log(Object.prototype.toString.call(2)) // [object Number] console.log(Object.prototype.toString.call(true)) // [object Boolean] console.log(Object.prototype.toString.call('str')) //[object String] console.log(Object.prototype.toString.call([])) //[object Array] console.log(Object.prototype.toString.call(function(){})) //[object Function] console.log(Object.prototype.toString.call({})) // [object Object] console.log(Object.prototype.toString.call(undefined)) //[object Undefined] console.log(Object.prototype.toString.call(null)) //[object Null]
2、除了vuex,还有其他别的状态管理框架吗?
redux也可以给vue使用
3、vue与react的优缺点
vue与react的相同点:
- 都是用于构建用户界面的ui库
- 都比较轻便
- 都是虚拟dom,提高渲染速度
- 都是组件化思想,组件化开发
- 都有独立的路由系统,以及独立的状态管理库
vue的优势:
- 模板和渲染函数的弹性选择
- 文档清晰,上手快
- 渲染速度快,体积更小
react的优势:
- 更适合超大型的项目
- 更大的生态圈,维护的群体更大
- 兼容性更好,支持ie8
4、函数组件与类组件各有什么优缺点
- 类组件的性能消耗比较大,因为类组件需要创建类组件的实例,而且不能销毁
- 函数式组件性能消耗小,因为函数式组件不需要创建实例,渲染的时候就执行一下,得到返回的react元素后就直接把中间量全部都销毁
- 函数组件没有this、没有生命周期、没有状态state
- 类组件有this、有生命周期、有状态
5、vue项目中的优化
- 封装可复用组件,让代码更简洁
- 第三方插件按需引入
- blog.csdn.net/qq_37939251…
- www.cnblogs.com/queenDream/…
6、vue、react的缺点
vue缺点:
- 不支持ie8以下浏览时
- 没有在data中声明对象的属性,但是却在页面中使用,打印能拿到数据,但是页面却没有更改
- 市场份额占比少
react的缺点
7、react常用的生命周期
-
componentWillMount,
-
componentDidMount
-
componentWillUnmount
-
componentWillReciveProps
-
sholdComponentUpdate
8、react的组件通信
9、ES6新特性
-
1、let、const声明
- 不能声明提前
- 有块级作用域
- 防止重复声明变量
-
2、数组的扩展
- Array.from:将伪数组对象或可遍历对象转换为真数组
- 如果有一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,称为伪数组。典型的伪数组有,函数的arguments对象,以及大多数DOM元素集,还有字符串
- 伪数组没有数组所有的方法,调用forEach遍历会报错
- Array.from()方法解决
- 如果有一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,称为伪数组。典型的伪数组有,函数的arguments对象,以及大多数DOM元素集,还有字符串
- Array.of(v1,v2,v3) 将一系列值转换成数组
- Array.of()基本上可以用来替代Array,并且不存在由于参数不同而导致的重载,而且他们的行为非常统一
- 数组实例的find()和findIdex(): 找出第一个符合条件的数组成员,如果没有符合的成员,find返回undefined,findIndex返回-1
- 数组的实例includes():返回一个布尔值,表示某个数组是否包含给定的值
- entries、keys、values
- Array.from:将伪数组对象或可遍历对象转换为真数组
-
3、箭头函数
-
4、扩张运算符
-
扩展运算符允许一个数组分割,并且将各个项作为分离的参数传给函数,用在字符串或数组前面,用于将字符串或数组进行拆解
-
合并数组
-
let arr = [1,2,3] let arr2 = [5,6,7] let arr3 = [...arr, ...arr2] -
数组去重
-
let arr = [1,2,3,4,5,6,1,2,3,4,5] [...new Set(arr)]
-
-
-
-
5、解构赋值
-
6、模板字符串
-
7、class
-
8、promise
-
9、for...of循环
-
10、import、export
10、什么叫类数组
看着像是数组,能通过下标输出对应的数据,但数组能调用的方法它都不能调用,且其数据结构也和数组不同,数据类型为object,实际上argument是类数组
11、原型链
当我们访问对象的一个属性或者方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中查找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined
12、说一下箭头函数和普通函数的区别
1、箭头函数是匿名函数,不能作为构造函数,不能使用New
2、箭头函数不能绑定arguments,取而代之用rest参数解决
3、箭头函数没有原型属性
4、箭头函数没有自己的this,默认指向定义它时所处上下文的对象的this指向,如果没有上下文对象,this就指向window
5、箭头函数的this指向永远不变
6、箭头函数语法更加简洁
13、箭头函数的this指向
1、箭头函数的this,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象
2、箭头函数的this指向不能改变
14、说说你对闭包的理解
函数嵌套函数,函数内部的可以访问函数外包的变量
15、扩展运算符用来干嘛
常用来合并数组,以及数组去重
16、说一下null和undefined的区别
- null表示空值,
- undefined表示未定义,
- 两者类型不同,typeof null 是object,typeof undefined 是undefined
17、用过什么类型的本地存储,他们的区别是什么?
-
localStorage
- 有效期:长期有效,除非主动删除数据,否则数据永远不会消失
- 存储大小:一般大小是5Mb
- 存储位置:保存在客户端,不与服务器进行交互通信
- 存储内容类型:字符串类型
- 删除缓存:localStorage.removeItem('key')
- 清楚所有缓存: localStorage.clear()
-
sessionStorage
- 有效期:仅在当前会话下有效,只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源的另一个页面,数据依然存在,但是sessionStorage在关闭了浏览器窗口后就会销毁
- 存储大小为5MB
- 存储位置:保存在客户端,不与服务器进行交互通信
- 存储内容的类型:只能存储字符串类型
- 手动删除缓存:sessionStorage.removeItem("key");
- 清楚缓存:sessionStorage.clear();
-
cookie
- 有效期: expre设置的有效日期
- 存储大小不能超过4kb
18、怎么解决跨域问题
19、用过postMessage吗
没有用过postMessage,
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档,多窗口,跨域消息传递
20、sass或less与css相比有什么优势?他们之间有什么不同?
- sass和less比css多出了很多功能,如果变量,嵌套,运算,继承,颜色处理,函数等,易于阅读
- less在客户端解析
- sass在服务器端分析
21、cookie怎么设置过期时间(设置expire)
给expire设置一个过去的时间
22、函数组件与类组件各有什么优缺点
-
函数式组件性能比类组件的性能要高,因为类组件使用的时候需要实例化,而函数式组件直接执行函数取返回结果即可
-
函数式组件没有this,没有生命周期,没有状体(state)
-
类组件有this,有生命周期,有状态
23、你们是怎么管理本地代码的?有回滚过代码吗?
我们本地代码版本管理是git
回滚代码
指定一个目标版本,把本地的版本回滚指定版本上,本地代码被目标版本覆盖,再把覆盖后的代码推到远程分支上
24、用过jq吗
之前的项目有用过,用jq操作dom,以及动画之类的
25、常见的前端工具
postman,nvm,sourcetree,谷歌,谷歌插件
26、ES7
ES7在ES6的基础上添加了,求幂运算符(**),Array.prototype.includes(方法、函数作用域中严格模式的变更)
ES8 新增async、await异步解决方案
27、怎么实现rem布局
设置根元素大小,设计图纸的大小除以根元素的大小,得到rem单位
28、高阶组件
- 代码复用
- 渲染劫持
- State 抽象和更改
- Props 更改
29、this的指向
- 全局环境下,this指向window
- 如果给元素的事件行为绑定了函数,该函数中的this指向被绑定的那个元素
- 构造函数中的this指向当前实例
- 定时器函数中的this 指向window
- 调用函数时所处的指向上下文,就是this指向
30、computed和watch的区别
-
计算属性是计算值,只有依赖的属性发生变化时,才会执行方法
-
watch是监听观察动作,有改变就执行,
-
computed具有缓存性,数据变化先读取缓存,值没变不操作,
-
而watch没有缓存,一改变,直接执行
31、什么是执行上下文
执行上下文就是当前JavaScript代码被解析和执行时所在环境的抽象概念,js中运行任何的代码都是在执行上下文中运行的
32、执行上下文的类型
- 全局执行上下文:window对象
- 函数上下文:每次调用函数时,都会为该函数创建一个新的执行上下文。
- 每个执行上都有this,作用域,变量
33、前端页面优化方法
- 减少http的请求
- 合并压缩资源
34、vue项目中的性能优化
- 不要在模板中写过多的表达式
- 循环调用子组件时添加key
- 频繁切换使用v-show,不频繁切换使用v-if
- 引用第三方库,按需加载
35、react中的key
帮助我们跟踪那些项目已更改、添加、从列表中删除,key是独一无二的,可以让我们高效的去定位元素,并且操作它
36、vue中key的作用
当vue用v-for正在更新已渲染过的元素列表时,它默认就地复用策略。如果数据项的顺序改变了,Vue将不会移动DOM元素来匹配数据项的顺序,而是简单的复用此处每个元素,并且确保它在特地索引下显示已被渲染过的每个元素
37、从输入一个url到浏览器页面展示都经历了哪些过程?
- 1、在浏览器地址栏中输入url
- 2、浏览器解析url获取主机名
- 3、浏览器先查看浏览器缓存,如果缓存中有,会直接在屏幕中显示页面内容,如果没有需要域名解析(DNS解析),解析获取相应的IP地址,发送http请求,同时缓存
- 4、浏览器将端口号从url中解析出来
- 5、浏览器向服务器发起TCP连接,与浏览器建立TCP三次握手
- 6、握手成功后,浏览器向服务器发送http请求
- 7、服务器处理接收到的请求,将数据返回至浏览器
- 8、浏览器收到http响应
- 9、浏览器关闭连接,并解析文档
38、浏览器渲染过程
- 1、解析HTML,生成DOM树
- 2、解析css,生成cssom树
- 3、将DOM树和CSSOM树结合,生成渲染树
- 4、回流:根据生成的渲染树,进行回流,得到节点的集合信息
- 5、重绘:根据渲染树及其回流得到的集合信息,得到节点的绝对像素
- 6、将像素发送给GPU,展示在页面上
39、回流与重绘
回流:当渲染树中的一部分或者全部因为元素的规模尺寸,布局,隐藏等问题发生改变而需要重建的过程叫做回流。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会回流的,因为要构建渲染树。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受到影响的部分到屏幕中,该过程称为重绘
重绘:当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不影响布局的,比如background-color,则称为重绘
回流必定会引起重绘,但重绘不一定引起回流
40、Object.defineProperty的缺陷
- 无法检测到对象属性的新增或删除
- 无法监听数组变化
41、call、apply、bind的区别
- call、apply的第一个参数都是指定的对象,该对象就是函数的执行上下文
- call能传入多个参数
- apply只能传入两个参数,第二个参数是数组
- bind方法会返回一个执行上下文被改变的函数,而不会立即执行该函数,call、apply会直接执行该函数,bind的参数与call相同
42、函数防抖与函数节流
函数防抖:当持续触发事件时,一定时间端内没有再触发事件,事件处理函数才会执行一次
函数节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
43、loader和plugin的区别
loader用于加载某些资源文件,因为webpack本身只能打包CommonJs规范的js文件,对于其他资源,例如css,图片等,是没有办法加载的,这就需要对应的loader将资源转换
plugin用于扩展webpack的功能,直接作用于webpack,loader只专注于转换文件,而plugin不仅局限于资源加载
loader是运行在打包之前的,而plugin可能运行在打包之前,也可能运行在打包的过程中,或者打包完成之后。plugin不局限于打包,资源的加载,还有其他的功能,所以它是在整个编译周期都起作用的
44、那些情况会引起内存泄漏
- 闭包
- 意外的全局变量
- 全局变量在页面关闭之前不会被释放
- 使用严格模式能避免意外的全局变量
- 被遗忘的定时器
- 没有清理的DOM元素引用
45、浅拷贝与深拷贝
- 浅拷贝
- 浅拷贝就是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果一个对象改变了这个地址,就会影响到另一个对象
- 重新在堆中创建内存,拷贝前后对象的基本数据互不影响,但拷贝前后对象的引用类型因共享同一块内存,会互相影响
- 深拷贝
- 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
- 从堆内存中开辟一个新的区域存放新对象,对对象的子对象进行递归拷贝,拷贝前后两个对象互不影响
46、CommonJS规范的特点
- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块是同步加载的,即只有加载完成,才能执行后面的操作
- 模块在首次执行后就会缓存结果,如果想要再次执行,可清除缓存
- CommonJS输出是值的拷贝(即,require返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值) 这个对于引用类型的变量来说还是会有一点歧义的 (也就是如果修改的是引用类型,还是会受到影响)
47、为什么说CommonJS不太适合于客户端
- 服务器端所有的模块都存放在本地硬盘中,可以同步加载完成,等待时间就是硬盘的读取时间
- 浏览器,所有的模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于’假死状态‘
AMD规范是产生是因为CommonJS是同步加载的,不适应于客户端(浏览器环境),因此AMD这种异步加载的模块就诞生了
48、AMD和CMD的区别
- AMD中会把当前模块的依赖模块放到dependencies中加载,并在factory回调中拿到加载成功的依赖
- CMD一般不在dependencies中加载,而是写在factory中,使用require加载某个依赖模块
- AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同,二者皆为异步加载模块
- 1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
- 2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
49、CommonJS与ES6 Modules规范的区别
- CommonJS模块是运行时加载,ES6Modules是编译时输出接口
- CommonJS输出的是值的拷贝;ES6Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变
- CommonJS导入的模块路径可以是一个表达式,因为它使用的是require()方法;而ES6Modules只能是字符串
- CommonJS this指向当前模块, ES6Modules this指向undefined
- 且ES6 Modules中没有这些顶层变量:
arguments、require、module、exports、__filename、__dirname
关于第一个差异,是因为CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成
50、父组件和子组件之间的生命周期执行顺序
初次渲染就会触发的生命周期
- beforeCreate(), created()
- beforeMount(), mounted()
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。组件的销毁操作是先父后子,销毁完成的顺序是先子后父
加载渲染过程,子组件在父组件的beforeMount和Mounted之间渲染
- 父beforeCreate -> 父 created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMounted -> 子 mounted -> 父mounted
子组件的更新过程
- 父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新过程
- 影响到子组件: - 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
- 不影响子组件: - 父beforeUpdate -> 父updated
销毁过程
- 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
51、vue-router钩子函数
1、全局钩子
beforeEach(to,form,next)
afterEach(to,form,next)
2、组件内的钩子
beforeRouterEnter
beforeRouteLeave
beforeRouteUpdate
52、304缓存原理
304是http状态码,服务器用来标识这个文件没修改,不返回内容,浏览器在接收到这个状态码后,会使用浏览器已缓存的文件
客户端请求一个页面(A)。服务器返回页面A,并在给A加上一个Etag。客户端展现该页面,并将页面连同Etag一起缓存。客户再次请求页面A,并将上次请求时服务器返回的Etag一起传递给服务器。服务器检查该Etag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304(未修改----Not Modified)和一个空的响应体
总的来说
浏览器第一次向服务器请求资源,服务器在返回这个资源的时候会加上一个Etag,浏览器接收到后缓存资源和Etag,当下次浏览器再次请求这个资源的时候,会将上次请求服务器返回的Etag一起传递给服务器,服务器检查该Etag,并列出该页面自上次浏览器请求之后还未被修改,直接返回304和一个空的响应体,告诉浏览器从自己的缓存中拿
53、什么是http协议
HTTP超文本传输协议,通过浏览器和服务器进行数据交互,进行超文本(文本、图片、视频等)传输的规定。也就是说,http协议规定了超文本传输所要尊守的规则
特点/有点:
由于其简捷、快速的方式,适用分布式超媒体信息系统
54、attribute和property的区别是什么?
attribute是dom元素在文档中作为html标签拥有的属性
property就是dom元素在js中作为对象拥有的属性
所以:
对于html的标签属性来说,attribute和property是同步的,是会自动更新的,但是对于自定义的属性来说,他们是不同步的
55、react、vue中的key有什么作用?(key的内部原理)
1、虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,vue会根据心数据生成新的虚拟DOM,随后vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下:
对比规则:
-
旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变,直接使用之前的真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
-
旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到页面
用index作为key可能会引发的问题:
-
若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新==> 界面效果没问题,但效率低
-
如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
开发中如何选择key:
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证、学号等唯一值
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
面试笔记
1、说一下工作中解决过的比较困难的问题,说一下自己项目中比较有亮点的地方
2、你了解浏览器的事件循环吗?
2.1为什么js在浏览器中有事件循环的机制?
因为js是单线程的
Javascript是主要用途是与用户互动,以及操作DOM。如果它是多线程的会有很多复杂的问题要处理,比如有两个线程同时操作DOM,一个线程删除了当前的DOM节点,一个线程是要操作当前的DOM阶段,最后以哪个线程的操作为准?为了避免这种,所以js是单线程的。即使h5提出了web worker标准,它有很多限制,受主线程控制,是主线程的子线程
通过event loop 非阻塞的实现
2.2两种任务
宏任务: setTimeout,setUntervel, I/O操作
微任务: new Promise().then(),MutaionObserver'
2.3为什么要引入微任务,只有宏任务可以嘛?
页面渲染事件,各种io的完成事件等随时被添加到任务队列中,一直会保持先进先出的原则执行,我们不能准确的控制这些事件被添加到任务队列中的位置。但是这个时候突然有高优先级的任务需要尽快执行,那么一种类型的任务就不合适了,所以引入了微任务队列
浏览器的事件循环
1、执行全局同步代码,这些同步代码有一些是同步语句,有一些是异步语句
全局代码执行完毕后,调用栈会清空
从微队列中取出位于队首的回调任务,放入调用栈中执行,执行完后微队列长度减一
继续取出队首任务,放入执行栈中执行,以此类推,直到把微队列中的所有任务都执行完毕。
微队列中所有任务都执行完毕,微队列为空,调用栈也为空,取出宏任务队列中位于队首的任务,放入调用栈中执行,执行完毕,调用栈为空
事件的捕获和冒泡机制,你了解多少?
捕获:自顶向下
冒泡:自底向上
2、window.addEventListener监听的是什么阶段的事件
冒泡阶段:
window.addEventListener('click',()=>{
})
捕获阶段:
window.addEventListener('click',()=>{
},true)
3、平时有哪些场景用到了这个机制呢?
事件委托
4、一个历史页面,上面有若干个按钮的点击逻辑,每个按钮都有自己的click事件
需求:给每一个访问的用户添加了一个属性,banned=true,此用户点击页面上的任何按钮或者元素,都不可响应原来的函数,而是直接alert提升,你被封禁了
解决思路:在最上一级使用事件捕获
window.addEventListener('click',()=>{
if(banned){
e.stopProgagtion
}
},true)
工作中用过防抖和节流嘛?
你了解promise嘛?平时用的多嘛?
promise.all你知道有什么特性嘛?