前端

270 阅读4分钟

CSS部分

  • flex:1全称表示什么
  • animation动画(keyFrame动画)
  • 瀑布流的布局
  • 抽屉布局
  • 圣杯布局和双飞翼布局

js部分

  • 手写typeof
function getType (temp) {
  let str = temp.constructor.toString()
  return str.slice(9, str.indexOf('(')).toLowerCase()
}
  • this指针
  • 原型链
  • 让 a == 1 && a == 2 && a ==3 为true
// 方法一:
let val = 1
Object.defineProperty(window, 'a', { configurable: true, get: function () {
  return val++
} })

if (a == 1 && a == 2 && a == 3) {
  console.log('可以')
} else {
  console.log('不可以')
}
// 方法二:
const a = {
  i: 1,
  toString: function () {
    return this.i++
  }
}

if (a == 1 && a == 2 && a == 3) {
  console.log('可以')
} else {
  console.log('不可以')
}
  • 异步函数Promise
  • 手写Promise.race
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject)
    }
  })
}
  • 闭包(防抖和节流)

防抖: juejin.cn/post/711237…

  • es5和es6中继承
  • 深拷贝和浅拷贝
  • import和require的区别
  • forEach和map的区别
  • 实现一个bind函数 最简单的写法
       bind() {
            let that = this;
            return function () {
                that.apply(arguments)
            }
        }

重写函数原型上的bind函数

     if (!Function.prototype.bind){
        Function.prototype.bind = function () {
            let that = this
            let context = [].shift.call(arguments)
            let args = [].slice.call(arguments)
            return function () {
                that.apply(context,args.concat([].slice.apply(arguments)))
            }
        }
  • 宏任务、微任务
    console.log('start: 开始执行')
        async function async1() {
            console.log(1)
            await async2()
            console.log(2)
        }

        async function async2() {
            console.log(3)
            async function async3() {
                console.log(4)
                await setTimeout(function () {
                    console.log(5)
                }, 0)
            }
            async3()
        }

        console.log(6)
        async1()
        setTimeout(function () {
            console.log('end: 结束所有任务')
        }, 0)
        new Promise(function (resolve, reject) {
            console.log(8)
            resolve()
        }).then(function () {
            console.log(9)
        }).finally(function () {
            console.log('微任务执行完毕')
        })

        console.log('同步任务执行结束')

结果:
start: 开始执行
6
1
3
4
8
同步任务执行结束
2
9
微任务执行完毕
5
end: 结束所有任务

  • flat的作用
            Array.prototype.myFlat = function (d = 1) {
                let arr = [...this]
                return d > 0 ? arr.reduce((acc, val) => {
                    return acc.concat(Array.isArray(val) ? val.myFlat(d - 1) : val)
                }, []) : arr.slice()
            }
  • Promise.all原理
    Promise.myAll = function (arr) {
            let arrResult = []
            let count = 0
            let len = arr.length

            return new Promise(function (resolve, reject) {
                for (let i = 0; i < arr.length; i++) {
                    Promise.resolve(arr[i]).then(function (res) {
                        arrResult[i] = res
                        count++

                        if (count === len) {
                            return resolve(arrResult)
                        }

                    }, function (err) {
                        return reject(err)
                    })
                }
            })
        }
        
        let p1 = Promise.resolve(3);
        let p2 = 1337;
        let p3 = new Promise((resolve, reject) => {
            setTimeout(resolve, 100, 'foo');
        });

        Promise.myAll([p1, p2, p3]).then(res => {
            console.log(res)
        }).catch(err => {
            console.log(err)
        })
  • promise.all限制请求个数
    Promise.myAll = function (arr, limit) {
            let arrResult = []
            let count = 0
            let len = arr.length

            return new Promise(function (resolve, reject) {
                for (let i = 0; i < limit; i++) {
                    Promise.resolve(arr[i]).then(function (res) {
                        arrResult[i] = res
                        count++

                        if (count === len) {
                            return resolve(arrResult)
                        } else {
                            if (count < len) {
                                return continueStart(arr, count, resolve, reject, arrResult, len)
                            }
                        }

                    }, function (err) {
                        return reject(err)
                    })
                }
            })
        }

        function continueStart(arr, i, resolve, reject, arrResult, len) {
            return Promise.resolve(arr[i]).then(function (res) {
                arrResult[i] = res
                if (arrResult.length === len) {
                    return resolve(arrResult)
                } else {
                    let count = arrResult.length
                    if (count < len) {
                        return continueStart(arr, count, resolve, reject, arrResult, len)
                    }
                }
            }, function (err) {
                return reject(err)
            })
        }
  • 深copy
    function deepClone(obj) {
            let arr = Array.isArray(obj) ? [] : {}

            if (obj && typeof obj === 'object') {
                for (let item in obj) {
                    if (obj[item] && typeof obj[item] === 'object') {
                        arr[item] = deepClone(obj[item])
                    } else {
                        arr[item] = obj[item]
                    }
                }
            }

            return arr
        }

Vue部分

  • 组件间传值的几种方式

1、props/$emit
2、$emit/$on
3、Vuex
4、$attrs/$listeners
5、provide/inject
6、$parrent/$children 、ref

  • $emit作用和原理

$emit也是采用了发布订阅者设计模式。

Vue.prototype.$emit = function (event: string): Component {
    const vm: Component = this
    let cbs = vm._events[event]
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      for (let i = 0, l = cbs.length; i < l; i++) {
        try {
          cbs[i].apply(vm, args)
        } catch (e) {
          handleError(e, vm, `event handler for "${event}"`)
        }
      }
    }
    return vm
  }
}

