• 2017 阿里校招前端笔试题小结


    阿里前端笔试题,题目不多,难度也不大,我只记录了两道稍微有点难度的编程题。题目如下:
    1.JSON.stringify 的功能是,将一个 JavaScript 字面量对象转化为一个 JSON 格式的字符串。例如:

    const obj = {a:1, b:2}
    JSON.stringify(obj) // => '{"a":1,"b":2}'

    当要转化的对象有“环”存在时(子节点属性赋值了父节点的引用),为了避免死循环,JSON.stringify 会抛出异常,例如:

    const obj = {
      foo: {
        name: 'foo',
        bar: {
          name: 'bar'
          baz: {
            name: 'baz',
            aChild: null // 待会将指向obj.bar
          }
        }
      }
    }
    obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo形成环
    JSON.stringify(obj) // => TypeError: Converting circular personucture to JSON

    请完善以下“环”检查器函数 cycleDetector,当入参对象中有环时返回 true,否则返回 false。

    function cycleDetector(obj) {   
      // 请添加代码
    }

    解题思路:首先很容易想到要遍历这个对象,然后判断属性值是否为一个对象,如果是,则递归遍历这个属性值,但难点是该如何判断这个属性值是否为某个父节点的引用,要怎样拿到父节点的引用呢???
    其实我们可以先用一个数组cache用来保存对象类型的属性值,再用一个标记变量来标记是否有环,然后遍历时判断这个属性值类型是否为一个对象,如果是,则判断这个属性值是否在那个cache数组里,如果在,则表明有环,如果不在,则把这个属性值添加到数组里,再递归遍历这个属性值即可。

    具体代码如下:

    function cycleDetector(obj) {
        let hasCircle = false, //用一个变量去标记是否有环
            cache = []; //保存值为对象的属性值
        (function(obj) {
            Object.keys(obj).forEach(key => {
                const value = obj[key]
                if (typeof value == 'object' && value !== null) {
                    const index = cache.indexOf(value)
                    if (index !== -1) { //如果cache中存在这个value,则表示有环
                        hasCircle = true
                        return
                    } else {
                        cache.push(value)
                        arguments.callee(value)
                    }
                }
            })
        })(obj)
        return hasCircle
    }

    2.实现一个EventEmitter类,这个类包含以下方法:
    on(监听事件,该事件可以被触发多次)
    once(也是监听事件,但只能被触发一次)
    fire(触发指定的事件)
    off(移除指定事件的某个回调方法或者所有回调方法)

    class EventEmitter {
      /**请补充你的代码***/
    }
    const event = new EventEmitter()
    const drank = (person) => {
      console.log(person + '喝水')
    }
    event.on('drank', drank)
    event.on('eat', (person) => {
      console.log(person + '吃东西')
    })
    event.once('buy', (person) => {
      console.log(person + '买东西')
    })
    event.fire('drank', '我')   // 我喝水  
    event.fire('drank', '我')   // 我喝水  
    event.fire('eat', '其它人')   // 其它人吃东西
    event.fire('eat', '其它人')   // 其它人吃东西
    event.fire('buy', '其它人')  //其它人买东西
    event.fire('buy', '其它人')  //这里不会再次触发buy事件,因为once只能触发一次
    event.off('eat')  //移除eat事件
    event.fire('eat', '其它人')  //这里不会触发eat事件,因为已经移除了

    解题思路:这题其实就是实现发布-订阅模式了,难点在于怎样实现once事件,即只触发一次。其实也就是要实现两种类型的事件,我们可以用不同的对象去保存这两种类型的事件,然后在fire的时候,这两种事件都要被处理即可。
    具体代码如下:

    class EventEmitter {
        constructor() {
            this.queue = {} //可触发多次的事件
            this.onceQueue = {} //只能触发一次的事件
        }
        on(event, fn) {  //监听事件,可以触发多次
            if (!this.queue[event]) this.queue[event] = []
            this.queue[event].push(fn)
        }
        once(event, fn) {   //监听事件,只能触发一次
            if (!this.onceQueue[event]) {
                this.onceQueue[event] = {
                    fns: [],
                    hasFired: false
                }
            }
            this.onceQueue[event].fns.push(fn)
        }
        fire() {  //触发指定的事件
            const event = [].shift.call(arguments), //取得事件名称
                fns = this.queue[event],  //取得该事件里所有的回调函数(可以触发多次的事件)
                onceFns = this.onceQueue[event]  //取得该事件里所有的回调函数(只能触发一次的事件)
            if (fns && fns.length != 0) {
                let i = 0,fn
                while (fn = fns[i++]) {
                    fn.apply(this, arguments)
                }
            }
            if (onceFns && !onceFns.hasFired) {
                let i = 0,fn
                while (fn = onceFns.fns[i++]) {
                    fn.apply(this, arguments)
                }
                this.onceQueue[event].hasFired = true
            }
        }
        off(event, fn = null) { //可移除特定事件里的某个回调函数或者所有回调函数
            const fns = this.queue[event]
            if (!fns || fns.length == 0) return
            if (fn) { //移除该事件特定的回调
                this.queue[event] = fns.filter(item => {
                    return item !== fn
                })
            } else { //移除该事件所有的回调
                this.queue[event] = []
            }
        }
    }

    原文地址:https://lenshen.com/2017/08/25/alibaba/

  • 相关阅读:
    HDFS上传/下载数据的过程和原理
    使用JavaAPI获取文件信息
    jquery 最佳实践
    jQuery ajax
    两中复制表结构
    c#基础知识
    类和结构区别
    访问修饰符
    设计模式详细
    设计模式分类
  • 原文地址:https://www.cnblogs.com/lizhiwei8/p/9578574.html
Copyright © 2020-2023  润新知