浏览器、 http
1、浏览器输入地址回车发生了什么
2、http1.0,1.1,2.0区别
3、浏览器缓存(你们项目中用到了吗? 具体干了些什么?)
4、工作原理
5、跨域解决
6、重绘重排
7、前端安全(XSS,CSP, CSRF)如果防范
8、路由原理(哈希路由和history模式)
vue
1、父组件中想用子组件里面的方法,有哪几种方式
- 子组件可以在自己的created钩子里面用 $emit 把this开放出去
- 在子组件上添加 ref 指令 拿到当前子组件的实例对象
- 其实在当前写的项目中,使用了类似angualr2的依赖注入的机制,我们将所有的页面行为操作,和业务逻辑分别写到了action.ts、helper.ts里面了,如果让这两个模块用 @injectable 装饰器注入到实例列表里面,外界就可以轻松使用该实例的任何方法了(但不能为了使用方法而故意注入,这样做也有它的缺点)、
2、vue是怎样实现数据更新的
- observe Object.defineProperty 数据劫持
- watcher 告诉订阅值的地方 数据更新了
- compile 收到更新通知 更新状态
3、vue和angualr2的数据更新机制的区别
4、虚拟dom
React
1、 生命周期
2、 setState同步还是异步?我想拿到异步后的数据怎么办?react为什么把setstate设计成这样?
3、虚拟dom的优势、原理
4、diff算法在虚拟dom中如何比较的新旧树的差异
5、错误边界
js
1、 ["1", "2", "3"].map(parseInt)
[1,NaN,NaN]
2、冒泡、快排、深度递归
3、jsonp
4、深层复制
let a = {
name: '刘',
age: 18,
say: function() {}
}
let b = JSON.parse(JSON.stringify(a));
b.age //18
b.age = 20;
b.age // 20
a.age //18
b // {name:"刘",age: 20}
- 这种方式可以实现深层复制,但是复制的对象有函数是不行的。
- 如果对象是一个重复引用的特性的(自己的属性里面有原对象信息,无限的自己引用自己),也不适用
5、es6新语法
6、js原型 能手画出来
7、bind、call、apply
8、隐式转换
+'23' + 1 // 24
9、数组降维
let a = [[1,2],[4,5],[77,88]];
[].concat.apply([], a)
a.flatMap(item => item)
10、promise、async、await、generator生成器
11、发布订阅模式、观察者模式、单例模式
12、面向对象的特点、以及用面向对象编程需要注意的点
13、0.1 + 0.2 !=0.3 为什么?
14、正则匹配三个连续的数字(常用正则)
15、 es6 Proxy
16、怎样区分function、 数组、 对象
17、快速生成一个 0到N的数组,然后要求在不生成新数组的情况下再打乱成随机数组
let a = [...(new Array(n)).keys()];
let b = Array.from({length:n},(v, k) => k);
let c = (n) => Array.from({length:n}).map((v,k) => k);
// 利用sort
arr.sort((v1, v2) => {
return 0.5 - Math.random() > 0? 1: -1
})
// 随机项调换位置
for(let i = 0; i < arr.length; i++) {
let curr = i;
let randomIndex = Math.floor(Math.random() * arr.length);
let currItem = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = currItem;
}
18、 广度优先递归一个二叉树
19、 查看两个单链表是否有交叉(有重复值)
20、 时间复杂度
21、 js对象和Map的区别
22、 斐波那切数列 (分析下 函数被调用了多少次)
23、 for in for of 的区别
ts
1、怎样表示下面结构
let a = [
[Box,Box],
[Box,Box],
...
]
Array<Box[]>
2、装饰器
3、反射
4、接口和抽象类的区别
5、反射实现依赖注入(为了解决什么问题,实现过程)
6、对象值的自动推断
const test = {
aa: {aaa: 1},
bb: {bbb: 2}
}
type TestObj = typeof test;
const fun: <T extends keyof TestObj>(key: T) => TestObj[T] = key => test[key];
fun('aa'); // 自动推断为 {aaa: number}
css布局
1、css权重
2、c3新选择器
3、less的特性
4、记得小东西 多使用伪类
5、吸顶实现
6、内容过长footer跟随内容,内容很少footer就在屏幕下面 (flex实现)
项目
1、项目的架构
2、项目中遇到问题怎样解决
3、项目优化、首屏优化, 骨架屏
4、提交了几个commit, 线上紧急bug如何处理(考Git)
状态管理工具
1、vuex, redux, vuex和redux的区别
2、谈谈你对rxjs的理解
webpack
1、为什么要用loader,它解析原理
2、优化
3、那些常用的配置(入口,出口,loader,plugin,devserve等)
node
1、事件循环机制 eventloop
手写题
1、 千分位(隔三位插个逗号)
- var a = '123456789'; => a = '123,456,789,'
function toThounsandInsertStr(str: string): string {
let result: string = '';
let rule: RegExp = /\d{3}$/;
while (rule.test(str)) {
result = RegExp.lastMatch + result;
if (str != RegExp.lastMatch) {
result = ',' + result;
}
str = RegExp.leftContext;
}
return str + result;
}
toThounsandInsertStr('1234567') // 1,234,567
toThounsandInsertStr('123') // 123
toThounsandInsertStr('1') //1
2、 碰到a就剔除字符串里面的 a
和 a前面的数字
- var a = '12
3a
456a
789aa
'; => a = '12457'
let _arr = a.split('');
for(let i = 0; i < _arr.length; i++) {
let start = 0; let n = 2;
if(_arr[i] == 'a') {
start = i -1 < 0? 0: i -1;
n = i - 1 < 0? 1: 2;
_arr.splice(start, n);
i = n == 2 ? i - 2 : 0;
}
}
console.log(_arr.join(''))
3、 一个请求在 delay函数延迟过后发送,结果可能成功也可能失败,如果失败就重复发送,重复n次过后还是失败,结束发送返回错误
- 请求函数 query, 成功{succ: true}, 失败{succ: false}
- delay函数 本身返回一个promise
function delay(t) {
console.log('延迟执行开始')
return new Promise(resolve => {
setTimeout(() => {
console.log('延迟完毕')
resolve()
}, t)
})
}
function query() {
console.log('请求开始')
return new Promise(resolve => {
setTimeout(() => {
console.log('1秒失败,请求完毕')
resolve({succ: false})
},1000)
})
}
async function repeatQuery(delayTime, n) {
await delay(delayTime)
let res = await query().then(data => data);
console.log(12312312, res)
if(res.succ) {
return res
} else {
if (n <= 0) {
return
}
return await repeatQuery(delayTime, --n)
}
}
repeatQuery(2000, 5).then(res => {
console.log(111, res)
})
4、数组转树
var a = [
{id: 1, pid: 0, name: '上海市' },
{id: 2, pid: 1, name: '宝山'},
{id: 3, pid: 1, name: '普陀'},
{id: 4, pid: 0, name: '北京'},
{id: 5, pid: 4, name: '朝阳'},
{id: 6, pid: 4, name: '五环'},
{id: 7, pid: 2, name: '镇平'},
{id: 8, pid: 2, name: '宝山'},
]
//转成递归树形式展示
- 思路整理
- 第一次遍历节点的时候,会发现遍历的子节点很有可能还没有父亲节点
- 所以利用第一次遍历,找出根节点,并把所有子节点统一放到Map中,形式为(pid, [pid对应的子节点...])
- 然后用根节点的id去匹配Map中的pid对应的子节点,匹配到的结果数组也即是当前节点的children
- 如果有children,把children当做下一次要匹配的元素找每一个children元素的children
function arrTotree(arr) {
let parentMap = new Map();
// let nodeMap = new Map();
arr.forEach(item => {
// nodeMap.set(item.id, item);
if(parentMap.has(item.pid)) {
let arr = parentMap.get(item.pid);
arr.push(item)
} else {
parentMap.set(item.pid, [item])
}
})
let res = [];
res = parentMap.get(0);
(function loopAddChild(res) {
res.forEach(item => {
if (parentMap.has(item.id)) {
item.children = parentMap.get(item.id);
loopAddChild(item.children)
}
})
})(res)
return res;
}
// 简化版
function arrToTree(arr) {
let map = new Map();
arr.forEach(item => {
if(!map.has(item.pid)) {
map.set(item.pid, [item])
} else {
let arr = map.get(item.pid);
arr.push(item)
}
})
// 都是指针 第二次遍历原数组 匹配id和pid 一样的话 map取出来当做当前遍历元素的children
arr.forEach(item => {
if (map.has(item.id)) {
item.children = map.get(item.id);
}
})
// 把顶级的数组取出来 返回
return map.get(0)
}
5、 数组去重(两种方式, 如果有for循环,时间复杂度不能是o(n^2))
var a = [1,2,3,3,2,1,4,5,6,6];
Array.from(new Set([...a]))
// ----
[...new Set([...a])]
// for循环 - 空间换时间
let arr= []
let b = {};
for (let i = 0; i < a.length; i++) {
let curr = a[i];
if (b[curr] >= 0) {
continue
}
b[curr] = i;
arr.push(a[i]);
}
console.log(arr)
6、浏览器事件循环
原则1:先执行主任务,然后是微任务(promise),最后是宏任务(setTimeout)
原则2:执行宏任务的时候碰到微任务,先将其推到task队列,执行完当前宏任务后,去清空微任务队列
原则3: 宏任务、微任务先进先出
原则4:new Promise 会立即执行 但 resolve是一个异步回调
原则5:async 返回的是一个Promise
原则6:await 会跳出当前执行的 async 函数体(await 让出线程)接着执行
原则7:当await操作符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数
原则8:谨记上面 分析完下面三道题 就无敌了
题1
Promise.resolve().then(function promise1 () {
console.log('promise1');
})
setTimeout(function setTimeout1 (){
console.log('setTimeout1')
Promise.resolve().then(function promise2 () {
console.log('promise2');
})
}, 0)
Promise.resolve().then(function promise1 () {
console.log('promise3');
})
setTimeout(function setTimeout2 (){
console.log('setTimeout2')
}, 0)
题2
setTimeout(() => {
console.log(1)
})
log()
function log() {
setTimeout(() => {
console.log(2)
setTimeout(() => {
console.log(3)
})
})
}
var a = new Promise(resolve => {
setTimeout(() => {
console.log(4)
})
resolve(() => {
setTimeout(() => {
console.log(5)
setTimeout(() => {
console.log(6)
})
})
})
})
setTimeout(() => {
console.log(7)
})
a.then((fn: any) => {
fn()
setTimeout(() => {
console.log(8)
})
})
题3
console.log('1')
async function async1() {
await async2()
console.log('2')
}
async function async2() {
await async3()
console.log('3')
}
async function async3() {
console.log('4')
}
async1()
setTimeout(function () {
console.log('5')
}, 0)
new Promise(resolve => {
console.log('6')
resolve()
}).then(function () {
console.log('7')
return new Promise(resolve => {
console.log('8')
resolve()
})
}).then(function () {
console.log('9')
})
console.log('10')
7、一个数组中有一系列的整数 例如 [1,2,-3,4,5,-3,4,-6],利用一个for循环在数组中找出连续相加和最大的一段 [4,5,-3,4] 和为 10
var a = [6, -3, -2, 7, -15, 1, 2, 2];
var b = [1, -2, 2, -1, 4, 5, -3, 4, -1, -2];
function findSum(arr) {
if (arr.length < 0) return 0;
let maxSum = 0;
let temSum = 0;
let resArr = []
let originIndex = 0
let spliceIndex = 0
for (let i = 0; i < arr.length; i++) {
temSum += arr[i];
resArr.push(arr[i])
if (temSum > maxSum) {
maxSum = temSum;
spliceIndex = i - originIndex
}
if(temSum < 0) {
temSum = 0;
resArr = []
originIndex = i
}
}
resArr.splice(spliceIndex, resArr.length - 1)
return {
maxSum,
resArr
}
}
// 简单测了下 好像是对的...
console.log(findSum(a));
console.log(findSum(b));
8、一个牧场,有一个母牛, 2年后母牛会生一头母牛和一头公牛,3年只生一头公牛,5年母牛会死,4年公牛也会死,N年后这个牧场里面有多少只牛
9、 如何让if(a == 1 && a == 2 && a == 3 ) 为true
10、用js打印杨辉三角形
11、微信抢红包逻辑
12、找出一个单词在字典中所有字母一样的单词, 如 god -> dog (查字典), 给出思路
13、实现一个无线可调用的函数用来做加法运算 add(1)(2)(3)()
---- 6
function add(num, prevSum) {
let result = prevSum || 0
return num ? function (nextNum) {
result = result + num
return add(nextNum, result)
} : result
}
let a = add(1)(2)(3)()
console.log(a)
// 如果非要求柯里化,可以用arguments
14、使用Promise自身特性,实现每隔两秒顺序打印数组
- 正常思路都是在一个
async
的方法里面用for循环await
- 这里运用 Promise自身可链式调用的特性
const list: number[] = [1, 2, 3]
function a(i: number): ProNum {
return new Promise((resolve) => {
setTimeout(() => {
console.log(i)
resolve(i)
}, 2000)
})
}
var p: ProNum = Promise.resolve(0)
function test (i = 0) {
if (i === list.length) return
p = p.then(() => a(list[i]))
test(i+1)
}
test(0)
type ProNum = Promise<number>
15、 合并两个有序数组
function mergeTwoOrderArr(...chunk: number[][]): number[] {
const [arr1, arr2] = chunk;
let tem = Array.from(arr1)
let i = arr1.length - 1;
let j = arr2.length - 1;
let tail = arr1.length + arr2.length - 1;
while (j >= 0) {
if (arr1[i] > arr2[j]) {
tem[tail] = arr1[i]
i--
} else {
tem[tail] = arr2[j]
j--
}
tail --
}
return tem
}
其他
1、未来几年的职业计划
2、为什么选择前端
3、 学习前端技术的途径
4、 自身优缺点
5、对未来公司有什么要求
6、开发项目中遇到了什么让你感觉最骄傲的事情
7、你对我们公司有什么想要了解的
8、为什么离职
9、讲一下你最近在做的项目