1. 程式人生 > >記錄一次阿里面試

記錄一次阿里面試

阿里面試

(2018-09-18)人工智慧實驗室

  • 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<link href="print.css" rel="stylesheet" media="print">
        • 提供媒體查詢<link href="other.css" rel="stylesheet" media="(min-width: 30em) and (orientation: landscape)">
      • 改變JS阻塞模式

        defer & async 在inline-script皆無效

        • defer

          使用延遲載入的方式 <script src="app3.js" defer></script>

          defer屬性表示延遲執行引入的JS,即和HTML的構建是並行的

        • async

          表示非同步執行引入 <script src="app.js" async></script>

          如果已經載入好,就會開始執行,執行順序不一致

      • 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

這個階段執行由setTimeoutsetInterval發出的回撥

  • pending callbacks

    執行延期到下一個事件迴圈的I/O callbacks

  • idle, prepare

    內部使用

  • poll

    恢復新的I/O事件,執行幾乎除了timers、setImmediate、close callbacks

  • check

    setImmediate在這裡觸發

  • close callbacks

    一些關閉事件的回撥(socket.on(‘close’, …))