有赞面试记录一 - pod4g/tool GitHub Wiki

这是有赞的第二面(第一面主要问了很多项目问题,不方便写出来),全部是coding题,在40分钟之内全部做了出来,做个记录

// =====================================================
// 欢迎参加有赞前端 Coding 面试
// =====================================================
// 界面介绍:
//   上方设置按钮可以切换语言、字体大小、主题
//   右侧控制台可以显示代码执行结果,可用于编码过程中的 DEBUG
// =====================================================
// Coding 须知:
//   本次 Coding 时间限制为 45 分钟,共 3 道题目
//   题目难度自上向下递增,请量力答题
// =====================================================

/**
 * 解析url中的queryString
 * 
 * 输入:https://www.youzan.com?name=coder&age=20&callback=https%3A%2F%2Fyouzan.com%3Fname%3Dtest&list[]=a&json=%7B%22str%22%3A%22abc%22,%22num%22%3A123%7D
 * 输出:
 * {
 *  name: coder,
 *  age: 20,
 *  callback: https%3A%2F%2Fyouzan.com%3Fname%3Dtest,
 *  list: [a],
 *  json: {
 *      str: 'abc',
 *      num: 123
 *  }
 * }
 */

function safyParse(json) {

    let obj = null
    try {
      obj = JSON.parse(json)
    } catch(e) {}

    return obj
}

const url = 'https://www.youzan.com?name=coder&age=20&callback=https%3A%2F%2Fyouzan.com%3Fname%3Dtest&list[]=a&json=%7B%22str%22%3A%22abc%22,%22num%22%3A123%7D';

function parseQuery(url) {

  url = url.toString()

  if (typeof url !== 'string') { throw new TypeError('arguments should be a string type') }

  const [ host, queryString ] = url.split('?')

  const querys = queryString.split('&')

  const map = Object.create(null)
  const arrayReg = /\[\]$/
  
  for(let query of querys) {
    let [k, v] = query.split('=')
    if (arrayReg.test(k)) {
        const list = []
        list.push(k)
        k = k.replace(arrayReg, '')
        v = list
    } else if (/^\{\[/.test(decodeURIComponent(v))) {
        v = safyParse(v)
    }
    
    map[k] = v
  }

  return map
}

console.log(parseQuery(url));



/**
 * 统计一个复杂对象中的英文字符 a-z以及A-Z个数,
 * 输入:
 * {
 *  name: 'code',
 *  obj: {
 *      name: 'CODE',
 *      age: [12, 45, 20],
 *      info: {
 *          nick: 'haha!'
 *      }
 *  },
 *  hooby: ['a', 'B']
 * }
 * 输出:14
 */
const obj = {
    name: 'code',
    obj: {
        name: 'CODE',
        age: [12, 45, 20],
    info: {
        nick: 'haha!'
    }
    },
    hooby: ['a', 'B']
}

function getType(arg) {
  return Object.prototype.toString.call(arg).slice(8, -1).toLowerCase()
}

console.log(getType('abc'))

function calcCountOfObject(obj) {
  if (obj == null && getType(obj) !== 'object') {
    throw new TypeError('argument should be a object type')
  }
  let counter = 0
  const reg = /[a-zA-Z]/
  Object.keys(obj).forEach(key => {
    const value = obj[key]
    const type = getType(value)
    if (type === 'string') {
        for (let i = 0; i < value.length; i++) {
          if(reg.test(value[i])) {
              counter++
           }
        }
    } else if (type === 'object' || type === 'array') {
        counter += calcCountOfObject(value)
    }
  })
  return counter
}

console.log(calcCountOfObject(obj));
/**
 * 3. 请实现一个事件模型(EventEmitter)
 * 实现简单的 on、off、once等功能
 */

class EventEmitter {
  constructor() {
    // 数据结构
    // { eventName1: { queue: [fn1, fn2, fnn], called: false }, eventName2:  }
    this.map = {}
  }
  __inqueue(eventName) {
    const { queue } = this.map[eventName]
    if (queue && queue.length > 0) {
        if (queue.inclues(callback)) {
            queue.push(callback)
        }
    } else {
        // 如果没有,则实例化一个队列
        map[eventName] = [callback]
    }
  }

  on(eventName, callback) {
    this.__inqueue(eventName)
  }

  off(eventName) {
    // 简单粗暴,直接清空回调队列
    map[eventName] = []
  }

  once(eventName, callback) {
    const { called } = map[eventName]
    if (called) { return }
    this.__inqueue()
  }
}