筆記:記錄一次面試

阿賀呀發表於2018-10-18

阿里面試

(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=“內容文字…”,文字中包含部分敏感詞彙,如[“86學潮”,“薩德”,“中共”]。 // 請編寫函式查詢到這些敏感詞,並將其字型顏色更改為紅色

    
        const change = (text, filters = ['86學潮', '薩德', '中共']) => {
        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', ...))

相關文章