笔记:记录一次面试

203 阅读6分钟

阿里面试

(2018-09)xxxxxx

  • hsf原理

  • nextTick、setImmediate

    setImmediate是在事件队列以后 nextTick在每次事件队列之前,在当前进程执行完以后立即执行

  • koa-middleware中调两次next()

    if (i <= index) return Promise.reject(new Error('next() called multiple times'))

  • node程序sleep三秒

      console.log('begin')
      sleep(3);
      function sleep(seconds) {
        const begin = new Date()
        const waitTill = new Date(begin.getTime() + seconds * 1000)
        while(waitTill > new Date()) {}
        console.log('how long ', new Date() - begin)
      }
    
  • 浏览器从输入网址到渲染的整个过程

    1. DNS 查询
    2. TCP 连接
    3. HTTP 请求即响应
    4. 服务器响应
    5. 客户端渲染 这五个步骤并不一定一次性顺序完成。如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染
      • 处理 HTML 标记并构建 DOM 树。

      • 处理 CSS 标记并构建 CSSOM 树。

      • 将 DOM 与 CSSOM 合并成一个渲染树。

      • 根据渲染树来布局,以计算每个节点的几何信息。

      • 将各个节点绘制到屏幕上。 1.默认情况下,CSS被视为阻塞渲染饿资源,这意味着浏览器将不会渲染任何已处理的内容,直至CSS-DOM构建完毕 2.JS不仅可以读取和修改DOM属性,还可以读取和修改CSS-DOM属性

        存在阻塞的CSS资源时,浏览器会延迟JS的执行和DOM构建,并且: - 当浏览器遇到一个script标签时,DOM将构建暂停,直至脚本执行结束 - CSS-DOM构建时,JS将暂停执行,直至CSS-DOM就绪

        实际使用时,应遵循以下两个原则 1.CSS优先:引入顺序上,CSS资源先于JS资源 2.JS应尽量少影响DOM的构建

      • 改变CSS的阻塞模式

        • 设置media
        • 提供媒体查询
      • 改变JS阻塞模式 defer & async 在inline-script皆无效

        • defer 使用延迟加载的方式 defer属性表示延迟执行引入的JS,即和HTML的构建是并行的
        • async 表示异步执行引入 如果已经加载好,就会开始执行,执行顺序不一致
      • document.createElement 默认加载的JS是异步的document.createElement("script").async --> (default: true) 创建link标签已知在chrome中不会阻塞渲染

  • 如何在node中捕获异常不让程序崩溃

    • 同步方法 使用return

      
          const divideSync = (x, y) => {
            // if error condition?
            if (y === 0) {
              // "throw" the error safely by returning it
              return new Error("Can't divide by zero")
            } else {
              // no error occured, continue on
              return x / y
            }
          }
          // Divide 4/2
          let result = divideSync(4, 2)
          // did an error occur?
          if (result instanceof Error) {
            // handle the error safely
            console.log('4/2=err', result)
          } else {
            // no error occured, continue on
            console.log('4/2=' + result)
          }
          
          // Divide 4/0
          result = divideSync(4, 0)
          // did an error occur?
          if (result instanceof Error) {
            // handle the error safely
            console.log('4/0=err', result)
          } else {
            // no error occured, continue on
            console.log('4/0=' + result)
          }
      
    • 基本的回调函数 return 出error。error为null时程序正常

          const divide =  (x, y, next) => {
                // if error condition?
                if (y === 0) {
                  // "throw" the error safely by calling the completion callback
                  // with the first argument being the error
                  next(new Error("Can't divide by zero"))
                } else {
                  // no error occured, continue on
                  next(null, x / y)
                }
              }
              
              divide(4, 2,  (err, result) => {
                // did an error occur?
                if (err) {
                  // handle the error safely
                  console.log('4/2=err', err)
                } else {
                  // no error occured, continue on
                  console.log('4/2=' + result)
                }
              })
              
              divide(4, 0,  (err, result) => {
                // did an error occur?
                if (err) {
                  // handle the error safely
                  console.log('4/0=err', err)
                } else {
                  // no error occured, continue on
                  console.log('4/0=' + result)
                }
              })
      
    • 基于事件流

      手动触发一个error事件

       // Definite our Divider Event Emitter
          const events = require('events')
          
          const Divider = function() {
            events.EventEmitter.call(this)
          }
          require('util').inherits(Divider, events.EventEmitter)
          
          // Add the divide 
          Divider.prototype.divide = function(x, y) {
            // if error condition?
            if (y === 0) {
              // "throw" the error safely by emitting it
              const err = new Error("Can't divide by zero")
              this.emit('error', err)
            } else {
              // no error occured, continue on
              this.emit('divided', x, y, x / y)
            }
          
            // Chain
            return this;
          }
          
          // Create our divider and listen for errors
          const divider = new Divider()
          divider.on('error', (err) => {
            // handle the error safely
            console.log(err)
          })
          divider.on('divided', (x, y, result) => {
            console.log(x + '/' + y + '=' + result)
          })
          
          // Divide
          divider.divide(4, 2).divide(4, 0)
      
    • 安全地捕获异常

      • domain包含代码块
      var d = require('domain').create()
            d.on('error', function(err){
                // handle the error safely
                console.log(err)
            })
            
            // catch the uncaught errors in this asynchronous or synchronous code block
            d.run(function(){
                // the asynchronous or synchronous code that we want to catch thrown errors on
                var err = new Error('example')
                throw err
            })
      
      
      • 如果知道可能发生错误的地方,并且无法使用domain时(*异步无法被try catch捕获)
            // catch the uncaught errors in this synchronous code block
            // try catch statements only work on synchronous code
            try {
                // the synchronous code that we want to catch thrown errors on
                var err = new Error('example')
                throw err
            } catch (err) {
                // handle the error safely
                console.log(err)
            }
      
      • 使用try包含你的异步回调函数
      var divide = function (x, y, next) {
                // if error condition?
                if (y === 0) {
                    // "throw" the error safely by calling the completion callback
                    // with the first argument being the error
                    next(new Error("Can't divide by zero"))
                } else {
                    // no error occured, continue on
                    next(null, x / y)
                }
            }
            
            var continueElsewhere = function (err, result) {
                throw new Error('elsewhere has failed')
            }
            
            try {
                divide(4, 2, continueElsewhere)
                // ^ the execution of divide, and the execution of 
                //   continueElsewhere will be inside the try statement
            } catch (err) {
                console.log(err.stack)
                // ^ will output the "unexpected" result of: elsewhere has failed
            }
      
      
    • 使用process.on('uncaughtException')捕获

      
          // catch the uncaught errors that weren't wrapped in a domain or try catch statement
          // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
          process.on('uncaughtException', function(err) {
              // handle the error safely
              console.log(err)
          })
          
          // the asynchronous or synchronous code that emits the otherwise uncaught error
          var err = new Error('example')
          throw err
      
  • try catch是否能捕获异步的异常在node中 不能,需要进行回调处理

  • websocket的鉴权

    • 使用一个ticket

    • 在connection时检查remoteAddress的cookie信息

    • 如果不通过,则terminate

    • 把ticket主动发送给客户端,让其保存

    • 每次onMessage检查ticket、时效、token

      源码服务器端JavaScript

          import url from 'url'
          import WebSocket from 'ws'
          import debug from 'debug'
          import moment from 'moment'
          import { Ticket } from '../models'
          
          const debugInfo = debug('server:global')
          
          // server 可以是 http server实例
          const wss = new WebSocket.Server({ server })
          wss.on('connection', async(ws) => {
            const location = url.parse(ws.upgradeReq.url, true)
            const cookie = ws.upgradeReq.cookie
            debugInfo('ws request from: ', location, 'cookies:', cookie)
          
            // issue & send ticket to the peer
            if (!checkIdentity(ws)) {
              terminate(ws)
            } else {
              const ticket = issueTicket(ws)
              await ticket.save()
              ws.send(ticket.pojo())
          
              ws.on('message', (message) => {
                if (!checkTicket(ws, message)) {
                  terminate(ws)
                }
                debugInfo('received: %s', message)
              })
            }
          })
          
          function issueTicket(ws) {
            const uniqueId = ws.upgradeReq.connection.remoteAddress
            return new Ticket(uniqueId)
          }
          
          async function checkTicket(ws, message) {
            const uniqueId = ws.upgradeReq.connection.remoteAddress
            const record = await Ticket.get(uniqueId)
            const token = message.token
            return record
              && record.expires
              && record.token
              && record.token === token
              && moment(record.expires) >= moment()
          }
          
          // 身份检查,可填入具体检查逻辑
          function checkIdentity(ws) {
            return true
          }
          
          function terminate(ws) {
            ws.send('BYE!')
            ws.close()
          }
      ticket
          import shortid from 'shortid'
          import { utils } from '../components'
          import { db } from './database'
           
          export default class Ticket {
            constructor(uniqueId, expiresMinutes = 30) {
              const now = new Date()
              this.unique_id = uniqueId
              this.token = Ticket.generateToken(uniqueId, now)
              this.created = now
              this.expires = moment(now).add(expiresMinutes, 'minute')
            }
           
            pojo() {
              return {
                ...this
              }
            }
           
            async save() {
              return await db.from('tickets').insert(this.pojo()).returning('id')
            }
           
            static async get(uniqueId) {
              const result = await db
                .from('tickets')
                .select('id', 'unique_id', 'token', 'expires', 'created')
                .where('unique_id', uniqueId)
              const tickets = JSON.parse(JSON.stringify(result[0]))
              return tickets
            }
           
            static generateToken(uniqueId, now) {
              const part1 = uniqueId
              const part2 = now.getTime().toString()
              const part3 = shortid.generate()
              return utils.sha1(`${part1}:${part2}:${part3}`)
            }
          }
      utils
          import crypto from 'crypto'
           
          export default {
            sha1(str) {
              const shaAlog = crypto.createHash('sha1')
              shaAlog.update(str)
              return shaAlog.digest('hex')
            },
          }
      
      
  • 会话劫持

  • 敏捷开发

  • spring boot注解原理

  • tree shaking

  • 死锁(哲学家问题)

  • MB字节

  • handle Promise Error

  • Bridge

  • redux中间件

  • https加密步骤

  • 开发组件库怎么减少代码的量

  • PureComponent

  • 笔试题

    // 1. 写一个函数实现数字格式化输出,比如输入 00999999999,输出为 999,999,999

    
        const filter = (str, replace = ',') => {
       	const arr = str.split('');
     	return arr.reduce((init, item, index) => {
        	if (index !== 0 && index%3 === 0) {
            	init += replace;
            }
          	if (!!parseInt(item, 10)) {
            	init += item;
            }
            return init;
        }, '');
          
     }
    

    // 2. 编写一个函数 parse queryString,它的用途是把 URL 参数解析为一个key, value对象

    
        const convert = (url, replace = '=') => {
        	// localhost:8081/api/test?name=asdasd&b=adasd
          const index = url.indexOf('?')
          const shortUrl = url.slice(index + 1)
          const arr = shortUrl.split('&')
          return arr.reduce((init, item) => {
            const keys = item.split(replace);
          	init[keys[0]] = keys[1]
            return init;
          }, {})
        }
    

    // 3. 有一段文本它的内容是:var text=“内容文字…”,文本中包含部分敏感词汇,如[“萨德”,“中共”]。 // 请编写函数查找到这些敏感词,并将其字体颜色更改为红色

    
        const change = (text, filters = [ '萨德', '中共']) => {
        const regStr = filters.reduce((init, item) => {
            init +=`(${item})|`
          }, '');
        const reg = new Reg(`/${regStr.slice(0, regStr.length - 1)}/g`);
    	return text.replace(reg, `<span style="color: red">$&1<span>`);
    }
    

nodeJs事件机制

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘
  • 技术团队的结构、他们使用了哪些技术、用到了哪些工具,遇到了哪些挑战,以及他们的系统架构
  • 你们最近遇到了什么技术挑战?
  • 你在这个公司最满意的一点是什么?
  • 你们的团队是怎样的结构,工作是怎么分配的?
  • timers 这个阶段执行由setTimeout和setInterval发出的回调
  • pending callbacks 执行延期到下一个事件循环的I/O callbacks
  • idle, prepare 内部使用
  • poll 恢复新的I/O事件,执行几乎除了timers、setImmediate、close callbacks
  • check setImmediate在这里触发
  • close callbacks 一些关闭事件的回调(socket.on('close', ...))