0122面试题——Promise、文件上传、柯里化、this、原始值类型、捕获/冒泡

207 阅读4分钟

javascript

模拟实现

  1. 实现一个简单的Promise,具备then 与 catch方法
// 如有精力,考虑一下如何实现链式调用
function myPromise(executor){
    this.status = 'pending'
    this.rejectedList = []
    this.fulfilledList = []
    this.result
    this.err
    function resolve (data) {
        this.result = data
        this.status = 'fulfilled'
        this.fulfilledList.forEach(f=>{f(result)})
    }
    function reject (data) {
        this.err = data
        this.status = 'rejected'
        this.rejectedList.forEach(f=>{f(err)})
    }
    this.then = function(callback) {
        if (this.status === 'pending') {
            this.fulfilledList.push(callback)
        } else {
            callback(this.result)
        }
        return this
        
        
    }
    this.catch = function (callback) {
        if (this.status === 'pending') {
            this.rejectedList.push(callback)
        } else {
            callback(this.err)
        }
        return this
    }

    executor(resolve.bind(this), reject.bind(this))
}

new myPromise((res,rej)=>{
    console.log(1)
    res('success')
}).then(console.log)

new myPromise((res,rej)=>{
    console.log(2)
    rej('error')
}).then(console.log)
.catch(err=>{
    console.log('----')
    console.log(err)
})

场景题

  1. 如何实现大文件的分片上传,断点续传,还有通常说的秒传? 实现一个大文件上传和断点续传
    • 分片上传
      • 解释:分片上传,即指将大文件切片成多个小文件块,然后同时上传
      • 思路:
        • 预设一个SIZE值,表示每个分片的最大大小,如SIZE=10*1024*1024
        • 根据SIZE,利用Blob.prototype.slice(File继承了Blob,同样拥有slice方法),将File对象进行切片,并记录每个切片的顺序,便于服务器合并分片
        • 服务器接收到所有切片后进行合并
    • 断点续传
      • 解释:在拥有类似暂停/恢复功能的上传场景中,要求从上一次的位置接着上传
      • 思路:
        • 在分片上传的基础上,增加上传中断的方法
        • 当用户点击暂停时,将所有正在上传的切片上传中断(已上传完成的则不用管)
        • 当用户点击恢复时,服务器返回哪些切片已经拥有,前端根据返回的切片列表进行过滤,继续上传剩余的切片
        • 服务器接收到所有切片后进行合并
    • 秒传
      • 解释:即不管多大的文件,都是瞬间上传完毕
      • 思路:计算待上传文件的Hash值,提交给后端查询是否上传过该文件,若是则直接返回上传成功(实际根本没上传)

理论

  1. 什么是函数柯里化?他有什么作用
  • 将一个接收多个参数的函数转换成接收首个参数的函数,并返回接收余下参数的函数
  • 作用
    • 惰性求值
    • 可以提前传递部分参数
    • 参数复用
  1. 实现一个柯里化函数
function currying(fn, ...args) {
    return fn.length > args.length ? (...args2) => currying(fn, ...args, ...args2) : fn(...args)
}

// ---测试代码---
function sum(a,b,c,d,e){
    console.log(a+b+c+d+e)
}
sum = currying(sum)
sum(1,2)(3)(4)(5) // 15
  1. this指向考察,阐明输出结果是什么
    • 例1
    var a = 1
    var obj = {
        fun:function(){
            console.log(a)
        },
        a:2
    }
    obj.fun() // 1, 直接打印a跟this无关,从当前作用域到全局作用域中查找a,对象不构成作用域,外部只有,a跟obj,因此打印外部的a,即1
    
    • 例2
    var a = 1
    function foo(){
        console.log(a) 
    }
    function bar(){
        var a = 2
        foo()
    }
    bar() // 1,js是静态作用域,从定义函数的位置到全局作用域查找变量a,即外部的a
    
  2. 原始值类型有哪些
    • Number
    • String
    • Boolean
    • Undefined
    • Null
    • BigInt
    • Symbol
  3. null是对象吗?为什么 typeof null === 'object'
    • 不是
    • js设计时,把引用类型的标识位设置成000,而null的值就是0,因此在判断的时候会把null判断成对象

事件相关

  1. 事件触发的几个阶段是什么
    • 捕获:由外而内,由父到子,网景提出
    • 事件目标处理函数
    • 冒泡:由内而外,由子到父,IE提出
  2. 为什么通常在冒泡阶段执行事件
    • 由子到父元素,即是由精确的元素到模糊的元素,可以更精确的处理事件,同时可以阻止事件冒泡到父元素
  3. 实现一个demo
    • html结构
    <ul id='list'>
        <li data-id>
            <!-- 很多子节点,但不包含li -->
        </li>
    </ul>
    
    • 要求点击li或者li中的任意子节点都能取到li上的data-id如何实现
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <ul id='list'>
        <li data-id="li" onclick="handle(event)">
            <p>11111111111111111111111111111111</p>
            <p>11111111111111111111111111111111</p>
            <p>11111111111111111111111111111111</p>
        </li>
      </ul>
      <script>
        function handle(e) {
          alert(e.currentTarget.dataset.id)
        }
      </script>
    </body>
    </html>