# 2020 / 3 / 8

内容出自掘友「WaterMan」 入口:https://juejin.im/post/5c19c1b6e51d451d1e06c163#heading-2

# 腾讯

# 1 / 知道什么是事件委托吗?

所谓事件委托,就是利用事件冒泡的原理,将自己所触发的事件,让父元素代替执行

即:不在触发事件的元素(直接Dom)上设置监听事件,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生在哪一个子元素上来做出不同的响应

为什么要使用事件委托?

  • 提高性能
  • 新增加的元素也能触发绑定在父元素上的监听事件

# 2 / 对Promise了解吗?(主观)

Promise是异步编程得一种常见方法,它是一个对象,通过它可以获取异步操作的结果,做出相应的回调

Promise对象具有以下特点:

  • Promise的状态不受外界影响,Promise对象代表一个异步操作,具有三种状态:pending(等待态),fulfilled(完成态),rejected(失败态)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  • 一旦状态改变,就不会再次改变
  • 任何时候都可以得到结果

Promise对象的状态改变,只有两种可能:

  • pending → fufilled
  • pending → rejected

只要这两种情况发生,状态就凝固了(不再发生变化),会一致保持这个结果,这时就称为resolved(已定型),如果改变已经发生,再对Promise对象添加相应回调(then()/catch()),也会立即得到这个结果

# 3 / 你之前遇到过跨域问题吗?是怎么解决的

解决跨域的方法:

  • jsonp(仅能处理get)
  • cors(服务端设置响应头 Access-Control-Allow-Origin)
  • window.name(利用浏览器窗口内,载入所有的域名都共享一个window.name)
  • document.domain / window.postMessage()

# 4 / 代码题

有一个类如下:

function Person(name){
    this.name = name
}
let p = new Person('Tom')
1
2
3
4
  1. p.proto 等于什么? 答:Person.prototype

  2. Person.proto 等于什么? 答:Function.prototype

# 5 / typeof 和 instanceof 的区别

  • typeof
    • 能够正确判断简单数据类型(原始类型),除了null以外,因为 typeof null = 'object'
    • 对于对象而已,typeof 不能正确判断对象类型,typeof仅可以区分开function,除此之外,结果均为 object
  • instanceof
    • 能够准确判断复杂数据类型,但是不能正确判断简单数据类型
    • 原理:通过原型链判断

# 6 / new & instanceof的内部机制

  • new
    • 创建一个新对象(空对象)
    • 对这个新对象进行原型链接
    • 将this指向新创建的对象,并执行构造函数中的方法
    • 如果函数没有返回其他对象,那么this指向这个新对象,否则this指向构造函数返回的对象

· 手写new

function myNew(){
    const obj = function(){}
    const Constructor = [].shift.call(arguments) // 删除第一个参数并获取这个参数

    obj.__proto__ = Constructor.prototype

    const returnObj = Constructor.apply(obj, arguments) // this指向这个新对象,并且执行构造函数

    return typeof returnObj === 'object' ? returnObj : obj
}
1
2
3
4
5
6
7
8
9
10
  • instanceof
    • 通过原型链进行判断,例如 A instanceof B,在A的原型链中层层查找,查找是否有原型等于 B.prototype,如果一直找到A的原型链顶端(Object.prototype.proto),仍然找不到,那么返回 false,否则返回 true

· 手写 instanceof

function myInstanceof(left, right){
    let rightPrototype = right.prototype
    let leftPrototype = left.__proto__

    while(true){
        if(leftPrototype === null) return false
        if(leftPrototype === rightPrototype) {
            return true
        }

        leftPrototype = leftPrototype.__proto__
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7 / 下面代码输出什么?

for(var i = 0; i < 10; i++){
    setTimeout(() => {
        console.log(i)
    }, 0)
}
1
2
3
4
5

答:输出10个10

追问:如何输出0-9?

  • 闭包
for(var i = 0; i < 10; i++){
    (function(j){
        setTimeout(() => {
            console.log(j)
        },0)
    })(i)
}
1
2
3
4
5
6
7
  • var 改为 let
for(let i = 0; i < 10; i++){
    setTimeout(() => {
        console.log(i)
    }, 0)
}
1
2
3
4
5

# 8 / 说一下箭头函数This指向问题?

箭头函数没有自己的this,当我们使用箭头函数时,箭头函数会默认帮我们将绑定外层this的值,即箭头函数的this值与外层的this值一致

# 9 / for...in迭代和for...of有什么区别?

  • for in
    • 遍历对象及其原型链上可枚举的属性
    • 如果用于遍历数组,除了遍历其元素外,还会遍历数组对象自定义的可枚举属性及其原型链上的可枚举属性
    • 遍历对象返回的属性名和遍历数组返回的索引都是字符串类型
    • 某些情况,可能按随机顺序遍历数组元素
  • for of
    • 支持遍历数组,类数组对象,字符串,Map对象,Set对象
    • 不支持遍历普通对象
    • 遍历后输出的结果是索引值(for in是索引)

# 10 / 使用过flex布局吗?flex-grow和flex-shrink属性有什么用?

  • flex-grow:用于项目的放大比例,默认为0,即存在剩余空间,也不放大
  • flex-shrink:用于项目的缩小比例,默认为1,即空间不足时,该项目将缩小

# 11 / Http请求中的keep-alive有了解吗?

传统的Http都是一次Tcp连接一次request,这种情况效率较低,而在http1.1协议中添加了keep-alive的支持,并默认开启

客户端和服务器在建立连接并完成request后并不会立即断开Tcp连接,而是在下次request时复用这次Tcp连接,但这里也必须要有Tcp连接的timeout时间限制,不然会造成服务器端口被长期占用。

使用keep-alive可以在一次Tcp连接中持续发送多份数据,通过keep-alive减少Tcp连接次数,以此提高性能和提高http服务器的吞吐量。

长时间的Tcp连接容易导致系统资源无效占用,配置不当的keep-alive,有时损失更大,所以正确配置keep-alive的timeout时间非常重要

# 12 / 数组扁平化处理:实现一个flatten方法,使得输入一个数组,该数组里面的元素也可以是数组,该方法会输出一个扁平化的数组

function flatten(arr){
    return arr.reduce(function(prev, item){
        return prev.concat(Array.isArray(item) ? flatten(item) : item)
    },[])
}
1
2
3
4
5

一行代码解决:

Array.from(new Set(arr.flat(infinity))).sort((a, b) => { return a - b})
1