其他分享
首页 > 其他分享> > 2022秋招前端面试题(四)(附答案)

2022秋招前端面试题(四)(附答案)

作者:互联网

代码输出结果

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')
复制代码

输出结果如下:

async1 start
async2
start
async1 end
复制代码

代码的执行过程如下:

  1. 首先执行函数中的同步代码async1 start,之后遇到了await,它会阻塞async1后面代码的执行,因此会先去执行async2中的同步代码async2,然后跳出async1
  2. 跳出async1函数后,执行同步代码start
  3. 在一轮宏任务全部执行完之后,再来执行await后面的内容async1 end

这里可以理解为await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中。

实现数组原型方法

forEach

语法:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

参数:

callback:为数组中每个元素执行的函数,该函数接受1-3个参数currentValue: 数组中正在处理的当前元素index(可选): 数组中正在处理的当前元素的索引array(可选): forEach() 方法正在操作的数组 thisArg(可选): 当执行回调函数 callback 时,用作 this 的值。

返回值:undefined

Array.prototype.forEach1 = function(callback, thisArg) {
    if(this == null) {
        throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {
        throw new TypeError(callback + 'is not a function');
    }
    // 创建一个新的 Object 对象。该对象将会包裹(wrapper)传入的参数 this(当前数组)。
    const O = Object(this);
    // O.length >>> 0 无符号右移 0 位
    // 意义:为了保证转换后的值为正整数。
    // 其实底层做了 2 层转换,第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型
    const len = O.length >>> 0;
    let k = 0;
    while(k < len) {
        if(k in O) {
            callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
}
复制代码

map

语法: arr.map(callback(currentValue [, index [, array]])[, thisArg])

参数:与 forEach() 方法一样

返回值:一个由原数组每个元素执行回调函数的结果组成的新数组。

Array.prototype.map1 = function(callback, thisArg) {
    if(this == null) {
        throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {
        throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let newArr = [];  // 返回的新数组
    let k = 0;
    while(k < len) {
        if(k in O) {
            newArr[k] = callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
    return newArr;
}
复制代码

filter

语法:arr.filter(callback(element [, index [, array]])[, thisArg])

参数:

callback: 用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:element、index、array,参数的意义与 forEach 一样。

thisArg(可选): 执行 callback 时,用于 this 的值。

返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

Array.prototype.filter1 = function(callback, thisArg) {
    if(this == null) {
        throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {
        throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let newArr = [];  // 返回的新数组
    let k = 0;
    while(k < len) {
        if(k in O) {
            if(callback.call(thisArg, O[k], k, O)) {
                newArr.push(O[k]);
            }
        }
        k++;
    }
    return newArr;
}
复制代码

some

语法:arr.some(callback(element [, index [, array]])[, thisArg])

参数:

callback: 用来测试数组的每个元素的函数。接受以下三个参数:element、index、array,参数的意义与 forEach 一样。

thisArg(可选): 执行 callback 时,用于 this 的值。
返回值:数组中有至少一个元素通过回调函数的测试就会返回 true;所有元素都没有通过回调函数的测试返回值才会为 false

Array.prototype.some1 = function(callback, thisArg) {
    if(this == null) {
        throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {
        throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this); 
    const len = O.length >>> 0;
    let k = 0;
    while(k < len) {
        if(k in O) {
            if(callback.call(thisArg, O[k], k, O)) {
                return true
            }
        }
        k++;
    }
    return false;
}
复制代码

reduce

语法:arr.reduce(callback(preVal, curVal[, curIndex [, array]])[, initialValue])

参数:

callback: 一个 “reducer” 函数,包含四个参数:

preVal:上一次调用 callback 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]

curVal:数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]

curIndex(可选):数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。

array(可选):用于遍历的数组。
initialValue(可选): 作为第一次调用 callback 函数时参数 preVal 的值。若指定了初始值 initialValue,则 curVal 则将使用数组第一个元素;否则 preVal 将使用数组第一个元素,而 curVal 将使用数组第二个元素。
返回值:使用 “reducer” 回调函数遍历整个数组后的结果。

Array.prototype.reduce1 = function(callback, initialValue) {
    if(this == null) {
        throw new TypeError('this is null or not defined');
    }
    if(typeof callback !== "function") {
        throw new TypeError(callback + 'is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    let accumulator = initialValue;
    // 如果第二个参数为undefined的情况下,则数组的第一个有效值(非empty)作为累加器的初始值
    if(accumulator === undefined) {
        while(k < len && !(k in O)) {
            k++;
        }
        // 如果超出数组界限还没有找到累加器的初始值,则TypeError
        if(k >= len) {
            throw new TypeError('Reduce of empty array with no initial value');
        }
        accumulator = O[k++];
    }
    while(k < len) {
        if(k in O) {
            accumulator = callback(accumulator, O[k], k, O);
        }
        k++;
    }
    return accumulator;
}
复制代码

z-index属性在什么情况下会失效

通常 z-index 的使用是在有两个重叠的标签,在一定的情况下控制其中一个在另一个的上方或者下方出现。z-index值越大就越是在上层。z-index元素的position属性需要是relative,absolute或是fixed。

z-index属性在下列情况下会失效:

写代码:实现函数能够深度克隆基本类型

浅克隆:

function shallowClone(obj) {
  let cloneObj = {};

  for (let i in obj) {
    cloneObj[i] = obj[i];
  }

  return cloneObj;
}
复制代码

深克隆:

function deepCopy(obj) {
  if (typeof obj === 'object') {
    var result = obj.constructor === Array ? [] : {};

    for (var i in obj) {
      result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  } else {
    var result = obj;
  }

  return result;
}
复制代码

px、em、rem的区别及使用场景

三者的区别:

使用场景:

什么情况会阻塞渲染?

首先渲染的前提是生成渲染树,所以 HTML 和 CSS 肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。然后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。

当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置。对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染。

什么是闭包,闭包的作用是什么

当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数。
闭包作用:
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
复制代码

Virtual Dom 的优势在哪里?

Virtual Dom 的优势」其实这道题目面试官更想听到的答案不是上来就说「直接操作/频繁操作 DOM 的性能差」,如果 DOM 操作的性能如此不堪,那么 jQuery 也不至于活到今天。所以面试官更想听到 VDOM 想解决的问题以及为什么频繁的 DOM 操作会性能差。

首先我们需要知道:

DOM 引擎、JS 引擎 相互独立,但又工作在同一线程(主线程) JS 代码调用 DOM API 必须 挂起 JS 引擎、转换传入参数数据、激活 DOM 引擎,DOM 重绘后再转换可能有的返回值,最后激活 JS 引擎并继续执行若有频繁的 DOM API 调用,且浏览器厂商不做“批量处理”优化, 引擎间切换的单位代价将迅速积累若其中有强制重绘的 DOM API 调用,重新计算布局、重新绘制图像会引起更大的性能消耗。

其次是 VDOM 和真实 DOM 的区别和优化:

  1. 虚拟 DOM 不会立马进行排版与重绘操作
  2. 虚拟 DOM 进行频繁修改,然后一次性比较并修改真实 DOM 中需要改的部分,最后在真实 DOM 中进行排版与重绘,减少过多DOM节点排版与重绘损耗
  3. 虚拟 DOM 有效降低大面积真实 DOM 的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部

一般如何产生闭包

如何防御 XSS 攻击?

可以看到XSS危害如此之大, 那么在开发网站时就要做好防御措施,具体措施如下:

  1. CSP 指的是内容安全策略,它的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。
  2. 通常有两种方式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式

僵尸进程和孤儿进程是什么?

数组去重

ES5 实现:

function unique(arr) {
    var res = arr.filter(function(item, index, array) {
        return array.indexOf(item) === index
    })
    return res
}
复制代码

ES6 实现:

var unique = arr => [...new Set(arr)]
复制代码

函数防抖

触发高频事件 N 秒后只会执行一次,如果 N 秒内事件再次触发,则会重新计时。

简单版:函数内部支持使用 this 和 event 对象;

function debounce(func, wait) {
    var timeout;
    return function () {
        var context = this;
        var args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}
复制代码

使用:

var node = document.getElementById('layout')
function getUserAction(e) {
    console.log(this, e)  // 分别打印:node 这个节点 和 MouseEvent
    node.innerHTML = count++;
};
node.onmousemove = debounce(getUserAction, 1000)
复制代码

最终版:除了支持 this 和 event 外,还支持以下功能:

function debounce(func, wait, immediate) {
    var timeout, result;

    var debounced = function () {
        var context = this;
        var args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        } else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    };

    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    };

    return debounced;
}
复制代码

使用:

var setUseAction = debounce(getUserAction, 10000, true);
// 使用防抖
node.onmousemove = setUseAction

// 取消防抖
setUseAction.cancel()
复制代码

死锁产生的原因? 如果解决死锁的问题?

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。

系统中的资源可以分为两类:

产生死锁的原因:

(1)竞争资源

(2)进程间推进顺序非法

若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁

产生死锁的必要条件:

预防死锁的方法:

柯里化

题目描述:柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。

实现代码如下:

function currying(fn, ...args) {
  const length = fn.length;
  let allArgs = [...args];
  const res = (...newArgs) => {
    allArgs = [...allArgs, ...newArgs];
    if (allArgs.length === length) {
      return fn(...allArgs);
    } else {
      return res;
    }
  };
  return res;
}

// 用法如下:
// const add = (a, b, c) => a + b + c;
// const a = currying(add, 1);
// console.log(a(2,3))
复制代码

浏览器是如何对 HTML5 的离线储存资源进行管理和加载?

标签:function,面试题,数组,DOM,callback,2022,秋招,进程,资源
来源: https://www.cnblogs.com/helloworld1024aa/p/16558395.html