2022年面试总结

212 阅读12分钟

2022.4.23面试

二分查找+排序(二分查找的前提就是有序数组)

  if(arr.length == 0){
    return -1
  }
  let min = 0;
  let max = arr.length-1;

  while( min <= max ){
    let mid = Math.floor((min + max)/2);
    if(arr[mid] == target){
      return mid
    }
    else if(target>arr[mid]){
      min = mid+1
    }
    else if(target<arr[mid]){
      max = mid-1
    }

  }
  return -1;
}
console.log(binarySearch([0,2,3,4,6,9,12,16,17,25,36,47,59],6))

作用域、this指向

var a = 1
function foo(){
  console.log(this.a,this);
};
var obj = {
    a: 2,
    foo,
    bar() {
      foo(); // 1
  }
}
var foo = obj.foo;
foo();    // 1
obj.foo(); // 2
obj.bar() // 1  函数或对象内调用函数,则指向window
-------------------------------------------------------------
var a = 'w' 
let obj = {
  a: 'o',
  print: function() {
    console.log(this.a);
  },
  print2: () => { 
    console.log(this.a);
  }
}
let p = obj.print;
let p2 = obj.print2;
obj.print(); //o
obj.print2();//w
p();//w
p2();//w

参考:juejin.cn/post/701947…

自加自减(a++、 ++a)

let a = 5;
console.log(a++) //5
  let b = a++ + a; // a++ 先计算后在自加,6+5 = 11
  let c = ++a + a;  // ++a 先自加后再计算 7+7 = 14
  console.log(b, c);

键名转换

  • 键名只能是为字符串或symbol;
  • 键名为对象时,全部转换为[object,Object]
var a={}, b='123', c=123;  
a[b]='b'; //此时 a = { 123: 'b' }

a[c]='c'; // c 的键名会被转换成字符串'123',这里会把 b 覆盖掉。
          // 此时 a = { 123: 'c' }
console.log(a[b]); // 输出c

参考 juejin.cn/post/696985…

sort 手写

//用法
return arr.sort((a,b)=>{
      return a-b
    })

手写参考 :juejin.cn/post/703554…

Native、Hybrid App、Web App区别

Native:安卓、ios原生开发

性能体验最好; 但开发和发布成本高

Web App:移动端的网站,常被称为H5应用,就是特定运行在移动端浏览器上的网站应用。

性能体验一般,主要取决于不同浏览器限制;但开发和发布成本低

Hybrid App:介于上面两者之间,混合体兼具“Native App良好交互体验的优势”和“Web App跨平台开发的优势”

开发和发布方便,效率介于上面两者之间;但学习范围广,需要原生配合 参考:juejin.cn/post/684490…

Event Loop 理解

  • 栈:stack,先进后出,是只能在某一端插入删除特殊线性表
  • 堆:heap ,利用完全二叉树维护的一组数据
  • 队列:queue,先进先出,只能在一段插入,另一端删除的线性表;进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列
  • 宏任务:MacroTask,包含全部script、setTimeout、setInteval、setImmediate、I/O、UI-Rendering
  • 微任务:MicroTask,包含Promise、Process.nextTick()(node运行环境才有)、MutationObserve这里
  1. 执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去检查微任务(microTask)队列是否为空,如果为空的话,就执行Task(宏任务),否则就一次性执行完所有微任务。
  2. *每次单个宏任务执行完毕后,检查微任务(microTask)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务(microTask)后,设置微任务(microTask)队列为null,然后再执行宏任务,如此循环。 ***注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行

原文参考:juejin.cn/post/684490…

2022.4.27笔试

水平垂直居中

  • 1、text-align,margin,line-height,
  • 2、absolute+transform(两种,50% 和 上下左右设置为0,margin:auto)
  • 3、flex,justify-content,align-items
  • 4、display: table-cell; text-align: center; vertical-align: middle;

