Shopee
笔试(24选择+1算法)
-
css创建新的合成层
-
正则(3道选择)
-
Number.Max_Safe_Integer === Math.pow(2,53)-1
安全(Safe)在本文中的提到的意思是指能够准确地表示整数和正确地比较整数。比如Math.pow(2,64)=== Math.pow(2,64)+1 //true
因为2的64次方大于最大安全值,所以无法正确比较
-
CSS3选择器 :nth-child(n) 选择器匹配属于其父元素的第 N 个子元素,不论元素的类型。
-
Substring和slice的用法
console.log(‘fucku_letherman’.slice(2,-2));//cku_letherm
console.log(‘fucku_letherman’.substring(2,-2));//fu- substring()
substring(start, stop)原则上参数不能为负数,若强行传递负数,在执行时会被当成0处理。
另外,如果参数 start 与 stop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。如果 start 比 stop 大,那么该方法在提取子串之前会先交换这两个参数。
- substr()
substr(start, length)参数 start 如果是负数,那么该参数声明从字符串的尾部开始算起的位置。也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推;
参数 length 原则上也不该为负数,若强行传递负数,会被当成0处理,那么该方法返回的就是一个空串(即长度为 0 的字符串)。、
- slice(start,end)都可以是负数 start的下标一定要在end下标左侧,否则返回空
-
x=[0,1,2,3] x.lengh= 2 x[1]+x[2] = ?
首先:x的长度可以改变,x.length=2改变后 x=[0,1]
那么:x[2] = undefined
而:undefined + 1 = NaN + 1 = NaN -
max-width、min-width、width
.maxwidth{
width:100px;
min-width: 120px;
max-width: 80px;
}
问maxwidth元素的width是多少,答案为120px
min-width>max-width时,元素最终的宽度显示min-width的值
- x.reverse() x本身也会反转!
技术一面 简单粗暴两道算法 挂
- 将类数组字符串转化为数组
.eval()全搞定。。我服了,看完答案再看吭哧吭哧写了半个多钟头的我宛如傻逼
《eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。我们可以借助它来将字符串还原成数组》
const str1 = '[1,2,3]'
const str2 = '[1,2,4,"5","a"]'
const str3 = "[1,[2,22],3]"
const str4 = "[1,[2,[22,4],5],6]"
const obj1 = '{"a":1,"b":2}'
const obj2 = {
a:1,
b:2
}
console.log(eval(str1)); //[1,2,3]
console.log(eval(str2)); //[1,2,4,"5","a"]
console.log(eval(str3)); //[1,[2,22],3]
console.log(eval(str4)); //[1,[2,[22,4],5],6]
console.log(JSON.stringify(obj2)); //'{"a":1,"b":2}'
console.log(JSON.parse(obj1)); //obj2
console.log(JSON.parse(obj1) === obj2); //false
console.log(JSON.stringify(obj2) === obj1); //true
-
商品搜索路径图求宽和深
输入如下:
const sourceData = [
[“bag”, “purse”],
[“purse”, “hand purse”],
[“bag”, “backpack”],
[“purse”, “purse wallet”],
[“purse wallet”, “coin wallet”],
[“purse wallet”, “man purse”],
[“shoe”, “nike shoe”],
];根据输入建立一个如下图的sankey diagram(类似电商的商品搜索路径图)
要求实现一个函数返回该图的width(最长路径)和height(路径总数)。如上图的width = 4, height = 5。
const sourceData = [
["bag", "purse"],
["purse", "hand purse"],
["bag", "backpack"],
["purse", "purse wallet"],
["purse wallet", "coin wallet"],
["purse wallet", "man purse"],
["shoe", "nike shoe"],
];
const getDimension = (data)=>{
//定义树节点
function Treenode(val) {
this.val = val
this.left = this.right = null
}
//map记录整体结构,方便树的构建
const map = new Map()
for(let item of data){
if(map.has(item[0])){
map.get(item[0]).push(item[1])
}else{
map.set(item[0],[item[1]])
}
}
//根据map构建树
const get_tree = (node) =>{
if(map.has(node.val)){
let arr = map.get(node.val)
map.delete(node.val)
node.left = new Treenode(arr[0])
get_tree(node.left)
if(arr[1])
{
node.right = new Treenode(arr[1])
get_tree(node.right)
}
}
}
//回溯计算最大深度
const back_track = (node,dep)=>{
if(node ===null) return dep-1
if(node.left === null) {
console.log(depth);
depth++
return dep
}
else return Math.max(back_track(node.left,dep+1),back_track(node.right,dep+1))
}
let depth = 0
let width = 0
//循环遍历所有key
while(map.size !== 0){
console.log(map);
let mapkey_arr = [...map.keys()]
const root = new Treenode(mapkey_arr[0])
get_tree(root)
console.log(root);
let current_width = back_track(root,1)
width = current_width>width ? current_width: width
}
return [width,depth]
}
console.log(getDimension(sourceData)) //[4,5]
同花顺
笔试(30道题,选择+简答+代码,时间略紧)
技术一面
- 除了try、catch外捕获异常的方法
- var、let、const的区别
- 箭头函数?sort中的回调函数的this指向
- 密码传输加密方法
- 滚轴回到顶部怎么做
京东
技术一面
- 原型链
Object.prototype.test = 123
let a = ""
console.log(a.test) // 123
a.proto === String.prototype
String.proto === Function.prototype
Function.prototype.proto == Object.prototype
-
v-computed
if (a>0) return b+c
a不变时,该计算属性会重新渲染吗
-
vue有双向绑定了,为什么还需要虚拟DOM
技术二面
- git的fetch和pull的区别
总的来说 git pull约等于 git fetch + git merge
git fetch会将数据拉取到本地仓库,并不会自动合并,gitpull是从远程获取最新版本并merge到本地,同时git pull会让本地苦衷master的commitID改变。
- git如何删除之前的commit
- 先git log查看提交日志
- git rebase -i HEAD~1(对最近一次commit进行rebase)或git rebase -i 9fbf10(commit id)
- 设置操作指令drop
- 分别用递归和迭代实现斐波那契数列
- 事件循环
略
字节 小何健康
技术一面
- script异步加载的方式
- flex:1的含义
- flex-bias
- align-content
- position:sticky
- tcp如何保证可靠传输
- 算法:链表反转
var reverseList = function(head) {
var list = head;
var p = list;
var q= null;
while(p.next !== null) {
q = p.next;
p.next = q.next;
q.next = list;
list = q;
}
return list;
};
-
变量提升和函数提升的先后问题
结论:函数优先级高于变量提升,但遇到变量赋值时,会覆盖同名函数声明console.log(bar); // f bar() { console.log(123) } console.log(bar()); // undefined var bar = 456; function bar() { console.log(123); // 123 } console.log(bar); // 456 bar = 789; console.log(bar); // 789 console.log(bar()) // bar is not a function
相当于
// 函数提升,函数提升优先级高于变量提升 var bar = function() { console.log(123) }; // 变量提升,变量提升不会覆盖(同名)函数提升,只有变量再次赋值时,才会被覆盖 var bar; console.log(bar); console.log(bar()); // 变量赋值,覆盖同名函数字面量 bar = 456; console.log(bar); // 再次赋值 bar = 789 console.log(bar); console.log(bar());
-
SPA
单页Web应用(single page web application,SPA): SPA 是一种特殊的 Web 应用,是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的。它将所有的活动局限于一个 Web 页面中,仅在该 Web 页面初始化时加载相应的 HTML 、 JavaScript 、 CSS 。一旦页面加载完成, SPA 不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换 HTML(采用的是 div 切换显示和隐藏),从而实现UI与用户的交互。
特点:- MVVM开发模式,前后端各司其职
- ajax:数据通过ajax同步
- 路由:在单页面下通过变化url地址来实现多页面效果
-
路由模式,为什么history需要后端支持
- css的加载会不会阻塞页面渲染
会,css的加载参与css规则树的构建,而render tree又需要css规则树,所以会阻塞
例外:media query声明的css不阻塞渲染
技术二面
- 移动端前端适配方案
- rem和em区别
- em是相对长度单位,相对于当前对象内文本的字体尺寸。如果当前对行内字体尺寸未被认为设置,则相对于浏览器的默认字体尺寸,会继承父集元素的字体大小。
- 所有未经调整的浏览器默认:1em = 16px
- 为简化,在body选择器中声明font-size = 62.5%,使em变为16*62.5% = 10px,方便计算
- rem是css新增的相对单位,他与em的区别在于他相对的是html跟元素
- 补充:rpx,微信小程序尺寸单位,规定:屏幕宽750rpx,具体机型具体值
- em是相对长度单位,相对于当前对象内文本的字体尺寸。如果当前对行内字体尺寸未被认为设置,则相对于浏览器的默认字体尺寸,会继承父集元素的字体大小。
- vw、vh、css倍率
- cache-control
- 防抖实现
- 垂直居中,水平右对齐
- xss攻击
- 前端性能优化
- 二分查找
- http 2.0
字节 新业务
技术一面
-
微信小程序相关
- 小程序生命周期
- onLanuch和onLoad的区别
- rpx
-
css相关
-
盒子模型 W3C(标准)和 IE的区别:
box-sizing:border-box开启IE计算
默认是content-box(W3C)
-
如何获取元素高度
document.getElementById() or document.getElementByClassName()
.clientHeight =》content+padding的高度
.offsetHeight =》算上border的高度如果要只算content的高度 那么就需要声明
docuement.getElementById().style.height
但此时只能取到内联属性height
即:<div class="content-box border-style padding-style margin-style" style="height:50px"> // "50px"
如果要求不使用内联属性,则要使用
getComputedStyle(document.getElementsById().height
此时就是css经渲染计算之后的结果,可以得到纯content的高度 -
flex:1的意思
flex:1是flex:1 1 0的简写(实际上是 flex:1 1 任意数字+任意长度单位)
依次为——
flex-grow:1默认为0,存在剩余空间也不放大
flex-shrink:1默认为1,空间不够时缩小
flex-basis:给上面两个属性分配多余空间前,计算项目是否有多余空间,默认为auto,即项目本身大小-
flex-basis详细解释:
flex-basis 指定了 flex 元素在主轴方向上的初始大小,如果不使用 box-sizing 改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的尺寸。
首先,flex-item的宽度由自身尺寸 、flex-basis、flex-grow/flex-shrink 共同决定
其次,不做声明时,flex-basis的默认值是auto,完全根据自身尺寸渲染,自身尺寸渲染优先级如下:
min-width >= max-width >width >content size
而在声明时,flex-basis的优先级大于width,同时设置两者则width不起作用;
只设置width时会生效,是因为basis尺寸按照自身尺寸来算,自身尺寸又取决于width。两者的区别体现在当内容超过限定宽度时是否会超出边界,flex-basis的设置会按最小宽度限时,但width不会,它会让字符溢出。
综上可得出结论:width:100px + flex-basis:auto => 元素自身100px content + flex-basis:100px => max(content,flex-bais) =>元素自身大于等于100px
-
-
BFC?如何变成BFC
Block Formatting Context 块级格式上下文,独立渲染区域,处于BFC内部的元素与外部元素定位互不影响。
触发条件:
- float不为none
- overflow 不为visible
- display: table-cell、table-caption、inline-block、flex, inline-flex 块容器
- position 不为relative或static 中的任一个
- 根元素
-
margin塌陷现象
- 垂直并列,以两盒最大外边距为准
- 嵌套关系,父级元素塌陷
父子都设置了同方向的margin,如子:margin-top:20px,父:margin-top:50px,两个属性取最大值,显示为父元素的margin,即父元素距离上边的元素,子元素紧贴父元素;此时只给子元素设置margin-top:50px也是同样的效果。
-
下方html中sec元素的高度为?
<style> html,*{ padding: 0;margin: 0; } #sec{ background: #f00; } .child{ height: 100px; margin-top: 10px; background: yellow; } </style> </head> <body> <section id="sec"> <article class="child"> </article> </section> </body>
sec元素高度为100px,父子嵌套发生了margin塌陷,如果要实现父元素110px,则要求父元素为bfc
如:border: 1px solid transparent; padding: 10px; overflow:hidden; position: fixed; display: flex; display:table; /* 以上任一即可,总之就是让父元素变成bfc */
-
-
js相关
-
如何判断基本类型和引用类型
- typeof
number、string、undefined、boolean、function、symbol都可以直接判断出来 - 区分对象和数组
(1) isArray (2).length (3).contructor() (4).tostring()
- typeof
-
引用类型深拷贝(对象中有函数的情况如何)
最简单的api:JSON.parse(JSON.stringify())
但它无法解决三种情况:- 循环引用
- 无法拷贝特殊对象,诸如RegExp,Date,Set,Map
- 无法拷贝函数
针对以上情况的解决办法:
- 创建一个Map。记录下已经拷贝过的对象,如果说已经拷贝过,那直接返回它行了。
- 针对不同类型,做不同的特殊处理。核心:
const ctor = target.constructor
newobj = new ctor()
即重新构造该结构 - 函数分箭头函数和普通函数,箭头函数不是任何类的实例,每次调用都是不一样的引用,普通函数都是Function的实例,所以分情况处理
最终版代码:
//获得对象类型 const getType = obj => Object.prototype.toString.call(obj) //判断是否为非空对象 const isObject = target => (typeof target === 'object' || typeof target === 'function') && target !==null if(map.get(obj)){ return obj } // 需要额外处理的结构 const canTraverse = { '[object Map]': true, '[object Set]': true, '[object Array]': true, '[object Object]': true, '[object Arguments]': true, }; // 全类型变量对应变量 const mapTag = '[object Map]'; const setTag = '[object Set]'; const boolTag = '[object Boolean]'; const numberTag = '[object Number]'; const stringTag = '[object String]'; const symbolTag = '[object Symbol]'; const dateTag = '[object Date]'; const errorTag = '[object Error]'; const regexpTag = '[object RegExp]'; const funcTag = '[object Function]'; // RegExp的处理 const handleRegExp = (target) =>{ const {source , flags} = target return new target.constuctor(source,flags) } // 函数的处理 const handleFunc = (func) => { //箭头函数直接返回,每个引用都是新的地址 if(!func.prototype) return func const bodyReg = /(?<={)(.|\n)+(?=})/ const paramsReg = /(?<=\().+(?=\))/ const functionString = func.toString() const param = paramsReg.exec(functionString) const body = bodyReg.exec(functionString) if(!body) return null if(param){ const paramArr = param[0].split(','); return new Function(...paramArr,body[0]) }else{ return new Function(body[0]) } } //无法遍历的变量处理 const handleNotTraverse = (target,tag) =>{ //取它的构造函数 const Ctor = target.constuctor switch(tag) { case boolTag: return new Object(Boolean.prototype.valueOf.call(target)); case numberTag: return new Object(Number.prototype.valueOf.call(target)); case stringTag: return new Object(String.prototype.valueOf.call(target)); case symbolTag: return new Object(Symbol.prototype.valueOf.call(target)); case errorTag: case dateTag: return new Ctor(target); case regexpTag: return handleRegExp(target); case funcTag: return handleFunc(target); default: return new Ctor(target); } } const deepClone = (obj , map = new WeakMap())=>{ //若不是引用类型,直接return if(!isObject(obj)) return obj //取出具体类型 【object xxxx】 let type = getType(obj) let newobj //处理非循环对象 if (!canTraverse[type]) { return handleNotTraverse(obj,type) }else{ let ctor = obj.constuctor newobj = new ctor() } if(map.get(obj)){ return obj } map.set(obj,true) //处理map if(type === mapTag){ target.forEach((item,key)=>{ newobj.set(deepClone(key,map),deepClone(item,map)) }) } //处理set if(type === setTag){ target.forEach(item=>{ newobj.add(deepClone(item,map)) }) } //处理数组和对象 for(let item in obj){ if(typeof item ==='object'){ map.set(obj,true) newobj[item] = deepClone(item) }else{ newobj[item] = obj[item] } } return newobj }
补充:WeakMap类,与Map类似,但WeakMap
1. 只接受对象作为key
2. key所用对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。
3. 没有size属性
4. 没clear()方法
5. 不能遍历
-
-
es6相关
- 执行顺序题
const list = [1, 2, 3] const square = num => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num * num) }, 1000) }) } function run() { list.forEach(async x=> { console.log(x) const res = await square(x) console.log(res) }) } run()
结果:先输出123,1s后输出149
延伸:如何在不修改square的情况下,每过1秒输出一个数字async function run() { for(let x of list){ console.log(await square(x)); } } run()
这里主要考虑forEach和for循环的差别,forEach是对每项执行同一个回调函数,而for循环则是顺序执行,遇到await会暂停后边的遍历,如:
list = [1,2,3] const run1 = () =>{ list.forEach(async(i)=>{ await new Promise(res=>{ setTimeout(()=>{ res(1) },1000) }) console.log(i); }) } const run2 = async() =>{ for(let i = 0 ; i<list.length ; i++){ console.log(list[i]); await new Promise(res=>{ setTimeout(()=>{ res() },1000) }) } } run1() // 1秒后一起输出123 run2() // 1 。。。1s后输出 2 。。。1s后输出 3
+ 网络相关 + xss攻击和csrf攻击 + tcp如何保证可靠传输 + tcp重传机制 + 协商缓存和强缓存 + cookies如何只让请求携带 而不让浏览器获取 + 算法 + 顺时针便利二维数组 顺时针遍历二维数组 [[1,2,3], [4,5,6], [7,8,9]] 1 2 3 6 9 8 7 4 5 + 二叉树层序遍历