# 阿里2021第一轮笔试

# 题一

一个半小时两道笔试题,题目如下:

# 1. 信号灯控制器

用 React 实现一个信号灯(交通灯)控制器,要求:

  1. 默认情况下, 1.1. 红灯亮20秒,并且最后5秒闪烁 1.2. 绿灯亮20秒,并且最后5秒闪烁 1.3. 黄灯亮10秒 1.4. 次序为 红-绿-黄-红-绿-黄
  2. 灯的个数、颜色、持续时间、闪烁时间、灯光次序都可配置,如: lights=[{color: '#fff', duration: 10000, twinkleDuration: 5000}, ... ]
import React from 'react'
import ReactDOM from 'react-dom

const trafficStyle = {
  display: 'flex'
}

const itemStyle = {
  width: '30px',
  height: '30px',
  border: '1px solid #000',
  borderRadius: '15px',
  margin: '5px',
  listStyleType: 'none'
}

class Traffic extends React.Component {
  timeId = null

  constructor(props) {
    super(props)
    this.state = {
      activeIndex: 0,
      isLighting: false,
      lights: [
        {color: '#ff0000', duration: 10000, twinkleDuration: 5000},
        {color: '#00ff00', duration: 10000, twinkleDuration: 5000},
        {color: '#ffff00', duration: 10000, twinkleDuration: 0},
      ]
    }
  }

  walk(index) {
    if (index < 0 || index > this.state.lights.length - 1) {
      index = 0
    }
    this.setState({
      activeIndex: index
    })

    const current = this.state.lights[index]
    if (current.duration > 0) {
      this.shine(current.duration, () => {
        if (current.twinkleDuration > 0) {
          this.twinkle(current.twinkleDuration, () => {
            this.walk(index + 1)
          })
        } else {
          this.walk(index + 1)
        }
      })
    }

    if (current.duration <= 0 && current.twinkleDuration > 0) {
      this.twinkle(current.twinkleDuration, () => {
        this.walk(index + 1)
      })
    }
  }

  shine(duration, cb) {
    this.setState({
      isLighting: true
    })

    this.timeId = setTimeout(() => {
      this.setState({
        isLighting: false
      })

      cb()
    }, duration)
  }

  twinkle(duration, cb) {
    this.timeId = setTimeout(() => {
      this.toggleLighting()

      if (duration - 500 > 0) {
        this.twinkle(duration - 500, cb)
      } else {
        cb()
      }
    }, duration > 500 ? 500 : duration)
  }

  toggleLighting() {
    this.setState({
      isLighting: !this.state.isLighting
    })
  }

  componentDidMount() {
    this.walk(this.state.activeIndex)
  }

  componentWillUnmount() {
    clearTimeout(this.timeId)
  }

  render() {
    const lightItems = this.state.lights.map((item, index) => {
      return <Light key={index} 
                color={item.color} 
                isActive={index === this.state.activeIndex && this.state.isLighting} />
    })

    return (
      <div className="traffic">
        <ul className="traffic-lights" style={trafficStyle}>{lightItems}</ul>
      </div>
    )
  }
}

function Light(props) {
  return (
    <li className='light' 
      style={{...itemStyle, backgroundColor: props.isActive ? props.color : '#fff'}}>
    </li>
  )
}

ReactDOM.render(<Traffic />, document.getElementById("root"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

# 2. 寻找特定 IP

IPV4 的 IP 地址是32位的二进制数,为增强可读性,通常我们以8位为1组进行分割, 用十进制来表示每一部分,并用点号连接,譬如 192.168.1.1。显然,存在这样的 IP 地址, 0到9十个数字各出现一次。具备这样特征的 IP 地址里,表示成二进制数时,二进制数左右对称 (也就是“回文”,表示成32位二进制不省略0)的情况有几种,分别是哪些?要求性能尽可能高

# 题二

# 1.计算多个区间的交集

区间用长度为2的数字数组表示,如[2, 5]表示区间2到5(包括2和5); 区间不限定方向,如[5, 2]等同于[2, 5]; 实现getIntersection 函数 可接收多个区间,并返回所有区间的交集(用区间表示),如空集用null表示 示例: getIntersection([5, 2], [4, 9], [3, 6]); // [4, 5] getIntersection([1, 7], [8, 9]); // null

function getIntersection() {
  const nums = [...arguments];
  const n = nums.length;

  let ans = null;
  let i = 1;
  while (i < n) {
    let section1 = ans !== null ? ans : nums[i-1].sort();
    let section2 = nums[i].sort();

    // 左边取大,右边取小
    let left = Math.max(section1[0], section2[0]);
    let right = Math.min(section1[1], section2[1]);	
    if (left <= right) {
      ans = [left, right];
    } else {
      return null;
    }

    i++;
  }

  return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 2. 计算dom元素的个数

DOM的体积过大会影响页面性能,假如你想在用户关闭页面时统计(计算并反馈给服务器) 当前页面中元素节点的数量总和、元素节点的最大嵌套深度以及最大子元素个数,请用 JS 配合 原生DOM API实现该需求(不用考虑陈旧浏览器以及在现代浏览器中的兼容性,可以使用任意 浏览器的最新特性;不用考虑shadow DOM)。比如在如下页面中运行后:

<html>
  <head></head>
  <body>
    <div>
      <span>f</span>
      <span>o</span>
      <span>o</span>
    </div>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10

会输出:

{
  totalElementsCount: 7,
  maxDOMTreeDepth: 4,
  maxChildrenCount: 3
}
1
2
3
4
5

# 3.实现一个Events模块

请使用原生代码实现一个Events模块,可以实现自定义事件的订阅、触发、移除功能

const fn1 = (... args)=>console.log('I want sleep1', ... args)
const fn2 = (... args)=>console.log('I want sleep2', ... args)
const event = new Events();
event.on('sleep', fn1, 1, 2, 3);
event.on('sleep', fn2, 1, 2, 3);
event.fire('sleep', 4, 5, 6);
// I want sleep1 1 2 3 4 5 6
// I want sleep2 1 2 3 4 5 6
event.off('sleep', fn1);
event.once('sleep', ()=>console.log('I want sleep));
event.fire('sleep');
1
2
3
4
5
6
7
8
9
10
11