普通函数、箭头函数区别

  1. 箭头函数:没有原型prototype、arguments,this指向上层作用域且不可改变this指向,不可作为构造函数
  2. 普通函数:this指向调用的对象

防抖节流

  • debounce(防抖,n秒内重复触发会重刷新时间n,直至n秒内无触发操作才会执行)
    • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
    • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
function debounce(fn,delay){
  var timer = null;
  return function(arg){
    if(timer){
      clearTimeout(timer)
    }
    timer = setTimeout(()=>{
      fn.call(this,arg)
    },delay)
  }
}
  • throttle(节流,n秒内只执行一次,多次触发无效)
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
// 立即执行,最后一次不满足不执行
function throttle(fn,delay){
  let timer = null;
  return function(arg){
    if(!timer){
      timer = setTimeout(()=>{
        fn.call(this,arg);
        timer = null;
      },delay)
    }
  }
}
// 延迟执行,最后一次一定执行
function throttle(fn,delay){
  let prev = null;
  return function(arg){
    let now = new Date();
    if(now-prev>=delay){
      setTimeout(()=>{
        fn.call(this,arg);
        prev = now;
      },delay)
    }
  }
}
// 立即执行,最后一次一定执行
function throttle(fn,delay){
  let prev = null;
  let timer = null;
  return function(arg){
    let now = new Date();
    if(now-prev>=delay){
      fn.call(this,arg);
      prev = now;
    }else{
      timer = setTimeout(()=>{
        fn.call(this,arg);
      },delay) 
    }
  }
}

单页面路由两种模式

  1. history模式(window.history)
  • history有两个api,pushState()和replaceState()改变url不会触发popState刷新页面。
  • 使用上面api之后重新刷新页面容易造成404
  1. hash模式 (location.hash)
  • hash改变会触发onhashchange事件,改变hash不会重新加载页面。
  • 兼容好,但不利于SEO

性能优化

image.png camo.githubusercontent.com/ef75a121551…

get、post区别

getpost
参数放在url传递,所以请求不安全,长度限制参数放在Request body传给服务端
可缓存,刷新页面无影响刷新页面重新提交post
请求产生一个数据包请求产生两个数据包
header和data一起发送先发送header,服务端响应100后在发送data
GET请求只能进行url编码(application/x-www-form-urlencoded)POST支持多种编码(application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多种编码。)
状态码描述
200请求成功
201请求成功并创建新的资源
202已经接受请求,但未处理
203处理但未处理完
204服务端已经处理完请求,但无内容返回
301永久重定向
302临时重定向
304本地缓存
400错误请求,请求格式错误
401未经许可,未通过http认证
403拒绝请求
404未找到请求资源
500服务器错误

函数参数 rest 、默认参数

  • rest代表剩余参数,后面不可再接参数。
  • 定义了默认值的参数,应该是函数的尾参数。
  • arguments是伪数组,要先转成数组。

XSS(cross site scripting)跨站脚本攻击

  • 指黑客往html或dom注入恶意js脚本文件,当用户浏览网站时实施攻击.
  • 分三种存储型XSS、反射型XSS、Dom型XSS
  1. 存储型XSS将恶意代码提交到数据库,当用户浏览网页时会被恶意执行,通常攻击带有用户保存数据的网站功能,例如发帖评论等。存储型XSS隐蔽性高,危害大,任何允许用户储存数据的web程序都可能存在该漏洞。

  2. 反射型XSS是将恶意代码放在url上,用户点击该链接就会遭到攻击,由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

  3. DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。它不需要服务器解析响应的直接参与,触发XSS靠的是浏览器端的DOM解析,所以防范DOM型XSS完全就是前端的责任,必须注意!!!

常见防范手段

  • 在cookies设置httponly属性,js脚本就无法读取cookies信息
  • 将输入信息进行过滤或转义HTML
  • 白名单

