这期分享的是 Shopee
的面经,中间较为曲折。先走了一轮卖家平台的面试,HR
面面完之后说不合适。后面供应链部门 HR
又找到我,说可以再面一次供应链部门,所以就有了两次面经,每次都有技术一二面以及 HR
面
自己是三月份面的 Shopee
,以下作为自己的面经记录(当时算是一年半多经验),有一些问题会总结归纳知识点,希望对大家有所帮助【仅供参考,欢迎讨论】
前面整理了一下 Bigo
的,感兴趣可以看下:【面试说】一年半前端 Bigo 一二三 面[1]
谈谈对面经的看法:关于面经,其实个人的理解是作为一个查漏补缺的作用,深入的话,还是得靠其它途径的学习和实践
关于事件循环,我写了一篇 【前端进阶】深入浅出浏览器事件循环【内附练习题】[2],我自认为是比较深入浅出。能够做出文末的题目,你就成功了
类似如下题目:
// 基本数据类型-栈内存
let a1 = 0;
// 基本数据类型-栈内存
let a2 = 'this is string';
// 基本数据类型-栈内存
let a3 = null;
// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let b = { m: 20 };
// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
let c = [1, 2, 3];
对于原始类型,数据本身是存在栈内,对于对象类型,在栈中存的只是一个堆内地址的引用
上面的如下图所示:
内存中栈区的数据,在函数调用结束后,就会自动的出栈,不需要程序进行操作,操作系统会自动执行,换句话说:栈中的变量在函数调用结束后,就会消失
那么在栈中存储不了的数据(比如一个对象),就会被存储在堆中,栈中就仅仅保留一个对该数据的引用(也就是该块数据的首地址)
参考:「前端进阶」JS中的栈内存堆内存[3]
箭头函数、没有 prototype
、没有自己的 this
指向、不可以使用 arguments
、自然不可以new
this
this
指向永远不变this
的指向prototype
new
arguments
参考:ES6 - 箭头函数、箭头函数与普通函数的区别[4]
function create (ctr) {
// 创建一个空对象
let obj = new Object()
// 获取构造函数
let Con = [].shift.call(arguments)
// 将对象(实例)的 __proto__ 和构造函数的 prototype 绑定
obj.__proto__ = Con.prototype
// 绑定this,以及参数
let result = Con.apply(obj, arguments);
// 确保返回的是对象
return typeof result === 'object'? result : obj;
}
instanceof
主要的作用就是判断一个实例是否属于某种类型,其原理的实现类似如下:
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
其实 instanceof
主要的实现原理就是只要右边变量的 prototype
在左边变量的原型链上即可。因此,instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype
,如果查找失败,则会返回 false
,告诉我们左边变量并非是右边变量的实例
参考:浅谈 instanceof 和 typeof 的实现原理[5]
跨站点请求伪造,其原理是攻击者构造网站后台某个功能接口的请求地址,诱导用户去点击或者用特殊方法让该请求地址自动加载。用户在登录状态下这个请求被服务端接收后会被误以为是用户合法的操作。对于 GET
形式的接口地址可轻易被攻击,对于 POST
形式的接口地址也不是百分百安全,攻击者可诱导用户进入带 Form
表单可用 POST
方式提交参数的页面
参考:「每日一题」CSRF 是什么?[6]
xhr.withCredentials = true;
强缓存:200 OK (from memory/disk cache) 协商缓存:200 和 304
具体流程见下图:
参考:区分http请求状态码来理解缓存(协商缓存和强制缓存)[7]
如果一个 cookie
被设置了 Secure=true
,那么这个 cookie
只能用 https
协议发送给服务器,用 http
协议是不发送的
设置了 http-only
不能通过 JS
访问 cookie
,减少 XSS
攻击
e.target
指向触发事件监听的对象e.currentTarget
指向添加监听事件的对象参考:e.target与e.currentTarget的区别[8]
computed
不支持异步操作,当 computed
内有异步操作时无效,无法监听数据的变化computed
的值在 getter
执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取 computed
的值时才会重新调用对应的 getter
来计算参考:Vue的computed和 watched的区别[9]
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
详见官方文档[10]
props
当前组件接收到的 props
对象。Vue 实例代理了对其 props
对象属性的访问。
attr 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="
详细的,之前写过一篇文章:【Vue进阶】——如何实现组件属性透传?[11]
可以看我的另外一篇文章:深入浅出 Vue 中的 key 值[12]
只要同时满足以下两大条件,就属于简单请求
(1) 请求方法是以下三种方法之一:
(2)HTTP的头信息不超出以下几种字段:
参考:跨域资源共享 CORS 详解[13]
/**
* 深复制实现
*/
function clone(data) {
let result = {};
function isObject(data) {
if (
(typeof data === "object" || typeof data === "function") &&
data !== null
) {
return true
} else {
return false
}
}
if (Array.isArray(data)) {
result = [...data];
}
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (isObject(data[key])) {
result[key] = clone(data[key]);
} else {
result[key] = data[key];
}
}
}
return result;
}
new Promise(function(resolve,reject){
resolve(Promise.reject())
}).then(function () {
console.log(1)
}).catch(function() {
console.log(2)
})
答案是 2
参考:Promise.resolve()[14]
之前总结过 Cookie
的一篇文章: 前端须知的 Cookie 知识小结[15]
HTTP
的 OPTIONS
方法 用于获取目的资源所支持的通信选项。客户端可以对特定的 URL 使用 OPTIONS
方法,也可以对整站(通过将 URL 设置为“*”)使用该方法。OPTIONS
方法对服务器发起请求,以检测服务器支持哪些 HTTP
方法:curl -X OPTIONS http://example.org -i
参考:OPTIONS[18]
server Worker
追问:如果不用 server Worker,你会采用什么方案?因为 SW 本身就是有兼容性问题的?
localStorage
追问:如果不用 localStorage(因为容量有所限制),那你会用什么去实现呢?
indexDB
实际上就是利用闭包和高阶函数实现函数的缓存:
以下是我的实现
f1('abc', 123, {b:3}); // 10, 1000s
f1('abc', 123, {b:3}); // 10, 1000s
function cache(f) {
let objCache = {}
return function () {
let curArgs = ''
// 这里使用深复制会好点
for (let i = 0; i < arguments.length; i++) {
if (Array.isArray(arguments[i])) {
curArgs += arguments[i].join(',')
} else if (typeof arguments[i] === 'object') {
curArgs += JSON.stringify(arguments[i])
} else {
curArgs += arguments[i]
}
}
// curArgs
if (curArgs) {
return objCache[curArgs]
} else {
objCache[curArgs] = f(curArgs)
return objCache[curArgs]
}
}
}
f2 = cache(f1);
f2('abc', 123, {b:3}); // 10, 1000s
f2('abc', 123, {b:3}); // 10, 0s
其他实现:参考:JS如何实现函数缓存[19]
const memorize = function(fn) {
const cache = {} // 存储缓存数据的对象
return function(...args) { // 这里用到数组的扩展运算符
const _args = JSON.stringify(args) // 将参数作为cache的key
return cache[_args] || (cache[_args] = fn.apply(fn, args)) // 如果已经缓存过,直接取值。否则重新计算并且缓存
}
}
const add = function(a, b) {
console.log('开始缓存')
return a + b
}
const adder = memorize(add)
new Promise((resolve,reject) => {
console.log(1)
resolve()
console.log(2)
}).then(() => {
console.log(3)
})
(function () {
const a = b =1;
// 这里我理解相当于
// b = 1,const a = b
})()
console.log(typeof a) // undefined
console.log(typeof b) // number
参考:由ES规范学JavaScript(二):深入理解“连等赋值”问题[21]
题目和答案见为什么我认为数据结构与算法对前端开发很重要?[22]
function my_print(n)
{
for (var i = 0; i < n; i++) {
console.log("-\n");
my_print(n - 1);
}
}
my_print(3);
my_print(n);
f(n) = n (1 + f(n-1))
= n + n *(n-1) + n * (n-1) *(n-2) + ...+ n! = O(n!)
整体的面试体验非常好,供应链时候的面试是参加了专场,当天面完了技术一二面以及 HR
面,流程算是非常快了。笔者现已入职 Shopee
供应链部门,这边的 Leader
和同事们很 Nice
的(年轻有活力的团队)。技术上我们鼓励分享,目前主要技术栈有 Vue
和 React
看了一下,我们现在还有在招人,有想要内推的,可以将你的简历命名"岗位名称_名字_工作地点"发送到我的邮箱:15521091584@163.com
具体内推见详情[23]
一般工作日晚上才处理邮件,如回复慢,请见谅
[1]
【面试说】一年半前端 Bigo 一二三 面: https://juejin.im/post/6880028535101227021
[2]
【前端进阶】深入浅出浏览器事件循环【内附练习题】: https://juejin.im/post/6880419772127772679/#heading-1
[3]
「前端进阶」JS中的栈内存堆内存: https://juejin.im/post/6844903873992196110#heading-0
[4]
ES6 - 箭头函数、箭头函数与普通函数的区别: https://juejin.im/post/5c979300e51d456f49110bf0
[5]
浅谈 instanceof 和 typeof 的实现原理: https://juejin.im/post/6844903613584654344#heading-0
[6]
「每日一题」CSRF 是什么?: https://zhuanlan.zhihu.com/p/22521378
[7]
区分http请求状态码来理解缓存(协商缓存和强制缓存): https://www.cnblogs.com/fangsmile/p/13072940.html
[8]
e.target与e.currentTarget的区别: https://www.jianshu.com/p/1dd668ccc97a
[9]
Vue的computed和 watched的区别: https://juejin.im/post/5da7d371f265da5b7d691e3a
[10]
官方文档: https://cn.vuejs.org/v2/guide/mixins.html
[11]
【Vue进阶】——如何实现组件属性透传?: https://juejin.im/post/6865451649817640968
[12]
深入浅出 Vue 中的 key 值: https://juejin.im/post/6844903865930743815
[13]
跨域资源共享 CORS 详解: https://www.ruanyifeng.com/blog/2016/04/cors.html
[14]
Promise.resolve(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
[15]
前端须知的 Cookie 知识小结: https://juejin.im/post/6844903841909964813
[16]
The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Request-Method
[17]
请求头 Access-Control-Request-Headers 出现于 preflight request (预检请求)中,用于通知服务器在真正的请求中会采用哪些请求头。: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Request-Headers
[18]
OPTIONS: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/OPTIONS
[19]
JS如何实现函数缓存: https://blog.csdn.net/weixin_30925411/article/details/100090840
[20]
webpack使用HtmlWebpackPlugin进行cdn配置: https://www.jianshu.com/p/9248db0349fb
[21]
由ES规范学JavaScript(二):深入理解“连等赋值”问题: https://segmentfault.com/a/1190000004224719
[22]
为什么我认为数据结构与算法对前端开发很重要?: https://github.com/LeuisKen/leuisken.github.io/issues/2
[23]
详情: https://juejin.im/pin/6844910552704090119