整理《壹题》问题和解析,原文见https://juejin.cn/post/6844903885488783374
1.React/vue为什么要在列表组件写key,作用?
key的作用主要是为了高效的更新虚拟DOM列表,react和vue都是采用diff算法对比新旧虚拟节点,从而更新节点,vue给每一个属性绑定监听,采用依赖搜集追踪,react自顶而下的更新策略,有key值采用map映射找对应旧节点,没有采用遍历查找;
如果是简单无状态组件,不带key,节点原地复用,省去创建/销毁的开销
实际开发建议带唯一id作为key,因为业务场景复杂,唯一id保证组件状态正确,也因为用户基本感受不到key值开销
2.['1', '2', '3'].map(parseInt) what & why ?
实际执行的的代码是:
['1', '2', '3'].map((item, index) => {
return parseInt(item, index)
})
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
parseInt('1', 0) // 1 基数位0,按照10为基数处理。这个时候返回1
parseInt('2', 1) // NaN 基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
parseInt('3', 2) // NaN
所以:
['1', '2', '3'].map(parseInt)
// 1, NaN, NaN
3.什么是防抖和节流?有什么区别?如何实现?
防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间(蓄力)
节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率(冷却)
4.介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
Set(集合)类似数组,成员不能重复,允许储存任何类型的唯一值,无论是原始值或者是对象引用。方法有add, delete,has,size,clear,遍历方法keys(),values(), entries(),forEach()
Array.from(new Set(arr)) 或 [... new Set(arr)] 数组去重
WeakSet 只能储存被弱引用对象,不能存放值,而 Set 对象都可以
垃圾回收机制不考虑 WeakSet 对该对象的引用,运行前后成员个数可能不一致(被垃圾回收),WeakSet 对象是无法被遍历,方法有add, delete,has
Map(字典)是以 [key, value] 的形式储存,方法有set,get, delete,has,size,clear,遍历方法keys(),values(), entries(),forEach(),可以跟各种数据进行转换
WeakMap 键名只接收弱引用对象,键值可以是任意的
5.介绍下深度优先遍历和广度优先遍历,如何实现?
深度优先:DFS 找到一个节点后,把它的后辈都找出来,常用递归法。
广度优先:BFS 找到一个节点后,把他同级的兄弟节点都找出来放在前边,把孩子放到后边,常用 while
6.请分别用深度优先思想和广度优先思想实现一个拷贝函数?
7.ES5/ES6 的继承除了写法以外还有什么区别?
es6的class 类似const声明 不提升就地赋值,内部严格模式,不可枚举object.keys->[],无原型[[constructor]] 必须new class,不可new class.xxx()
8.setTimeout、Promise、Async/Await 的区别
主要是在事件循环中的区别,setTimeout属于消息队列中的宏任务,同属还有setImmediate setInterval
Promise属于微任务,消息队列中优先执行,同属还有process.nextTick MutationObserver
遇到await立即执行表达式,表达式后面的代码放到消息队列的微任务里,等其他同步任务执行完,再回表达式后面代码
9.Async/Await 如何通过同步的方式实现异步
async function a(){
const res = await b()
}
实现机制 Generator 的语法糖,* yield
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中
await 命令只能用在 async 函数之中,如果用在普通函数,就会报错
多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发(Promise.all),将多个Promise实例包装成一个新的Promise 实例,都为resolved才是resolved否则为rejected
10.异步运行输出
script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout
11.数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
Array.from(new Set(arr.flat(Infinity).sort((a,b)=> )))
12.JS 异步解决方案的发展历程以及优缺点
callback 回调地狱 不能try catch 不能return
Promise 链式调用 每次then后面返回全新的promise,return 的结果会被 Promise.resolve() 包装
Generator * yield
async await 将异步代码改造成同步代码
13.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
构造函数同步,then 异步 ,then内部定时器将代码放微任务队列
输出例子 12453 settime返回随机数 6
14.如何实现一个 new
function _new(fn, ...arg) {
const obj = Object.create(fn.prototype);
const ret = fn.apply(obj, arg);
return ret instanceof Object ? ret : obj;
}
15.简单讲解一下http2的多路复用
同一TCP连接,同一时刻同一时刻传输多个HTTP请求
Http2的传输是基于二进制帧,每一个TCP连接中承载了多个双向流通的流,每一个流都有一个独一无二的标识和优先级
Http2 头部压缩 服务器推送
16.谈谈你对TCP三次握手和四次挥手的理解
1、客户端发送syn包到服务器,等待服务器确认接收。
2、服务器确认接收syn包并确认客户的syn,并发送回来一个syn+ack的包给客户端。
3、客户端确认接收服务器的syn+ack包,并向服务器发送确认包ack
四次挥手 多了一层等待服务器再一次响应 回复数据是否已发送完毕
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态。
17.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
B突然重启,相当这个TCP连接没有正常关闭,B 没有向A FIN关闭信号,
FIN WAIT
和等待时间有关 如果超出多个等待阈值,抛出异常
18.React 中 setState 什么时候是同步的,什么时候是异步的?
这里的异步指的多个state会合成到一起进行批量更新
通过onClick引发的事件处理,调用setState不会同步更新this.state
19.React setState 代码输出
0023
20.介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
执行工程自身,确定首层依赖模块,获取模块,模块扁平化,安装模块,执行工程自身生命周期
21.分别介绍Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() 优劣
每一个继承 Object 的对象都有 toString 方法,如果没被重写,返回 [Object type] 用call或者apply方法来改变toString方法的执行上下文
instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype [] instanceof Array; // true
Array.isArray(arr);
22.介绍下重绘和重排(Repaint & Reflow),以及如何进行优化
浏览器渲染机制
节点几何或样式不影响布局的改变,例如outline, visibility, color、background-color等,称为重绘
布局或者几何属性需要改变就称为重排,是影响浏览器性能的关键因素
常见的触发硬件加速的css属性:
transform
opacity
filters
Will-change
23.介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景
观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
24.聊聊 Redux 和 Vuex 的设计思想
都是处理全局状态的工具库
大致实现思想都是:全局state保存状态---->dispatch(action)
------>reducer(vuex里的mutation)----> 生成newState;
整个状态为同步操作;
不同,vuex里面多了一步commit操作,在action之后commit(mutation)之前处理异步,而redux里面则是通过中间件处理
25.说说浏览器和 Node 事件循环的区别
主要的区别在于浏览器的event loop 和nodejs的event loop 在处理异步事件的顺序是不同
浏览器 同步任务-异步消息队列(微任务 宏任务)
Node的事件循环是libuv实现
26.介绍模块化发展历程
IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module
模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突
IIFE: 自执行函数,避免变量冲突
AMD: requireJS 编写模块化,变量提前声明好,define
CMD: 动态引入依赖文件
CommonJS: nodejs 中自带的模块化 var fs = require('fs');
ES Module: import a from 'a'
27.全局作用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?
Script 声明块中,不在Global windiow,相当于IIFE声明代码
去掉window,在声明代码块获取
28.cookie 和 token 都存放在 header 中,为什么不会劫持 token?
浏览器会自动携带同域cookie,不会自动携带 token
1.token支持跨域,将token置于请求头中,而cookie是不支持跨域访问的
2.服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;
29.聊聊 Vue 的双向数据绑定,Model 如何改变 View,View 又是如何改变 Model 的
vue2利用ES5的Object.defineProperty
Object.defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
30.两个数组合并成一个数组
[...A,...B] forEach(item=>arr.push(item)) concat
31.改造下面的代码,使之输出0 - 9
for (var i = 0; i < 10; i++) {
setTimeout(i => {
console.log(i);
}, 1000, i)
}
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000)
}
for (var i = 0; i < 10; i++) {
(i => {
setTimeout(() => {
console.log(i);
}, 1000)
})(i)
}
IIFE、eval、new Function、apply/call(Object.create(null), i)
32.Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法。
虚拟dom 生成vnode 依赖搜集 dom diff 有损耗和计算量,最终也是要操作dom
33.IIFE代码输出
输出函数b,非匿名自执行函数,函数名只读,类似const定义 不能重新赋值
匿名函数内b挂window
34.上题改造分别输出 10 20
var b = 10;
(function b(){
console.log(window.b); // 打印10
var b = 20;
console.log(b); // 打印20
})();
var 或 (b)传变量进立即执行函数
35.浏览器缓存读取规则
Service worker fetch响应
Memory Cache 关闭了TAB,memory cache 随之清空
Disk Cache 强制缓存(Expires Cache-Control 状态码全部是 200)协商缓存(Last-Modified 和 ETag 比较后确定 304 还是 200)
Push Cache
网络请求 响应内容存入 disk cache,响应内容 的引用 存入 memory cache,响应内容存入 Service Worker 的 Cache Storage
频繁变动资源Cache-Control: no-cache
不常变化资源Cache-Control: max-age=31536000
36.使用迭代的方式实现 flatten 函数
37.为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作?
因为异步操作是结果、发起时间不可预测
避免在里面做一种规范,使状态可预测,方便维护
38.a 在什么情况下会打印 1?
var a = {
i: 1,
toString() {
return a.i++;
}
}
Object.defineProperty(window, 'a', {
get: function() {
return this.value = this.value ? (this.value += 1) : 1;
}
});
39.介绍下 BFC 及其应用
块级格式上下文,独立的容器,里面的元素和外部的元素相互不影响
float 浮动、绝对定位、overflow不为visiable
作用:清除浮动、防止相邻元素外边距重叠
40.在 Vue 中,子组件为何不可以修改父组件传递的 Prop
易于监测数据的流动,出现了错误可以更加迅速的定位到错误发生的位置。
41.代码输出
// undefined
// 10
// 20
42.实现一个 sleep 函数
const sleep = (time) => {
return new Promise(resolve => setTimeout(resolve, time))
}
sleep(1000).then(() => {
// 这里写操作
})
43.使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进行排序,输出结果
102, 15, 22, 29, 3, 8
44.介绍 HTTPS 握手过程
客户端发起加密请求
服务端收到请求后使用CA证书产生公钥私钥密钥对,将包含公钥的证书发送给客户端
客户端验证证书的合法性,产生一个随机key,使用公钥将其加密,发送给服务器
45.HTTPS 握手过程中,客户端如何验证证书的合法性
校验证书的颁发机构是否受客户端信任,校验证书是否被吊销,对比系统时间,校验证书是否在有效期内
46.代码输出
2,3,4,f,f
47.双向绑定和 vuex 是否冲突
严格模式使用vuex,当用户输入时,v-model会试图直接修改属性值,这个修改不是在mutation中修改
computed: {
message: {
set (value) {
this.$store.dispatch('updateMessage', value);
},
get () {
return this.$store.state.obj.message
}
}
}
48.call 和 apply 的区别是什么,哪个性能更好一些
apply( ):两个参数,第一个是运行函数的作用域,第二个是参数数组
call( ):参数个数不定,第一个是运行函数的作用域,其余传递给函数的参数逐个列出。
call比apply的性能要好,即使参数是数组也可以用call
let params = [1,2,3,4]
xx.call(obj, ...params)
49.为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?
向服务器发送数据时,服务器用一个1x1的gif图片来作为响应
避免跨域(img 天然支持跨域)
不会阻塞页面加载
所有图片中,体积最小
目前比较合适的做法是服务器发送"204 No Content"
50.实现 (5).add(3).minus(2) 功能。
Number.prototype.add = function (number) {
if (typeof number !== 'number') {
throw new Error('请输入数字~');
}
return this + number;
};
大佬解法:
Number.MAX_SAFE_DIGITS = Number.MAX_SAFE_INTEGER.toString().length-2
Number.prototype.digits = function(){
let result = (this.valueOf().toString().split('.')[1] || '').length
return result > Number.MAX_SAFE_DIGITS ? Number.MAX_SAFE_DIGITS : result
}
Number.prototype.add = function(i=0){
if (typeof i !== 'number') {
throw new Error('请输入正确的数字');
}
const v = this.valueOf();
const thisDigits = this.digits();
const iDigits = i.digits();
const baseNum = Math.pow(10, Math.max(thisDigits, iDigits));
const result = (v * baseNum + i * baseNum) / baseNum;
if(result>0){ return result > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : result }
else{ return result < Number.MIN_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : result }
}
51.Vue 的响应式原理中 Object.defineProperty 有什么缺陷?
Object.defineProperty不能监听动态length的变化,劫持对象的属性,每个属性进行遍历
Proxy可以劫持整个对象,并返回一个新的对象
52.怎么让一个 div 水平垂直居中
常用:
display: flex;
justify-content: center;
align-items: center;
其他:
2. relative/absolute transform: translate(-50%, -50%);
3. relative/absolute 50%/-xxpx, 0 0 0 0 margin:auto
4. display: grid;justify-self: center;align-self: center;
53.代码输出
undefined
{n: 2}
. = 优先级,新旧引用对象,
54.冒泡排序如何实现,时间复杂度是多少, 还可以如何改进?
55.{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]
var obj = { 1: 222, 2: 123, 5: 888 };
let arr = [];
for(var i = 1; i <= 12; i++) {
if(obj[i]) {
arr.push(obj[i])
} else {
arr.push(null)
}
}
56.设计 LazyMan 类
57.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景。
display: none (不占空间,不能点击)
visibility: hidden(占据空间,不能点击)
opacity: 0(占据空间,可以点击)场景: 自定义图片上传按钮
58.箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?
箭头函数是普通函数的简写,没有自己的 this,无法调用 call,apply,从自己的作用域链的上一层继承 this
没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 proto
59.给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。
var newArr2 = nums1.filter((item) => {
return nums2.includes(item);
//return nums2.indexOf(item) > -1;
});
let intersection = (...arg) => {
let result = [];
arg[0].forEach((v) => {
if (arg[1].indexOf(v) !== -1) {
result.push(v);
arg[1].splice(arg[1].indexOf(v), 1);
}
}); return result;
}
60.如何修改才能让图片宽度为 300px,<img src="1.jpg" style="width:480px!important;”>
max-width:300px;覆盖其样式;
transform: scale(0.625);按比例缩放图片;
document.getElementsByTagName("img")[0].setAttribute("style","width:300px!important;")
61.介绍下如何实现 token 加密
token加密主要是来做客户端和服务端的用户信息校验
流程:客户端请求-》服务端校验签发token-〉客户端将token存在cookie或者localstorage
JWT认证举例
需要一个secret(随机数)
后端利用secret和加密算法(如:HMAC-SHA256)对payload(如账号密码)生成一个字符串(token),返回前端
前端每次request在header中带上token
后端用同样的算法解密
62.redux 为什么要把 reducer 设计成纯函数
纯函数修改,单一数据流,state只读/只能通过action执行修改
实现记录 回放 热更新
63.如何设计实现无缝轮播
p:overflow:hidden w400,c:w2000 flex animation:video 20s infinite
@keyframe video{0%{transform:translateX(0)}25%{transform:translateX(-400px)}}
64.模拟实现一个 Promise.finally
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => )
);
};
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => )
);
};
65.a.b.c.d 和 a['b']['c']['d'],哪个性能更高?
a.b.c.d性能更高,因为[ ]里面有可能是字符串,有可能是变量,至少多一次判断,而a.b.c.d是直接取用该字符串当作属性名
66.ES6 代码转成 ES5 代码的实现思路是什么
babel 将代码字符串解析成抽象语法树,即所谓的 AST,对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码,根据处理后的 AST 再生成代码字符串
67.数组编程题
function formArray(arr: any[]) {
const sortedArr = Array.from(new Set(arr)).sort((a, b) => a - b);
const map = new Map();
sortedArr.forEach((v) => {
const key = Math.floor(v / 10);
const group = map.get(key) || [];
group.push(v);
map.set(key, group);
});
return [...map.values()];
}
68.如何解决移动端 Retina 屏 1px 像素问题
rem + viewport
操作步骤如下。在根元素html设置font-size. 将元素转换成rem
通过 window.devicePixelRatio 拿到dpr 再写meta 设置 viewport的scale : 1/dpr
69.如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC' 变成 'aBc' 。
function processString (s) {
var arr = s.split('');
var new_arr = arr.map((item) => {
return item === item.toUpperCase() ? item.toLowerCase() : item.toUpperCase();
});
return new_arr.join('');
}
70.介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面的
修改了一个或多个文件时,文件系统接收更改并通知webpack,webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新,HMR Server 使用webSocket通知HMR runtime 需要更新,HMR运行时通过HTTP请求更新jsonp,HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新
71.实现一个字符串匹配算法,从长度为 n 的字符串 S 中,查找是否存在字符串 T,T 的长度是 m,若存在返回所在位置。
const find = (S, T) => {
if (S.length < T.length) return -1
for (let i = 0; i < S.length; i++) {
if (S.slice(i, i + T.length) === T) return i
}
return -1
}
72.为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因。
for 循环没有任何额外的函数调用栈和上下文;
forEach不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行时考虑
10万这个级别下, forEach 的性能是 for的十倍 for: 2.263ms forEach: 0.254ms
100万这个量级下, forEach 的性能是和for的一致 for: 2.844ms
forEach: 2.652ms
1000万级以上的量级上 , forEach 的性能远远低于for的性能 for: 8.422ms forEach: 30.328m
73.介绍下 BFC、IFC、GFC 和 FFC
BFC(Block formatting contexts):块级格式上下文
FFC(Flex formatting contexts):自适应格式上下文
GFC(GrideLayout formatting contexts):网格布局格式化上下文
IFC(Inline formatting contexts):内联格式上下文
74.使用 JavaScript Proxy 实现简单的数据绑定
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(getting ${key}!
);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log('setting',target, key, value, receiver);
if (key === "text")
return Reflect.set(target, key, value, receiver);
}
});
75.数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少
都是用 key 精确查找哈希表的过程,直接根据索引取的对应的元素,所以不管取哪个位置的元素的时间复杂度都是 O(1)
76.代码输出
对象的键名只能是字符串和 Symbol 类型,其他类型的键名会被转换成字符串类型
c b c
77.算法题「旋转数组」
function rotate(arr, k) {
const len = arr.length
const step = k % len
return arr.slice(-step).concat(arr.slice(0, len - step))
}
78.Vue 的父组件和子组件生命周期钩子执行顺序是什么
加载渲染:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新、销毁:
父beforeUpdate->子beforeUpdate->子updated->父updated
79.input 搜索如何防抖,如何处理中文输入
防抖 debounce函数,中文输入通过ref compositionstart/end
80.介绍下 Promise.all 使用、原理实现及错误处理
Promise.all()方法将多个Promise实例包装成一个Promise对象(p)
Promise对象(p)的状态是由数组中的Promise对象(p1,p2,p3)决定的
(p1,p2,p3)都变成fullfilled/fullend状态,会组成一个数组返回给传递给p的回调函数
如果p1,p2,p3中有一个Promise对象变为rejected状态的话,p也会变成rejected状态
给数组中的promise实例定义了错误处理catch方法的时候,就不会在走p的catch的方法,且参数实例在执行完catch方法之后状态会变成resolved
81.打印出 1 - 10000 之间的所有对称数
[...Array(10000).keys()].filter((x) => {
return x.toString().length > 1 && x === Number(x.toString().split('').reverse().join(''))
})
82.移动零算法
function zeroMove(array) {
let len = array.length;
let j = 0;
for(let i=0;i<len-j;i++){
if(array[i]===0){
array.push(0);
array.splice(i,1);
i --;
j ++;
}
}
return array
}
83.var、let 和 const 区别的实现原理是什么
var 和 let 用以声明变量,const 用于声明只读的常量
var 不存在块级作用域,在全局范围内有效,let 和 const 只在它所在的代码块内有效
var 变量提升,let 和 const 只可先声明,后使用
const 在声明时必须初始化赋值,一旦声明,其声明的值就不允许改变
原理变量名-内存地址-内存空间
JS 读变量时,先找到变量绑定的内存地址,然后找地址指向的内存空间,最后读内容,变量改变时,JS 不会用新值覆盖之前旧值的内存空间,而是重新分配一个新的内存空间来存储新值,并将新的内存地址与变量进行绑定,JS 引擎会在合适的时机进行 GC,回收旧的内存空间
const 定义变量(常量)后,变量名与内存地址之间建立了一种不可变的绑定关系,阻隔变量地址被改变
84.实现一个 add 函数,满足以下功能
function currying(fn, length) {
length = length || fn.length; // 注释 1
return function (...args) { // 注释 2
return args.length >= length // 注释 3
? fn.apply(this, args) // 注释 4
: currying(fn.bind(this, ...args), length - args.length) // 注释 5
}
}
85.react-router 里的 Link 标签和 a 标签有什么区别
最终渲染的 DOM 来看,这两者都是链接
Link 是 react-router 里实现路由跳转的链接,react-router 接管了其默认的链接跳转行为,Link 的“跳转”行为只会触发相匹配的 Route 对应的页面内容更新,而不会刷新整个页面。
a 标签就是普通的超链接了,用于从当前页面跳转到 href 指向的另一个页面(非锚点情况)
86.两数之和
function sum(arr, target) {
const res = []
for (let i = 0; i < arr.length; i++){
const a = target - arr[i]
const index = arr.indexOf(a,i)
if (index>=0 && index !==i) { // index 不能等于 i
res.push(i, index)
}
}
return res
}
87.在输入框中如何判断输入的是一个正确的网址
function isUrl(url) {
try {
new URL(url);
return true;
}catch(err){
return false;
}}
88.实现 convert 方法,把原始 list 转换成树形结构,要求尽可能降低时间复杂度
function convert(list) {
const res = []
const map = list.reduce((res, v) => (res[v.id] = v, res), {})
for (const item of list) {
if (item.parentId === 0) {
res.push(item)
continue
}
if (item.parentId in map) {
const parent = map[item.parentId]
parent.children = parent.children || []
parent.children.push(item)
}
}
return res
}
89.设计并实现 Promise.race()
Promise._race = promises => new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
90.实现模糊搜索结果的关键词高亮显示
<!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>
</head>
<body>
<div id='app'>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var myitem = {
template:`<div class='item'>
<p v-html="info.name"></p>
</div>`,
props:{
info:{
type:Object,
default:()=>{return {}},
}
}
}
var vm = new Vue({
el:'#app',
template:`
<div>
<input @input='inputSearchText'>
<myitem v-for='info in results' :key='info.name' :info='info'></item>
</div>
`,
data(){
return {
infos:[
{name:'地铁1',},
{name:'地铁6',},
{name:'地铁7',},
{name:'地铁10',},
{name:'地铁11',},
{name:'公交112',},
{name:'公交597',},
{name:'公交593',},
],
results:[],
}
},
created() {
this.results = JSON.parse(JSON.stringify(this.infos));
},
methods: {
inputSearchText : (function(timeout){
var timer;
return function(e){
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() => {
this.search(e.target.value);
//this.search_text = e.target.value
}, timeout);
}
})(1000),
search(text){
var reg = RegExp(`(${text})`);
var results = JSON.parse(JSON.stringify(this.infos));
var matches = results.filter(info=>info.name.match(reg));
matches.forEach(info=>{
info.name = info.name.replace(reg,`<span class='highlight'>$1</span>`
)});
this.results = matches;
console.log(this.results);
}
},
components:{
myitem,
},
})
</script>
<style>
.highlight{
color:red;
}
</style>
</html>
91.HTTPS 中间人攻击
服务器向客户端发送公钥时,攻击者截获公钥,自己生成一个伪造公钥,发给客户端
服务端在发送浏览器的公钥中加入CA证书,浏览器可以验证CA证书的有效性
92.实现一个函数 fn 找出链条中所有的父级 id
let res = [];
const findId = (list, value) => {
let len = list.length;
for (let i in list) {
const item = list[i];
if (item.id == value) {
return res.push(item.id), [item.id];
}
if (item.children) {
if (findId(item.children, value).length) {
res.unshift(item.id);
return res;
}
}
if (i == len - 1) {
return res;
}
}
};
93.给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请找出这两个有序数组的中位数
var findMedianSortedArrays = function(nums1, nums2) {
let m = nums1.length
let n = nums2.length
let k1 = Math.floor((m + n + 1) / 2)
let k2 = Math.floor((m + n + 2) / 2)
return (findMedianSortedArraysCore(nums1, 0, nums2, 0, k1) + findMedianSortedArraysCore(nums1, 0, nums2, 0, k2)) / 2
};
const findMedianSortedArraysCore = (nums1, i, nums2, j, k) => {
// 如果数组起始位置已经大于数组长度-1
// 说明已经是个空数组
// 直接从另外一个数组里取第k个数即可
if (i > nums1.length - 1) {
return nums2[j + k - 1]
}
if (j > nums2.length - 1) {
return nums1[i + k - 1]
}
// 如果k为1
// 就是取两个数组的起始值里的最小值
if (k === 1) {
return Math.min(nums1[i], nums2[j])
}
// 取k2为(k/2)或者数组1的长度或者数组2的长度的最小值
// 这一步可以避免k2大于某个数组的长度(长度为从起始坐标到结尾)
let k2 = Math.floor(k / 2)
let length1 = nums1.length - i
let length2 = nums2.length - j
k2 = Math.min(k2, length1, length2)
let value1 = nums1[i + k2 - 1]
let value2 = nums2[j + k2 - 1]
// 比较两个数组的起始坐标的值
// 如果value1小于value2
// 就舍弃nums1前i + k2部分
// 否则舍弃nums2前j + k2部分
if (value1 < value2) {
return findMedianSortedArraysCore(nums1, i + k2, nums2, j, k - k2)
} else {
return findMedianSortedArraysCore(nums1, i, nums2, j + k2, k - k2)
}
}
94.vue 在 v-for 时给每项元素绑定事件需要用事件代理吗?为什么?
可以使用,一般指向同一个处理函数
vue本身不做事件代理,
95.模拟实现一个深拷贝,并考虑对象相互引用以及 Symbol 拷贝的情况
function deepClone(obj, hash = new WeakMap()) {
if (hash.has(obj)) return obj;
var cobj;
// null
if (obj === null) { return obj }
let t = typeof obj;
// 基本类型
switch (t) {
case 'string':
case 'number':
case 'boolean':
case 'undefined':
return obj;
}
// 数组
if (Array.isArray(obj)) {
cobj = [];
obj.forEach((c, i) => { cobj.push(deepClone(obj[i])) });
} else {
cobj = {};
// object // symbol
if (Object.prototype.toString.call(obj) === "[object Object]") {
Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).forEach(c => {
hash.set(obj, obj);
cobj[c] = deepClone(obj[c], hash);
});
} else {
//内置Object
cobj = obj;
}
}
return cobj;
}
96.介绍下前端加密的常见场景和方法
Hash MD5/MD6 全程HTTPS Base64 / Unicode WebAssembly
97.React 和 Vue 的 diff 时间复杂度从 O(n3) 优化到 O(n) ,那么 O(n3) 和 O(n) 是如何计算出来的?
O(n) 代表如果有n节点需要更新,只需要操作dom n 次就能完成
98.代码输出
http://www.baidu.com
99.编程算法
function numberReverse(num) {
const str = num.toString()
return str.length === 1 ? str : numberReverse(str.substring(1)) + str.substring(0, 1)
}
100.代码输出
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
// 以上只是 Foo 的构建方法,没有产生实例,此刻也没有执行
Foo.prototype.a = function() {
console.log(3)
}
// 现在在 Foo 上挂载了原型方法 a ,方法输出值为 3
Foo.a = function() {
console.log(4)
}
// 现在在 Foo 上挂载了直接方法 a ,输出值为 4
Foo.a();
// 立刻执行了 Foo 上的 a 方法,也就是刚刚定义的,所以
// # 输出 4
let obj = new Foo();
/* 这里调用了 Foo 的构建方法。Foo 的构建方法主要做了两件事:
- 将全局的 Foo 上的直接方法 a 替换为一个输出 1 的方法。
- 在新对象上挂载直接方法 a ,输出值为 2。
*/
obj.a();
// 因为有直接方法 a ,不需要去访问原型链,所以使用的是构建方法里所定义的 this.a,
// # 输出 2
Foo.a();
// 构建方法里已经替换了全局 Foo 上的 a 方法,所以
// # 输出 1
评论区