CSRF(cross-site request forgery)跨站请求伪造

  • 诱惑用户进入第三方网站并登录,夺取用户登录信息进行登录,冒充用户进行操作。
  • 主要步骤:用户登录信任网站并在本地生成cookies,在未退出登录的情况下,去访问危险网站。 防范:
  • 验证码、token、referer check

XSS与CSRF区别

XSS 是代码注入问题,CSRF 是 HTTP 问题。 XSS 是内容没有过滤导致浏览器将攻击者的输入当代码执行。CSRF 则是因为浏览器在发送 HTTP 请求时候自动带上 cookie,而一般网站的 session 都存在 cookie里面(Token验证可以避免)。

2022.5.10笔试

IE内核、Firefox内核、chrome内核

  • IE:Trident
  • Firefox:Gecko
  • chrome:webkit

js数字存储

双精度浮点数在计算机中存储占8个字节(64位),包括符号位1位,指数位11位,尾数位52位

css颜色定义

  • rgb、rgba HSL模式、HSLA

  • H: Hue(色调)。0(或360)表示红色,120表示绿色,240表示蓝色,也可取其他数值来指定颜色。取值为:0 - 360

  • S: Saturation(饱和度)。取值为:0.0% - 100.0%

  • L: Lightness(亮度)。取值为:0.0% - 100.0%

清除浮动

.box::after { content: '.'; height: 0; display: block; clear: both; }

juejin.cn/post/698217…

Proxy 与Object.defineProperty对比区别

Object.definePropertyProxy
兼容好可以直接监听对象而非属性、可以监听数组变化、多种拦截方法(apply\ownKeys)

移动端viewport设置

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
​
​
width=device-width: 是自适应手机屏幕的尺寸宽度。
maximum-scale:是缩放比例的最大值。
minimum-scale:是缩放比例的最小值。
inital-scale:是缩放的初始化。
user-scalable:是用户的可以缩放的操作。

闭包

  • 概念:指有权访问其他函数作用域的变量。一种私有化,保护和保存变量的机制。保护,即保护变量不会被垃圾回收机制清除,保存即从栈内存变为堆内存引用。
  • 作用: 防止全局污染,应用场景主要是维护内部变量,我使用过的场景有防抖节流等
  • 过度使用会造成内存泄漏,页面渲染会变慢,导致程序卡顿崩溃。
  • 内存泄漏解决:避免过渡使用闭包、使用严格模式、销毁阶段解绑dom事件

2022.5.11 daole面试

meta 文档级元数据元素

定义:用于定义页面的说明、关键字和其他元数据。

  • 一、name属性
  1. author、description、keyword
  2. viewpoint 移动设备视口配置
  3. generator 开发工具、revised 页面最新版本
  4. robots 告诉搜索引擎机器人该做什么 {
    all:搜索引擎将索引此网页,并继续通过此网页的链接索引文件将被检索

none:搜索引擎讲忽略此网页

index:搜索引擎索引此网页

follow:搜索引擎继续通过此网页的链接索引搜索其它的网页

}

  1. renderer 指定浏览器渲染方式(webkit)
  • 二、http-equiv属性
  1. X-UA-Compatible 用于告知浏览器以何种版本来渲染页面。(一般都设置为最新模式,在各大框架中这个设置也很常见。)
<!-- 优先使用 IE 最新版本和 Chrome -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<!-- 不让百度转码 -->
<meta http-equiv="Cache-Control" content="no-siteapp" />
  1. content-Type设置字符集
  2. x-dns-prefetch-control 打开dns对a标签的提前解析
  3. cache-control(指定请求和响应应该遵循的缓存机制)

no-cache:先发送请求,确认服务器资源是否更改,无则使用缓存

no-store:不缓存

public:缓存所有响应

private:只为单个用户缓存

maxage:最大缓存时间不发送请求确认服务器资源更新

<< cache-control、Pragma、Expires、refresh

