字节跳动基础架构前端暑期实习(已offer)
4.20一面 4.28二面 5.12三面 5.17hr面 5月24offer
一面(40min)
1.js的基础数据类型
String Boolean Number Object Null Undefined Symbol BigInt
2.基础类型和引用类型的区别
这些数据可以分为原始数据类型和引用数据类型:
- 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
- 堆:引用数据类型(对象、数组和函数)
两种类型的区别在于存储位置的不同:
- 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
- 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
3.判断数据类型的方法
(1)typeof
(2)instanceof
(3) constructor
(4)Object.prototype.toString.call()
同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。
4.手写instanceof
function myInstanceof(leftValue,rightValue){
let rightPrototype = rightValue.prototype
leftValue = leftValue.__proto__
while(true){
if(leftValue === null){
return false
}else if(leftValue === rightPrototype){
return true
}
else{
leftValue = leftValue.__proto__
}
}
} 5.说一下apply、call、bind的区别
它们的作用都是将this绑定在指定对象上。
- apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给被调用的函数。
- call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数。
- bind函数允许传入需要给this绑定的对象,并且可选的传入全部或一部分形参,然后返回一个新的函数
6.看代码说输出
function Person(name){
this.name = name
}
Person.prototype.say = function(name){
return this.name
}
Person('abc')
const a = new Person('abc').say.call({})
console.log(a)
const p = {
x:'Y'
}
fn = () =>{
this.x = 'z'
}
fn.call(p)
console.log(p) 7.说一下vue数据的双向绑定
Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
主要分为以下几个步骤:
- 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
- compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
- Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
- MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
8.代码实现发布订阅机制(我只写出了发布和订阅,取消订阅没写出来,说了一下思想)
class EventCenter{
// 1. 定义事件容器,用来装事件数组
let handlers = {}
// 2. 添加事件方法,参数:事件名 事件方法
addEventListener(type, handler) {
// 创建新数组容器
if (!this.handlers[type]) {
this.handlers[type] = []
}
// 存入事件
this.handlers[type].push(handler)
}
// 3. 触发事件,参数:事件名 事件参数
dispatchEvent(type, params) {
// 若没有注册该事件则抛出错误
if (!this.handlers[type]) {
return new Error('该事件未注册')
}
// 触发事件
this.handlers[type].forEach(handler => {
handler(...params)
})
}
// ***移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
removeEventListener(type, handler) {
if (!this.handlers[type]) {
return new Error('事件无效')
}
if (!handler) {
// 移除事件
delete this.handlers[type]
} else {
const index = this.handlers[type].findIndex(el => el === handler)
if (index === -1) {
return new Error('无该绑定事件')
}
// 移除事件
this.handlers[type].splice(index, 1)
if (this.handlers[type].length === 0) {
delete this.handlers[type]
}
}
}
} 9.说一下同源策略
同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是:**protocol(协议)、domain(域名)、port(端口)**必须一致。
同源政策主要限制了三个方面:
- 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
- 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
- 当前域下 ajax 无法发送跨域请求。
同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作
10.怎么解决跨域
(1)CORS
(2)JSONP
(3)nginx代理跨域
(4)document.domain + iframe跨域
(5)WebSocket协议跨域
11.说一下简单请求和非简单请求,简单请求有没有post
简单请求不会触发CORS预检请求。若该请求满足以下两个条件,就可以看作是简单请求:
1)请求方法是以下三种方法之一:
- HEAD
- GET
- POST
2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
若不满足以上条件,就属于非简单请求了。
(1)简单请求过程:
Access-Control-Allow-Credentials: true // 表示是否允许发送Cookie Access-Control-Allow-Origin: <http://api.bob.com> // 和Orign一直 Access-Control-Expose-Headers: FooBar // 指定返回其他字段的值 Content-Type: text/html; charset=utf-8 // 表示文档类型
如果Orign指定的域名不在许可范围之内,服务器会返回一个正常的HTTP回应,浏览器发现没有上面的Access-Control-Allow-Origin头部信息,就知道出错了。这个错误无法通过状态码识别,因为返回的状态码可能是200。
在简单请求中,在服务器内,至少需要设置字段:Access-Control-Allow-Origin
(2)非简单请求过程
非简单请求是对服务器有特殊要求的请求,比如请求方法为DELETE或者PUT等。非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。
浏览器会询问服务器,当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求,否则就会报错。
预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Orign,表示请求来自哪个源。除此之外,头信息中还包括两个字段:
●Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。
●Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断,如果返回的头信息在中有Access-Control-Allow-Origin这个字段就是允许跨域请求,如果没有,就是不同意这个预检请求,就会报错。
服务器回应的CORS的字段如下:
Access-Control-Allow-Origin: <http://api.bob.com> // 允许跨域的源地址
只要服务器通过了预检请求,在以后每次的CORS请求都会自带一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
在非简单请求中,至少需要设置以下字段:
Access-Control-Allow-Origin: <http://api.bob.com> // 允许跨域的源地址 Access-Control-Allow-Methods: GET, POST, PUT // 服务器支持的所有跨域请求的方法 Access-Control-Allow-Headers: X-Custom-Header // 服务器支持的所有头信息字段 Access-Control-Allow-Credentials: true // 表示是否允许发送Cookie Access-Control-Max-Age: 1728000 // 用来指定本次预检请求的有效期,单位为秒
在非简单请求中,至少需要设置以下字段
'Access-Control-Allow-Origin' 'Access-Control-Allow-Methods' 'Access-Control-Allow-Headers'
反问:
对我以后学习前端有什么建议?
还是要巩固一下基础。了解一下前端工程化,项目怎么从0到1,webpack的原理,了解怎么优化,异步加载,less的配置还有原理,vue的内部实现机制,vue怎么封装的,babel等等。平时多看一些评价比较好的书籍。
二面(1h)
1.自我介绍
2.挑最近的项目介绍一下
3.怎么实现的手机端适配
4.3d地图怎么实现的?(他自己在那边搜了cesium,然后打开了项目地址)
5.对地图加载过慢有没有想过什么优化方式?(说了图片懒加载的原理)
6.长列表怎么优化的?
7.微信支付的实现方式?为什么不使用长连接,而是轮询方式?
8.怎么实现对按钮的权限控制?
9.怎么开始学的前端?
10.能实习多长时间?
11.实现扁平化
12.对以后前端方面的规划
反问:
1.公司里是怎么实现组件化的开发的?
需要有一个人去拉组件开发进度,然后组里互相沟通。
2.jsonp会不会阻塞js的解析?
他说会,然后吐槽现在谁还用jsonp(哔哩哔哩还用),要想不阻塞的话加asyc或者defer什么的(但是去之前查了资料jsonp创建的是动态脚本元素,不会阻塞其他页面进程,这部分感觉还是很有讨论空间的我感觉,但jsonp确实很老了)
三面(1h10min)
1.实习的时间
2.项目介绍
3.项目里的视频是怎么实现的?那你知道视频怎么从服务器加载到页面上的吗?(视频和其他资源不一样,有一个单独的应用协议)
4.组件间的传值你知道有哪些?说一下v-model,说一下双向数据绑定,Object.defineProperty是在哪一步实现的,说一下模板解析过程,mustache和vue的解析是不一样的,你具体了解过vue的模板解析过程吗?
5.说一下浏览器本地存储**、**LocalStorage和SessionStorage的区别?LocalStorage有什么限制吗?别的页面能访问这个页面的localStorage吗?如果是你设计LocalStorage,LocalStorage存满了又来了新数据,你怎么处理?(LS存储)
6.vue路由的原理?浏览器路由的原理?vue和浏览器对应的api?浏览器后退一定会回到上一个页面吗?
7.说一下单点登录?怎么实现的单点登录?说一下app扫描二维码登录网页的实现?
8.说一下http缓存?协商缓存的过程?Etag怎么计算的?一个场景:一个请求需要在不同的CDN服务器上请求资源,每个请求到达服务器的时间不一定?你要怎么算Etag?
9.说一下你的优点和缺点?
反问:
字节对前端工程师的要求是什么?
首先基础要扎实,学习能力要很强,对于计算机科班的要对和前端相关的计算机基础学科要有相当程度的深度掌握。
对我的建议?
有机会的话,尽量要能参与上线的项目实践,实践很重要,光看书的话有些知识不会遇到也不会去思考,实践很重要。
HR(12min)
三个部分:
- 互相做自我介绍。
- 项目遇到问题,怎么解决的,带来的思考,毕业之后的规划,有无转正意向。
- 解答我的问题:上海目前的情况是否线下实习?公司希望的话是线下实习,但也要看疫情的情况什么时候能有反馈?本周内。

查看22道真题和解析