# 前端面试题及答案汇总(七) 71-80

# 第 71 题: 实现一个字符串匹配算法,从长度为 n 的字符串 S 中,查找是否存在字符串 T,T 的长度是 m,若存在返回所在位置。

var findStr = function(S, T) {
  if (T.length > S.length) {
    return -1;
  }
  if(T === '' && S !== '') {
    return -1;
  }
  S = String(S);
  T = String(T);
  let m = S.length;
  let n = T.length;

  let p = 0;
  while(p + n <= m) {
    let temp = S.slice(p, p + n);
    if(temp === T) {
      return p;
    }
    p++;
  }
  return -1;
}
findStr('asdfqwer', '')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 第 72 题: 为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因。

  1. for 循环当然是最简单的,因为它没有任何额外的函数调用栈和上下文;
  2. forEach 其次,因为它其实比我们想象得要复杂一些,它的函数签名实际上是array.forEach(function(currentValue, index, arr), thisValue)它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;
  3. map 最慢,因为它的返回值是一个等长的全新的数组,数组创建和赋值产生的性能开销很大。

# 第 73 题: 介绍下 BFC、IFC、GFC 和 FFC

BFC(Block formatting contexts):块级格式上下文 页面上的一个隔离的渲染区域,那么他是如何产生的呢?可以触发BFC的元素有float、position、overflow、display:table-cell/ inline-block/table-caption ;BFC有什么作用呢?比如说实现多栏布局’

IFC(Inline formatting contexts):内联格式上下文 IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC中的line box一般左右都贴紧整个IFC,但是会因为float元素而扰乱。float元素会位于IFC与与line box之间,使得line box宽度缩短。 同个ifc下的多个line box高度会不同 IFC中时不可能有块级元素的,当插入块级元素时(如p中插入div)会产生两个匿名块与div分隔开,即产生两个IFC,每个IFC对外表现为块级元素,与div垂直排列。 那么IFC一般有什么用呢? 水平居中:当一个块要在环境中水平居中时,设置其为inline-block则会在外层产生IFC,通过text-align则可以使其水平居中。 垂直居中:创建一个IFC,用其中一个元素撑开父元素的高度,然后设置其vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。

GFC(GrideLayout formatting contexts):网格布局格式化上下文 当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。那么GFC有什么用呢,和table又有什么区别呢?首先同样是一个二维的表格,但GridLayout会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。

FFC(Flex formatting contexts):自适应格式上下文 display值为flex或者inline-flex的元素将会生成自适应容器(flex container),可惜这个牛逼的属性只有谷歌和火狐支持,不过在移动端也足够了,至少safari和chrome还是OK的,毕竟这俩在移动端才是王道。Flex Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素。伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说,Flexbox 定义了伸缩容器内伸缩项目该如何布局。

# 第 74 题: 使用 JavaScript Proxy 实现简单的数据绑定

<body>
  hello,world
  <input type="text" id="model">
  <p id="word"></p>
</body>
<script>
  const model = document.getElementById("model")
  const word = document.getElementById("word")
  var obj= {};

  const newObj = new Proxy(obj, {
      get: function(target, key, receiver) {
        console.log(`getting ${key}!`);
        return Reflect.get(target, key, receiver);
      },
      set: function(target, key, value, receiver) {
        console.log('setting',target, key, value, receiver);
        if (key === "text") {
          model.value = value;
          word.innerHTML = value;
        }
        return Reflect.set(target, key, value, receiver);
      }
    });

  model.addEventListener("keyup",function(e){
    newObj.text = e.target.value
  })
</script>
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

# 第 75 题:数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少

# 第 76 题:输出以下代码运行结果

这题考察的是对象的键名的转换。

  • 对象的键名只能是字符串和 Symbol 类型。
  • 其他类型的键名会被转换成字符串类型。
  • 对象转字符串默认会调用 toString 方法。
// example 1
var a={}, b='123', c=123;
a[b]='b';

// c 的键名会被转换成字符串'123',这里会把 b 覆盖掉。
a[c]='c';  

// 输出 c
console.log(a[b]);
1
2
3
4
5
6
7
8
9
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');  

// b 是 Symbol 类型,不需要转换。
a[b]='b';

// c 是 Symbol 类型,不需要转换。任何一个 Symbol 类型的值都是不相等的,所以不会覆盖掉 b。
a[c]='c';

// 输出 b
console.log(a[b]);
1
2
3
4
5
6
7
8
9
10
11
// example 3
var a={}, b={key:'123'}, c={key:'456'};  

// b 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。
a[b]='b';

// c 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。这里会把 b 覆盖掉。
a[c]='c';  

// 输出 c
console.log(a[b]);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 第 77 题:算法题「旋转数组」

function rotate(arr, k) {
  if(!arr || arr.length === 0) {
    return null;
  }
  let len = arr.length;
  let num = k % len;
  return arr.splice(-num).concat(arr);
}
console.log(rotate([1,2,3,4,5,6],8))
1
2
3
4
5
6
7
8
9

# 第 78 题:Vue 的父组件和子组件生命周期钩子执行顺序是什么

# 第 79 题:input 搜索如何防抖,如何处理中文输入

# 第 80 题:介绍下 Promise.all 使用、原理实现及错误处理

  function promiseAll(promises) {
    return new Promise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject()
      }
      let count = 0;
      let len = promises.length;
      let res = new Array(len);
      for (let i = 0; i < len; i++) {
        (function (i) {
          Promise.resolve(promises[i]).then((value) => {
            count++;
            res[i] = value;
            if (count === len) {
              return resolve(res);
            }
          }, (reason) => {
            return reject(reason);
          })
        })(i)
      }
    })
  }
  var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
  promiseAll([p1, p2, p3]).then(function (value) {
    console.log(value)
    })
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