和缓存相关的设置,但是遗憾的是这些往往不生效,我们一般都通过http headers来设置缓存策略

  • 三、charset
 <!-- 声明文档使用的字符编码 -->
<meta charset='utf-8'>

执行上下文(Excution context stack)

评估和执行js代码的环境的抽象概念。通俗讲,是在执行上下文中执行js代码;也可以理解为一个object,里面包括变量对象、活动对象、作用域链、调用者信息。

执行上下文 为我们的可执行代码块提供了执行前的必要准备工作,例如变量对象的定义、作用域链的扩展、提供调用者的对象引用等信息。

分三种类型:

全局上下文、函数上下文、eval上下文

函数作用域与执行上下文

函数作用域是声明时已经确定,而执行上下文是函数调用前才生成。两者我觉得是包含关系,执行上下文不仅包含当前函数作用域。还包含调用者对象信息以及整条作用域的信息。

数组与链表

  • 数组:线性表结构,元素地址强制连续,连续的内存空间,一般只存储相同数据类型(js数组可存储不同数据类型); 随机访问(查询)效率高,增删效率慢。
  • 链表:线性链表结构,是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。支持动态扩容; 随机访问(查询)效率低,增删效率快。(单链表最后指向null)
时间复杂度数组链表
查询O(0)O(n)
增删O(n)O(0)
在如今的编程中,更多的是只用到前两种,选用什么容器取决于你的业务属性。
  1. 在选用数组时可以预估一下集合规模然后初始化一个比较合适的动态数组,避免多次扩容。
  2. 选用链表时可以先问问自己是查询的场景多还是插入的场景多。
  3. 使用队列和栈时也可以先了解一下自己使用的类库的底层实现是数组还是链表,当你面对的东西对你来说不再是黑盒时,就可以轻松的作出判断了~

dpr(device pixel ratio)设备像素比

  • 物理像素=分辨率=设备像素,是手机出厂时固定的。
  • 设备独立像素=css像素=逻辑像素,抽象的。
  • 设备像素比(dpr)= 物理像素(分辨率)/css像素
  • ppi(pixel pei inch)每英寸像素,像素密度 通过window.devicePixelRadtio可获取设备像素比;若dpr=2,1px=实际设备的2px,需要设置meta中的viewport的init-scale= 1/dpr;

解决1px边框变粗问题。用dpr解决的话还是会因为浏览器不同产生不同结果,此时只能用伪类和transform解决,如下

.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  /*核心是利用transform缩放边框*/
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}

(设置html{ text-size-adjust:none},解决自适应变小时字体反而变大)

canvas(两倍放大在transform缩小会清晰原理)

因为canvas是以中线为基准测量1px,所以实际会覆盖2px,导致很多地方渲染重复变模糊

参考www.jb51.net/html5/81503…

image.png

tranform属性优化原因

wenku.baidu.com/view/6830ca…

2022.5.27面试

懒加载原理

  • 1、直接用img标签元素的属性loading=lazy;
  • 2、vue插件vue-lazyload
  • 3、手写懒加载,给src设置空或者一张默认loading,再给img设置dataset-src属性且里面为图片的真实地址,(当offsetTop-scrollTop<=clientHeight,就将图片地址赋值给src)
offfsetTopclientHeightscrollTop
元素距离浏览器顶部的绝对距离浏览器可视窗口大小滑动距离
function lazy(){
  var imgs = document.querySelectorAll();
  let relHeight = document.body.clientHeight;
  let scrollHeight = document.body.scrollHeight;
  for(let i=0;i<imgs.length;i++){
    let imgTop = imgs[i].offsetHeight;
    if(imgTop - scrollHeight <= relHeight){
      imgs[i].src = imgs[i].getAttribute('data-src');
    }
  }
   
}
window.onscroll = (()=>{
  lazy();
})

4、利用getBoundingClientRect(),当getBoundingWidthRect().top< clientHeight,赋值src