前端面试卷八

230 阅读1分钟

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

function find(S,T){
    if(!S || !T || S.length < T.length) return -1    //如果一方为空或者S长度小于T长度
    for(var i = 0 ;i < S.length; i++){
        if(S.slice(i,i+T.length) == T) return i
    }
    return -1
}

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

for 循环没有任何额外的函数调用栈和上下文;forEach 函数签名实际上是array.forEach(function(currentValue, index, arr), thisValue) 

它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,
这里可能拖慢性能;

介绍下 BFC、IFC、GFC 和 FFC

BFC(Block formatting contexts):

块级格式上下文页面上的一个隔离的渲染区域,那么他是如何产生的呢?可以触发 BFC 的元素有 float、position、overflow、display:table-cell/inline-block/table-caption/flex/inline-flex ;BFC 有什么作用呢?清除浮动,防止margin塌陷等。

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 定义了伸缩容器内伸缩项目该如

何布局。

使用 JavaScript Proxy 实现简单的数据绑定

  const model = document.getElementById("model")
  const word = document.getElementById("word")
  var obj= {};     //obj可以是对象,可以是数组,也可以是Proxy,生成代理对象也是对应的
  const newObj = new Proxy(obj, {    //拦截对象
      get: function(target, key, receiver) {
        return Reflect.get(target, key, receiver);    //等同于return target[key]
      },
      set: function(target, key, value, receiver) {
        if (key === "text") {    //拦截属性
          model.value = value;    //更新dom
          word.innerHTML = value;    //更新dom
        }
        return Reflect.set(target, key, value, receiver);    //等同于target[key] = value
      }
  });
  model.addEventListener("keyup",function(e){
    console.log(newObj.text)    //触发getter
    newObj.text = e.target.value    //触发setter
  })

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

数组可以直接根据索引取的对应的元素,所以不管取哪个位置的元素的时间复

杂度都是 O(1)得出结论:消耗时间几乎一致,差异可以忽略不计

输出以下代码运行结果

// example 1 
var a={}, b='123', c=123; 
a[b]='b';
a[c]='c'; 
console.log(a[b]); 
--------------------- 
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b'; 
a[c]='c'; 
console.log(a[b]); 
--------------------- 
// example 3 var a={}, b={key:'123'}, c={key:'456'}; 
a[b]='b';
a[c]='c'; 
console.log(a[b]);

1.对象的键名只能是字符串和 Symbol 类型。

2.其他类型的键名会被转换成字符串类型。

3.对象转字符串默认会调用 toString 方法。

// example 1

var a={}, b='123', c=123;a[b]='b';

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

// 输出 c

// example 2
var a={}, b=Symbol('123'), c=Symbol('123');

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

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

// 输出 b

// 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

算法题「旋转数组」

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:输入: [1, 2, 3, 4, 5, 6, 7] 和 k = 3 输出: [5, 6, 7, 1, 2, 3, 4] 解释: 向右旋转 1 步:

[7, 1, 2, 3, 4, 5, 6] 向右旋转 2 步: [6, 7, 1, 2, 3, 4, 5] 向右旋转 3 步: [5, 6, 7, 1, 2,

3, 4]

示例 2:输入: [-1, -100, 3, 99] 和 k = 2 输出: [3, 99, -1, -100] 解释: 向右旋转 1 步: [99,

-1, -100, 3] 向右旋转 2 步: [3, 99, -1, -100]

//思路:直接截取[5,6,7],然后放入数组的前面
function ratate(arr,k){    //k代表后移数量
    var array = [...arr]    //拷贝数组防止修改arr
    var len = array.length
    if(k==0) return array
    k = k%len    //如果k大于等于数组长度,比如k=7相当于k=0
    return array.slice(-k).concat(arraty.slice(0,len-k))   //[5,6,7].cancat([1,2,3,4])
}

Vue 的父组件和子组件生命周期钩子执行顺序是什么

1.父组件: beforeCreate -> created -> beforeMount

2.子组件: -> beforeCreate -> created -> beforeMount -> mounted

3.父组件: -> mounted

4.总结:从外到内,再从内到外

创建时:先挂载子组件到父组件上,再挂载父组件。
销毁时:父组件beforeDestory->子组件beforeDestory->子组件destoryed->父组件destoryed

input 搜索如何防抖,如何处理中文输入

<div>
    <input type="text" id="ipt">
</div> 
<script> 
    let ipt = document.getElementById('ipt'); 
    let dbFun = debounce() 
    ipt.addEventListener('keyup', function (e) { 
        dbFun(e.target.value); 
    })
    function debounce() { 
        let timer; 
        return function (value) { 
            if(timer) clearTimeout(timer); 
            timer = setTimeout(() => { console.log(value) }, 500); 
        } 
    } 
</script>

介绍下 Promise.all 使用、原理实现及错误处理

const p = Promise.all([p1, p2, p3]);

Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果

不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,

再进一步处理。(Promise.all 方法的参数可以不是数组,但必须具有 Iterator 接

口,且返回的每个成员都是 Promise 实例。)

    //定义all静态方法,传递一个参数[promise,1,2],数组中的每一项可以是promise,也可以是数字
    //只有每一项成功了,才会返回数组, 否则返回失败,而且promise是按顺序执行的  
    Promise.all = function (promises) {    
        var arr = []  //返回一个数组    
        var num = 0;    
        function precess(key, value) { //用于存放数据的一个方法      
            arr[key] = value      
            if (++num == callbacks.length) {   //代表所有的任务都执行完毕 promise是异步的    
                resolve(arr)     
            }    
        }    
        promises.forEach((item, i) => {     
             if (item.then && typeof item.then == 'function') {  //如果是一个promise        
                item.then(res => {      //执行,并把结果放入数组    
                    process(i, res)        
                }).catch(err){
                    reject(err)    //一旦出错就结束
                }      
             }      
             else {        
                process(i, item)      
             }    
        });    
    }