面经总结

字节商业变现

一面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
new Promise(function(resolve) {
for(var i=0 ; i < 10 ; i++) {
resolve(i);
}
}).then(function(i) {
console.log(i);
});
new Promise(function(resolve) {
for(var i=0 ; i < 10 ; i++) {
function a() {
resolve(i);
}
}
a();
}).then(function(i) {
console.log(i);
});
// 上面打印 0
// 下面打印 10(因为使用 var 定义是函数作用域,不会为每一个 i 保存一块内存)
// 这里一开始说错了,完全反过来,面试官还引导我说定义了一个函数而不是立即执行,太幸运了
0.1+0.2

add(a, b, precision)

DOM事件机制
target/currentTarget

1
2
3
4
5
比如现在有如下结构:
.parent>.children
现在点击children,则parent和children都会监听到点击事件
对于target,两次都是children
但是对与currentTarget,第一次是childern,第二次是parent

tcp三次握手、四次挥手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
三次挥手:
首先sc双方均处于closed状态
1. c向s发送SYN包,和初始序号seq,请求同步连接,c改变为SYN-SENT状态。
2. s收到SYN包后,改变状态为SYN-RECV状态,并将SYN包,ACK标志位置为1表示接收同步请求,seq,ack应答发送给c
3. c收到s发来的ACK标志位,发现同意,则发送ACK标志位为1,seq,ack发送给s
4. 双方进入ESTABLISHED状态

四次挥手:
首先sc双方均处于ESTABLISHED状态
1. c向s发送FIN包,和seq
2. 但由于s可能还有数据未发送完,所以并不会立即发送FIN包来响应c,而是发送ACK,表示我收到了你的FIN,但现在还不能终止。
3. s数据发送完毕后,会发送FIN包给c
4. c收到FIN包后,会告知s,ACK,表示c收到了s的FIN包
5. 此后c会等待2MSL,这个状态是为了确保s收到了c的确认信息
为什么是2MSL?
MSL指的是报文最大生成时间,目的是保证本次连接所用的数据包完全在网络中小时,以免影响之后的连接。

new Function()过程

1
首先,会创建一个对象实例,该实例的__proto__属性指向Function的property属性,创建之后,将Function的this指向绑定为该对象,进行执行,若Function返回结果是引用类型,则会返回该引用类型给,否则会返回该实例。

编程实现
还原扁平的树结构,结构如下,id表示当前节点id,parentId表示其父节点id。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const listTree = [
{ id: 1, parentId: 2 },
{ id: 2, parentId: 0 },
{ id: 3, parentId: 4 },
{ id: 4, parentId: 0 },
{ id: 5, parentId: 4 },
{ id: 6, parentId: 2 },
{ id: 7, parentId: 2 },
{ id: 8, parentId: 3 },
]

const listTree = [
{ id: 1, parentId: 2 },
{ id: 2, parentId: 0 },
{ id: 3, parentId: 4 },
{ id: 4, parentId: 0 },
{ id: 5, parentId: 4 },
{ id: 6, parentId: 2 },
{ id: 7, parentId: 2 },
{ id: 8, parentId: 3 },
]

function buildTree(listTree) {
const tree = {
id: 0,
children: []
}
function findChid(parentNode) {
parentNode.children = listTree.filter(node => node.parentId === parentNode.id)
if (!parentNode.children) return
parentNode.children.forEach(node => {
findChid(node)
});
}
findChid(tree)
return tree
}

console.log(buildTree(listTree));

二面

为什么 JS 的基本数据类型能够拥有方法,为什么对基本数据类型添加属性或者方法不生效

1
2
3
4
5
6
let str = 'test'
let str2 = str.substring(2)
console.log(str2) // st
const me='bytedance';
me.age=9;
console.log(me.age); // undefined
1
对于基本类型,js会对该基本类型进行包装,其实其方法均在String.prototype上,对基本数据类型添加属性不生效是因为,在调用结束后会销毁改包装类型,要想添加方法,可以在String.prototype上添加,这样在调用普通字符串方法时,js会创建一个临时的实例,该实例在找自定义的这个方法时,就会去原型对象上去找,然后在摧毁这个临时实例。

看输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Q1
var a = 1;
function print () {
console.log(this.a)
}
print() // 1
// Q2
const obj = {
a: 2,
print: function () { console.log(this.a) }
}
obj.print(); // 2
// Q3
function Person () {
this.a = 8
this.print = function () {console.log(this.a)}
return {a: 9}
}
const p = new Person()
console.log(p.a) // 9
console.log(p.print()) // 报错,返回的对象中没有 print 方法
// Q4
'use strict';
var a = 1;
function print () {
console.log(this.a)
}
print() // 报错 不能给 undefined 添加属性

期间聊了关于 symbol、BigInt、==、===

1
2
symble可以生成一个独一无二的值,Symbol()和Symbol.for()虽然都是创建独一无二的值,但for会事先在全局表中查括号里面的值对应的独一无二,若找到,则不再创建。
BigInt可以计算任意长度的整数,且仅支持整数。

写题,一道插花的贪心题

1
2
3
4
5
6
7
8
9
10
11
12
13
function insideFlowers(num, tem) {
let count = num - 1
let res = 0
for (let i = tem.length - 1; i > 0; i--) {
if (tem[i] === '1') {
res += (i - count)
count--
}
}
return res
}

console.log(insideFlowers(3, '101010'));

写 bind 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Function.prototype.myBind = function (ctx, ...args1) {
return (...args2) => {
let fn = Symbol()
ctx[fn] = this
let res = ctx[fn](...args1, ...args2)
delete ctx[fn]
console.log(ctx);
return res
}
}

function a(a, b) {
// console.log(this);
return this.a + b
}
let b = {
a: 9
}
console.log(a.myBind(b, 1, 3)());

console.log(b);

三面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let str  =  'my name is {{name}}, age is {{age}}'
let data = {name: 'tom', age: 16 }
function template(str, data) {
let arr = [];
let reg = /[\{]{2}([\w]+)[\}]{2}/g;
return str.replace(reg, function(item) {
console.log(item);
const _item = item.slice(2, item.length - 2);
console.log(_item);
if (data[_item]) {
return data[_item]; //
} else {
return "undefined";
}
});
}

4、箭头函数有哪些不适用的场景?

构造函数
this 绑定
与 this 相关都需要注意
5、HTTP 常用响应码介绍
1:表示服务器收到,需要进一步操作
2
:表示响应成功
200: ok
3:表示重定向(304表示资源未改变)
301: 永久重定向
302:临时重定向
304:资源未更改(在缓存中使用)
4
:表示客户端发生错误
400:客户端错误
401:身份认证错误
403:资源不允许访问
404:资源未找到
5**:表示服务器发生错误
500:服务器内部错误
6、TCP 和 UDP 区别
TCP 可靠,面向链接, 1对1, 字节流 HTTP
UDP 不可靠,尽全量交付 1对1 1对多,面向bao’w DNS

7、HTTPS过程 ?
http + ssl
对称加密
非对称加密
CA机构

8、实现一个自己 axois()

用 Promise 封装 xhr 写了一下
9、fetch 和 xhr 区别?

fetch返回 promise
xhr 有 abort
fetch 需要调用 json text方法才能获取数据,链式;
10、常用的设计模式介绍? 实现一个发布订阅;

单例
发布订阅/观察者
代理