1,vue的列表组件为何要加key?css
增长diff算法的同级比较效率, key是惟一索引,能够一目了然的看到同一级的是否变化,若是没有key, 那就只能一个个的去进行比较了。html
2, ['1', '2', '3'].map(parseInt)结果是多少? [1, NaN, NaN];前端
map函数的默认参数为 item,index和arr, parseInt的参数为 字符串 和 进制数。 因此转换为求 parseInt('1', 0) parseInt('2', 1) parseInt('3', 2)的值。
vue
关于求进制数: parseInt(str, radix)node
radix的范围是在 2和36之间, 当进制数为0的时候,默认为10进制。 str的值从第一个开始不能大于对应的进制数。若是第一个数大于对应的进制数,则返回NaN。 若是第一个数一旦小于进制数,就开始作运算,直到碰到大于进制数的就停下来。webpack
exp: parseInt('43256789', 5); 4*5*5 + 3* 5 + 2 = 117ios
3,节流和防抖, 有什么区别, 怎么实现?es6
节流: 限制一段时间内函数只执行一次。web
防抖: 函数一段事件执行一次, 若是在这段时间内被触发,则从新计算时间。ajax
参考 : http://www.javashuo.com/article/p-fcnnhvlc-dt.html
4, 介绍下 Set Map WeakSet WeakMap的区别
set 对应的是数组, 相似数组, 容许存储任何类型的值,但没有重复的成员。set内部的比较是用全等。 size是数量, 方法有keys, values, entries和forEach。
weakset: 只能储存对象引用。 因为是弱引用, 只要没被其余的变量或者属性引用,就会被回收。 因此没法被遍历,没法拿到全部的元素,也没有size属性。
Map: 对应的是obj对象。 是以key,value的形式存储的。 key能够为任意数据结构, 而对象的key只能为字符串。
weakmap: 键值对的集合,只接受对象做为键名。弱引用,不能遍历。
5,深拷贝
function isObject(obj) { return typeof obj === 'object' && obj !== null; } function deepClone(target, hash = new WeakMap()) { if (!isObject(target)) return target; if (hash.has(target)) return hash.get(target); let clone = Array.isArray(target) ? []: {}; hash.set(target, clone); for (let key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { if (isObject(target[key])) { clone[key] = deepClone(target[key], hash); } else { clone[key] = target[key]; } } } return clone; }
6,setTimeout, promise , async await 有什么区别
setTimeout : 定时器的回调函数是 属于 宏任务, 当前执行栈清空后执行。
promise: promise的then函数是微任务, 放在微任务队列, 当前同步任务执行完以后就立马执行微任务队列。
async / await: async函数返回promise, 正常状况下await后面也是promise,若是不是promise,则会被变为promise,而且当即resolve。
7, async / await 使如何经过同步写法的方式来实现异步的。
async/await是 generator的语法糖, 在遇到异步的操做时, 会暂停该函数, 交出执行权, 当该异步完成后, 再继续执行。
8, var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]; 扁平化数组而且去重 按顺序排列
[...new Set(arr.flat(Infinity))].sort((a,b) => a-b);
9,js异步解决方案的发展历程 以及优缺点
1,callback: 回调地狱, 强耦合,不能用try catch捕获错误。不能return
2, promise: 解决回调地狱, 采用链式调用。 不过语义很不清楚,一眼看去都是then的堆积。没法取消promise
3,generator:能够控制函数的执行, 也能够进行函数内外的数据交换和错误的处理。
4,async: 代码简洁,符合语义。由于有自动执行器,因此代码量少。
10, 如何实现一个new
function _new(fn, ...args) { let obj = Object.create(fn.prototype); let result = fn.apply(obj, args); return Object.prototype.toString.call(result) === '[object Object]' ? result : obj; }
11, 简单讲解一下http2的多路复用
多路复用代替了http1的序列和阻塞机制,全部相同域名的请求都经过一个tcp链接并发完成。 http1并发多个请求须要多个tcp链接,浏览器通常会限制为6个。 http2的多路复用会消除因多个tcp链接带来的延时和内存消耗,并且单个链接上能够进行并行交错的请求和响应,互相不干扰。
12,tcp的三次握手和四次挥手
三次握手: 客户端发送一个带SYN标志的数据包给服务器,服务器收到后返回一个带有SYN/ACK标志的数据包表示确认,客户端再回传一个带ACK标志的数据包表示握手结束。
四次挥手: 客户端进程发出连续释放报文,并中止发送数据。 服务器收到连续释放报文,发出确认报文。 服务器将最后的数据发送完毕,向客户端发送连续释放报文。 客户端收到后,发出确认。 服务器收到客户端的确认, 进入closed状态。
13, cookie和token都放在header中, 为何不劫持token呢
token是为了防止csrf跨站请求攻击的。 浏览器在发送http时会自动带上cookie, 可是不会自动带上token。
14, var a = ? , if (a == 1 && a == 2 && a== 3) { console.log(1)} a在什么状况下 会打印1;
由于用 == 号比较的时候,会进行隐式转换, 对于对象来讲,隐式转换会调用自身的toString或者valueof方法。
var a = { n: 1, toString() { return this.n++ } }
a==1 && a==2 && a==3 ; true
toString 改成valueof也能够
若是a为数组 有一种方法更简单
var a = [1,2,3];
a.toString = a.shift;
a == 1 && a==2 && a==3 ; true
15,bfc及其应用
bfc为块级格式化上下文,bfc内部的元素不管怎样都不会影响外部元素。
触发条件: html元素, display为inline-block table-cell和table-caption,position不为relative或static overflow为 auto scroll或hidden float的值不为none
基本用途: 去除margin重叠, 清除浮动 , 流体布局。
16,在vue中,为什么子组件不能修改父组件传递的prop,若是修改了,vue是如何监控到属性的修改并给出警告的?
若是传递的prop为引用类型,子组件修改的话不会报错,若是是基本类型,子组件修改的时候会失败,而且vue会给出警告。
由于在修改prop的时候,触发set会判断是否在根组件或者是更新子组件。 若是不是那就说明更新的是props, 会给出警告。
17,双向绑定和vuex是否冲突
在严格模式下,的确冲突, 由于在数值改变时,v-model会直接修改属性值,可是这个修改不是在mutaition中操做的,因此会报错。 解决方法:
1,在input中绑定vuex的state, 监听input或change事件,在回调中操做mutation
2,使用带有setter的双向绑定计算属性。
18,为何一般在发送数据埋点请求的时候使用的是1×1像素的透明gif图片
此方法主要应用于只需向服务器发送数据,无需服务器回复。
避免跨域, 不会阻塞页面, gif在全部格式中体积最小。
19,vue的响应式原理 Object.defineProperty有什么缺陷, 为何vue3改用了proxy?
Object.defineProperty没法监控到数组下标的变化,因此vue内部重写了数组的一些操做方法,但也只是针对了这几种方法作了hack,经过下标操做数据的话,vue就监听不到。
Object.defineProperty只能劫持对象的属性,所以须要对对象的每一个属性进行遍从来进行数据的监听,若是属性也是对象,还须要进行深度遍历。
es6的proxy有如下两个优势: 1,能够劫持整个对象,提升性能, 那么固然数组新加的属性就能够监听到了。
20,div垂直水平居中
参考 http://www.javashuo.com/article/p-eiwnvhgj-ea.html
21,如何实现冒泡排序,时间复杂度是多少, 如何改进
function bubble(arr) { const len = arr.length - 1; for (let i = 0; i < len; i++) { let isOk = true; for (let j = 0; j < len - i; j++) { if (arr[j] > arr[j+1]) { [arr[j], arr[j+1]] = [arr[j+1], arr[j]]; isOk = false; } } if (isOk) break; } return arr; }
22, 某公司1到12月份的销售额存在一个对象中 {1:222, 2:123, 5:888},请把数据处理为以下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]。
Array.from({length: 12}).map((_,index) => obj[index + 1] || null);
23,要求设计LazyMan类, 知足如下的功能
LazyMan('Tony'); // Hi I am Tony LazyMan('Tony').sleep(10).eat('lunch'); // Hi I am Tony // 等待了10秒... // I am eating lunch LazyMan('Tony').eat('lunch').sleep(10).eat('dinner'); // Hi I am Tony // I am eating lunch // 等待了10秒... // I am eating diner LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food'); // Hi I am Tony // 等待了5秒... // I am eating lunch // I am eating dinner // 等待了10秒... // I am eating junk food
class LazyClass { constructor(name) { this.eventQueen = []; this.name = name; this.init(); } init() { console.log('Hi I am ' + this.name); setTimeout(() => { this.next(); }); return this; } eat(things) { let that = this; this.eventQueen.push(function () { console.log('I am eating '+ things); that.next(); }); return this; } sleep(time) { let that = this; this.eventQueen.push(function () { setTimeout(() => { console.log('等待了' + time + 's'); that.next(); }, time*1000); }) return this; } sleepFirst(time) { let that = this; this.eventQueen.unshift(function () { setTimeout(() => { console.log('等待了' + time + 's'); that.next(); }, time *1000) }); return this; } next() { let fn = this.eventQueen.shift(); fn && fn(); } } function LazyMan(name) { return new LazyClass(name); }
24,比较opacity:0, visibility:hidden, display:none的优劣和适用场景
均可以让元素不可见,
display:none: 不渲染dom,不占据空间,没法监听事件,属性改变时会引发页面回流, transition不支持。
visibility:hidden : 占据空间,渲染dom, 没法监听事件, 属性改变引发页面重绘。
opacity:0 : 占据空间,渲染dom, 能够监听事件, 配合transition进行显示和隐藏, 属性改变引发页面重绘
25,箭头函数和普通函数的区别是什么? 构造函数可使用new生成实例,箭头函数能够吗?
箭头函数和普通函数的区别: 箭头函数没有this, 箭头函数使用的是箭头函数所在函数的this,由于也没法使用call apply和bind来绑定和改变this。
箭头函数没有arguments属性,若是要使用能够用rest参数, 不可使用yield命令,所以不能用做Generator函数。
箭头函数不能当作构造函数,由于不能使用new命令,不然会报错。
26,给定两个数组,计算他们的交集; 例如:给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。
function getSame(arr1, arr2) { let result = []; let newArr2 = [...arr2]; arr1.forEach(item => { let index = newArr2.indexOf(item); if (index > -1) { newArr2.splice(index, 1); result.push(item); } }); return result; }
27,已知代码: <img src="1.jpg" style="width:480px!important;”> 如何修改才能让图片宽度为300px? 下面的代码不能修改...
max-width: 300px;
28, token加密的步骤
后端经过随机数加签名来生成一个token, 前端拿到token后在接口调用的时候添加token,后端判断请求中携带的token进行验证。
29,模拟实现一个promise.finally
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), error => P.resolve(callback()).then(()=> { throw error}) ) }
30,随机生成一个长度为 10 的整数类型的数组,例如 [2, 10, 3, 4, 5, 11, 10, 11, 20]
,将其排列成一个新数组,要求新数组形式以下,例如 [[2, 3, 4, 5], [10, 11], [20]]
。
function getNewArray(arr) { let newArr = [...new Set(arr)]; let hash = {length: 0}; newArr.forEach(item => { let index = Math.floor(item / 10); if (!hash[index]) { hash[index] = []; hash.length++; } hash[index].push(item); }); return Array.from(hash); }
31,实现一个字符串匹配算法,从长度为n的字符串s中,查找是否存在字符串t,t的长度是n,若存在则返回位置
function findStr(str, part) { if (part.length > str.length) return -1; for (let i = 0; i < str.length - part.length; i++) { if (str.slice(i, i+part.length) === part) { return i; } } return -1; }
32,数组里面有10万个数据,取第一个元素和第十万个元素时间相差多少
数组本质上也是对象,其索引是字符串,查找数组的元素就是经过索引查找哈希表的过程,因此消耗的时间大体同样。
33,vue的父组件和子组件生命周期钩子执行顺序是什么
渲染: 父 beforeCreate --> 父created --> 父beforeMount --> 子beforeCreate --> 子created --> 子beforeMount --> 子mounted --> 父mounted
更新: 父 beforeUpdate --> 子beforeUpdate --> 子updated --> 父updated
销毁: 父beforeDestroy --> 子beforeDestroy --> 子 destroyed --> 父 destroyed
34,promise.all的使用、原理和错误处理
promise.all的参数必须有Iterator接口,参数的全部子元素都必须是promise示例,若是不是则自动调用promise.resolve转为promise示例。
promise.all的状态由子元素的状态决定,只有子元素的状态都变成fulfilled,promise.all的状态才变为fulfilled,此时子元素的返回值组成一个数组,传给promise.all的回调函数。只要子元素有一个变为rejected,promise.all就变成rejected,返回第一个被rejected的子元素的返回值。
35,axios、fetch和ajax有什么区别
ajax: XMLHttpRequest对象,最先出现的发送请求的技术。
axios: 使用promise对ajax的封装。
fetch:fetch是es6的用来代替ajax的异步请求技术。是原生的js,基于promise,为了扩展其余应用。
36,vue组件的通讯方式
1,props、$emit
2,$emit 、$on
3,$attrs和$listeners
4,provide、inject
5,vuex
37,vuex工做原理
vuex是专门使用于vue的状态管理工具,它集中管理全部组件的状态,并提供一系列方法来获取和操做状态。
读取的数据和状态存放在state里面, getter为state的计算属性,state的值发生变化它也会被从新计算,改变状态就是提交mutations,若是是异步的任务则须要封装在action里面。 module是将store分割成模块,每一个模块都具备上面的各个属性。
38,什么是虚拟dom, 为何虚拟dom会提升性能
虚拟dom其实就是js的一个对象,经过对象来描述真实的dom。
频繁的操做真实dom,效率是很低的。 virtual dom经过使用数据对象来描述dom树,在每次视图更新的时候,比较与真实dom的差别, 只须要一次性更新有变化的dom,减小不少没必要要的dom操做。 因此说 virtual是用来减小大范围频繁的重绘。
39,vue计算属性和方法的区别
计算属性是基于响应式依赖来进行缓存的,只有在相关的依赖发生变化的时候才进行求值。 而方法的话就不会进行缓存,老是会执行该函数。
40,vue事件总线
var bus = new Vue(); 定义事件 : bus.$on('事件名称', fn); 触发事件: bus.$emit('事件名称', fn);
41,spa单页面的理解,优缺点是什么
在页面初始化时加载相应的html、css和js,一旦页面加载完成,spa不会由于用户的操做而进行页面的从新加载或者跳转,而是使用路由机制实现内容的切换。
优势: 用户体验好,内容的改变不须要加载整个页面,避免了重复的渲染。 对服务器压力小。 先后端分工明确。
缺点: 初次加载耗时多, 前进后退路由管理, seo难度较大
42,npm run dev以后的执行过程
首先会在项目的根目录下寻找 package.json文件, 找到scripts对象对应的dev,执行对应的命令, webpack-dev-server 为一个node服务器,而后就会把当前的页面展现在node服务器的对应端口上。
43,为何组件中的data是一个函数
由于组件是用来复用的,若是组件中的data是一个对象,那么每个组件都是公用的一个data对象,data的值会相互影响,而当data为一个函数,那么每一个示例能够维护一份被返回的对象,互相独立,互不影响。
44,vue是如何实现双向数据绑定的
http://www.javashuo.com/article/p-pdmiibct-ek.html
45,var、let和const的区别
let和const不存在变量提高, 而且只在当前的代码块中有效。 并且还存在暂时性死区, 不容许重复的声明。 const 声明的常量不能改变值,不过当值 为对象时能够改变内容。
46,vue-router的router-link和a标签有什么区别
router-link渲染出来的也是a标签,看vue-router的源码,router-link接管了渲染的a标签的行为, click的时候阻止了a标签默认的跳转,取得to的属性,经过history或者hash来进行页面的切换,并不会刷新页面。
47,实现convert方法,将数组的list转换为tree结构
var list = [ {id:1,name:'部门A',parentId:0}, {id:2,name:'部门B',parentId:0}, {id:3,name:'部门C',parentId:1}, {id:4,name:'部门D',parentId:1}, {id:5,name:'部门E',parentId:2}, {id:6,name:'部门F',parentId:3}, {id:7,name:'部门G',parentId:2}, {id:8,name:'部门H',parentId:4} ]; // 转换后的结果以下 let result = [ { id: 1, name: '部门A', parentId: 0, children: [ { id: 3, name: '部门C', parentId: 1, children: [ { id: 6, name: '部门F', parentId: 3 }, { id: 16, name: '部门L', parentId: 3 } ] }, { id: 4, name: '部门D', parentId: 1, children: [ { id: 8, name: '部门H', parentId: 4 } ] } ] }, ··· ];
参考了一个大佬对对象引用的操做: function convert(list) { let result = []; let map = list.reduce((prev, next) => { prev[next.id] = next; return prev; }, {}); //这时候 list 和 map里面的元素是相互引用的, for (let item of list) { if (item.parentId === 0) { result.push(item); continue; } if (item.parentId in map) { let parent = map[item.parentId]; parent.children = parent.children || []; parent.children.push(item); // 后面的新增children 都会影响到根元素的内容 } } return result; }
48,实现Promise.race
Promise._race = function (promises) { return new Promise((resolve, reject) => { promises.forEach(promise => { promise.then(resolve, reject); }) }) }
49,实现模糊查询结果的关键词高亮
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .red{ color:red; } </style> </head> <body> <input type="text" id="input"> <ul id="list"></ul> <script> let list = ['ab', 'abc', 'abd', 'abcdef', 'abcef','abcdefghij']; // 模拟查询的数据 let container = document.getElementById('list'); let input = document.getElementById('input'); function debounce(fn) { // 防抖, 用户输入完成后300ms触发 let timeout; return function (...args) { if (timeout) { clearTimeout(timeout); timeout = null; } timeout = setTimeout(() => { fn.apply(this, args); },300) } } function handleInput(name) { let reg = new RegExp(`(${name})`); const result = list.reduce((prev, next) => { if (reg.test(next)) { let match = RegExp.$1; let str = `<li>${next.replace(match, '<b class="red">'+match+'</b>')}</li>`; prev.push(str); } return prev; }, []); if (name === '') { container.innerHTML = ''; return; } if (result.length === 0) { container.innerHTML = '暂无结果'; } else { container.innerHTML = result.join(''); } } input.addEventListener('input', debounce(e => { handleInput(e.target.value); })) </script> </body> </html>
50,已知数据格式,实现函数fn找到对应id的全部父级id
const data = [{
id: '1',
name: 'test1',
children: [
{
id: '11',
name: 'test11',
children: [
{
id: '111',
name: 'test111'
},
{
id: '112',
name: 'test112'
}
]
},
{
id: '12',
name: 'test12',
children: [
{
id: '121',
name: 'test121'
},
{
id: '122',
name: 'test122'
}
]
}
]
}];
fn(data, '121'); // [1, 12, 121]
function find(data, id) { if (Array.isArray(data)) { while(data.length) { let item = data.shift(); if (item.id == id) { return item.hash.split('-'); } if (item.children && item.children.length) { item.children.map( one => { one.hash = (item.hash || item.id) + '-' + one.id; data.push(one); }) } } } return []; }