根据传入的事件名从当前实例的_events属性(即事件中心)中获取到该事件名所对应的回调函数cbs,然后再获取传入的附加参数args,由于cbs是一个数组,所以遍历该数组,拿到每一个回调函数,执行回调函数并将附加参数args传给该回调。

  • watch、computed区别和原理
  • router的原理
  • 双向绑定的原理
  • mixins的原理
  • vue如何监听到数组和对象的变化
  • template如何模板转化
  • 虚拟Dom的实现原理

http部分

  • DNS解析流程
  • htts的工作流程
    • 1、客户端发起HTTPS请求
    • 2、服务端的配置
    • 3、传送证书
    • 4、客户端解析证书
    • 5、传送加密信息
    • 6、服务端解密信息
    • 7、传输加密后的信息
    • 8、客户端解密信息 avatar

Client发起一个HTTPS(https:/demo.linianhui.dev)的请求,根据RFC2818的规定,Client知道需要连接Server的443(默认)端口。 Server把事先配置好的公钥证书(public key certificate)返回给客户端。 Client验证公钥证书:比如是否在有效期内,证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,直到验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息。 Client使用伪随机数生成器生成加密所使用的会话密钥,然后用证书的公钥加密这个会话密钥,发给Server。 Server使用自己的私钥(private key)解密这个消息,得到会话密钥。至此,Client和Server双方都持有了相同的会话密钥。 Server使用会话密钥加密“明文内容A”,发送给Client。 Client使用会话密钥解密响应的密文,得到“明文内容A”。 Client再次发起HTTPS的请求,使用会话密钥加密请求的“明文内容B”,然后Server使用会话密钥解密密文,得到“明文内容B”。

webpack

  • webpack打包流程
  • 常见的loader及其作用
  • 打包优化

算法

  • 斐波那契系数
  • LRU缓存机制
       function LinkNode(key, val) {
            this.key = key
            this.val = val
            this.next = null
            this.pre = null
        }
        
        function DoubleLinkList() {
            this.size = 0
            this.head = new LinkNode()
            this.last = new LinkNode()
            this.head.next = this.last
            this.last.pre = this.head
        }

        DoubleLinkList.prototype.addNode = function (node) {
            if (!(node instanceof LinkNode)) throw new Error('参数必须是LinkNode类型')

            // 尾插入法
            const preNode = this.last.pre
            const nextNode = this.last.pre.next
            node.pre = preNode
            node.next = nextNode
            preNode.next = node
            nextNode.pre = node
            this.size++
        }
        
        DoubleLinkList.prototype.deleteNode = function (node) {
            if (!(node instanceof LinkNode)) throw new Error('参数必须是LinkNode类型')

            const preNode = node.pre
            const nextNode = node.next
            preNode.next = nextNode
            nextNode.pre = preNode
            this.size--
        }

        DoubleLinkList.prototype.refreshList = function (node) {
            this.deleteNode(node)
            this.addNode(node)
        }


        function LRUCatch(limit) {
            this.maxSize = limit
            this.map = new Map()
            this.doubleLinkList = new DoubleLinkList()
        }

        LRUCatch.prototype.get = function (key) {
            if (this.map.get(key) === undefined) return -1

            const curNode = this.map.get(key)
            this.doubleLinkList.refreshList(curNode)

            return curNode.val
        }
        
        LRUCatch.prototype.put = function (key, val) {
            const newNode = new LinkNode(key, val)

            // 如果存在,更新val
            if (this.map.get(key)) {
                this.doubleLinkList.refreshList(this.map.get(key))

                return this.map.get(key).val = val
            }

            if (this.map.size < this.maxSize) { // 空间足够时
                this.doubleLinkList.addNode(newNode)
            } else {
                // 清理链表中的头部节点
                const firstNode = this.doubleLinkList.head.next
                this.doubleLinkList.deleteNode(firstNode)
                this.doubleLinkList.addNode(newNode)
                
                // 删除哈希表中的key
                this.map.delete(firstNode.key)
            }

            this.map.set(key, newNode)
        }

其他

  • vue-cli都做了什么
  • shell指令 (自动化打包)
  • npm源发布自己打包的工具
  • 网页打包优化
  • Vue优化

Object.freeze()可以使响应式数据变成普通数据