侧边栏壁纸
博主头像
uvdream博主等级

一切皆有可能!

  • 累计撰写 40 篇文章
  • 累计创建 21 个标签
  • 累计收到 18 条评论

【小试牛刀】2021年面试经

uvdream
2021-09-04 / 0 评论 / 13 点赞 / 179 阅读 / 7,048 字
温馨提示:
本文最后更新于 2022-04-08,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Javascript

JavaScript 判断数组的方法

  • 通过 Object.prototype.toString.call()做判断
  • obj.__proto__ === Array.prototype;
  • Array.isArray()
  • obj instanceof Array
  • Array.prototype.isPrototypeOf(obj)

JavaScript去重方法

  • 1.遍历数组法

新建一个数组,遍历要去重的数组,当值不在新数组的时候(indexOf 为-1)就加入该新数组

  • 2.数组下标判断法

如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,表示第 i 项是重复的.忽略掉,否则存入结果数组

  • 3.排序后相邻去除法

给传入的数组排序.排序后相同的值会相邻.然后遍历排序后的数组,新数组之加入不与前一值重复的值

  • 4.优化遍历数组法

双层循环,外层循环表示从 0 到 arr.length,内层循环表示从 i+1 到 arr.length

  • 5.ES6 中 set 方法
  • 6.利用 ES6 中 sort

先排序,然后根据排序后的结果进行遍历及其相邻的元素对比

  • 7.利用 includes

检测数组中是否存在某个值

  • 8.利用 for 嵌套 for,然后 splice 去重
  • 9.利用 hasOwnProperty

利用 hasOwnProperty 哦安短是否存在对象属性

  • 10.利用递归去重
  • 11.利用 Map 数据结构去重
  • 12.利用 reduce+includes
  • 13.[...new Set(arr)]

JavaScriptnew做了什么


function Person() {

  this.name = name;


  this.age = age;


  this.sex = sex;


  this.sayName = function() {

    return this.name;

  };

}


var person = new Person("tom", 21, "famle");


console.log(person.name);

  • 创建一个新的对象
  • 将新的对象的__proto__指向构造函数的 prototype 对象
  • 将构造函数的作用域赋值给新的对象(也就是 this 指向新对象)
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 返回新的对象

var Obj = {};



Obj._proto_ =  Person.prototype();



Person.call(Obj);

let const var区别

  • var声明的变量挂载在window上,而let const不会
  • var声明的变量存在变量提升,letconst不存在变量提升
  • letconst声明形成块作用域
  • 同一作用域下letconst不能声明同名变量,而var可以
  • 暂存死区

this指向

1:this 永远指向一个对象;

2:this 的指向完全取决于函数调用的位置;


class Test {

  constructor() {}


  fun = () => {

    console.log(this);

  };


  fun1() {

    console.log(this);

  }

}


let test = new Test();


test.fun(); //Test


test.fun.call(this); //Test


test.fun1(); //Test


test.fun1.call(this); //window

JavaScript改变this指向办法

  • call()
  • apply()
  • bind()
  • that

call,apply,bind区别

bind 返回对应函数,便于稍后调用;apply 和 call 则是立即调用

除此外,在 ES6 的箭头函数下,call 和 apply 将失效

箭头函数和普通函数区别

  • 箭头函数是匿名函数,不能作为构造函数,不能使用 new
  • 箭头函数不能绑定 arguments,取而代之用 rest 参数解决
  • 箭头函数没有原型属性
  • 箭头函数的 this 永远指向其上下文的 this,没有改变其指向,普通函数的 this 指向调用它的对象
  • 箭头函数不绑定 this,会捕获其所在的上下文的 this,作为自己的 this 的值

JavaScript事件委托

事件委托就是利用冒泡的原理将事件加到父元素或者祖先元素上,触发执行效果

优点

  • 可以提高 js 性能
  • 可以动态添加 dom 元素,不需要因为元素的变动而修改事件的绑定

Promise链式调用

return this

Promise相比较ajax有哪些优势

  1. Promise 三个状态
  • pending:准备状态
  • fulfilled:成功
  • rejected:失败
  1. Promise 对象接受一个回调函数作为参数,改回调参数接受两个参数,分别是成功时的回调 resolve 和失败时的回调 reject,另外 resolve 的参数除了正常值以外,还可能是一个 Promise 对象的实例;reject 参数通常是一个 Error 对象的实例

  2. then 方法返回一个新的 promise 对象,并接受两个参数 onResolved(fulfilled 状态回调)

  3. catch 方法返回一个新的 promise 实例

  4. finally 方法不管 Promise 状态如何都会执行,该方法的回调函数不接受任何参数

  5. Promise.all()方法将多个 Promise 实例包装成一个新的 Promise 实例,该方法接受一个由 promise 对象组成的数组作为参数

    promise.all()方法返回新的实例 catch 方法,如果参数中某个实例调用了 catch 方法,将不会触发 promise.all()方法返回的新实例的 catch 方法

  6. promise.race()

  7. promise.resolve()

  8. promise.reject()

Promise优点

  1. 统一异步的 API

    逐渐被用作浏览器的异步 API,统一了各种各样的 API,以及不兼容的模式和写法

  2. Promise 与事件对比

    和事件相比较,promise 更适合处理一次性的结果,在结果计算出来之前或之后注册回调函数都是可以的,都可以拿到正确的值,

  3. Promise 与回调对比

    解决了回调低于的问题,将异步操作以同步操作的流程表达出来

  4. Promise 额外的好处就是更好的错误处理,并且写起来很轻松

Promise缺点

  • 无法取消 Promise,一旦新建他就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部
  • 当处于 pending 状态时,无法知道进行到哪个阶段
  • Promise 真正执行回调的时候,定义 Promise 那部分实际已经走完了,所以 promise 的报错堆栈上下文不大友好

异步问题同步化解决方案

JavaScript统计字符串中字符出现的次数


var arr = "hello world";


var obj = {};


for (var i = 0; i < arr.length; i++) {

  var key = arr[i];


  if (obj[key]) {

    obj[key]++;

  } else {

    obj[key] = 1;

  }

}


console.log(obj);

JavaScript函数柯里化

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。通俗点说就是将一个函数拆分成多个函数,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数


// 实现一个简单的加法


function add(a, b) {

  return a + b;

}


add(1, 2);


// 用柯里化实现


const currying = (x) => {

  return (y) => {

    return x + y;

  };

};


console.log(currying(1)(2)); // 3

  1. 优点:

    1)可以实现参数复用

    2)实现延迟执行;先收集所有参数,最后执行

    3)实现功能细化

  2. 缺点:

    1. 实现出现闭包,占用大量内存,产生性能问题
    2. 递归效率低

JavaScript防抖和节流

防抖

定义

在事件被触发后 n 秒后再执行回调函数,如果这个 n 秒内又被触发,则重新计时

应用场景

  1. 用户输入框连续输入一串字符串后,只会在输入完成后去执行最后一次查询的 ajax 请求,这样可以有效减少请求次数,节约请求资源
  2. window 的 resize,scroll 时间,不断的调整浏览器窗口大小,或者滚动时触发对应时间,防抖让其只触发一次

节流

定义

规定一个单位时间,在这个单位时间内,只能有一次触发时间的回调函数执行,如果同一单位时间内某事件被触发多次,只能一次能生效

应用场景

  1. 鼠标连续不断的触发某事件,只能单位时间内只触发一次
  2. 在页面的无限加载场景下,需要用户在滚动页面是,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据
  3. 监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断

JavaScript宏任务和微任务


setTimeout(() => {

  console.log(1);

}, 0);


console.log(2);


new Promise((resolve, reject) => {

  console.log(3);


  resolve();

}).then((res) => {

  console.log(4);

});


setTimeout(() => {

  console.log(5);

}, 0);


console.log(6);

答案


2 3 6 4 1 5

CSS

css 垂直居中的办法

仅仅居中元素宽高适用

  • absolute +负 margin
  • absolute + margin auto
  • absolute + calc

居中元素不定宽高

  • absolute + transform
  • line-height+text-align
  • writing-mode
  • table
  • css-table
  • flex
  • grid

JavaScript模块化规范有哪些

  • CommonJs
  • AMD
  • CMD
  • ES6 Modules

Vue

Vue为什么只有一个根元素

抽象出来的 dom 树只能有一个根节点

为什么抽象出来的 dom 树只有一个跟节点

  • 从查找和遍历的角度来说,如果有多个根,那么我们的查找和遍历的效率会很低
  • 如果一个树有多个根,说明可以优化,肯定会有一个节点是可以访问到所有的节点,那么这个节点就会成为新的根节点
  • 如果一个组件有多个入口或者多个根,那不就意味你的组件还可以进一步拆分成多个组件,进一步组件优化,降低代码之间耦合程度

vue实现深度监听

浏览器

浏览器如何渲染页面

  • 1.根据 html 文件构建 dom 树和 cssom 树,构建 dom 树期间,如果遇到 js,阻塞了 dom 树以及 cssom 树的构建,优先加载 js 文件,再继续构建 dom 树以及 cssom 树
  • 2.构建渲染树(Render Tree)
  • 3.页面的重绘(repaint)与重排(reflow 也称回流),页面渲染完成后,若有 js 操作了 dom,根据 js 对 dom 操作的动作大小,浏览器对页面进行重绘或回流

浏览器重绘和回流

回流

当 Render Tree 中的部分或全部元素的尺寸,结构或者处罚某些属性时,浏览器会重新计算并渲染页面,称为回流。此时浏览器需要重新进行计算,计算后还需要重新页面布局,因此是较重的操作。

会导致回流的操作有:

  • 页面初次渲染
  • 浏览器窗口发生变化
  • 元素尺寸,位置,内容发生变化
  • 元素字体大小变化
  • 添加或者删除可见的 dom 元素
  • 激活 css 伪类
  • 查询某些属性或者调用某些方法

clientWidth, clientHeight, clientTop, clientLeft


offsetWidth, offsetHeight, offsetTop, offsetLeft


scrollWidth, scrollHeight, scrollTop, scrollLeft


scrollIntorView(), scrollInToViewIfNeeded()


getComputedStyle()


getBoundingClientRect()


scrollTop

重绘

当元素的样式改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要对 UI 层面重新绘制,因为损耗较少

回流一定会导致重绘,但是重绘不一定导致回流

如何避免

CSS

  • 避免使用 table 布局
  • 尽可能在 dom 树的最末端改变 class
  • 避免设置多层内联样式
  • 避免 CSS 表达式

Javascript

  • 避免频繁的操作样式,最好一次性写好 style 属性,或者将样式定义为 class,并且一次性更改 class
  • 避免频繁操作 dom,创建一个 documentFragment,这上面应有所有的 dom 操作,最后把他添加到文档中
  • 也可以设置 display:none,操作结束后再把它显示出来,因为 display 属性为 none 的元素上进行 dom 操作不会引起重绘和回流
  • 避免频繁读取引发回流和重绘的属性,如果要多次使用,建议先把他缓存起来
  • 对具有复杂动画的元素使用绝对定位,使他脱离文档流,否则会引起父元素及其后续元素频繁回流

http

Tcp 过程

  • 第一次握手:

    客户端发送SYN报文,并发送序号为X

  • 第二次握手:

    服务端发送SYN+ACK报文,并发送序号Y,在确认序号为X+1

  • 第三次握手.

    客户端发送ACK报文,并且发送序号Z,在确认序号为Y+1

为什么连接的时候是三次握手,关闭的时候却是四次握手

因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

http状态码

| 状态码 | 分类描述 |

| ------ | ---------------------------------------------- |

| 1* | 信息,服务器收到请求,需要请求者继续执行操作 |

| 2* | 成功,操作被成功接收并处理 |

| 3* | 重定向,需要进一步的操作以完成请求 |

| 4* | 客户端错误,请求包含语法错误或无法完成请求 |

| 5* | 服务器错误,服务器在处理请求的过程中发生了错误 |

httphttps

HTTP协议通过客户端请求-->服务端响应的方式进行通信。但是HTTP有一个致命的缺点就是:不够安全。

HTTP协议的信息传输完全以明文的方式,不做加密,相当于在网络上裸奔。

HTTPS加密方式:非对称秘钥

webpack

webpack 常用插件

  • html-webpack-plugin(html 文件生成插件)
  • imagemin-webpack-plugin(图片压缩插件)
  • clean-webpack-plugin(清除空文件夹插件)
  • min-css-extract-plugin(css 公共代码提取)
  • copy-webpack-plugin(文件复制插件)
  • happyPack(多线程打包插件)(不维护)
  • thread-loader(多线程打包)

webpack做过哪些优化

  1. 优化 webpack 的构建速度
  • 使用高版本 webpack
  • 多线程,多实例构建
  • 缩小打包作用域
  • 充分利用缓存提升二次构建的速度
  • DLL 分包处理
  1. 优化webpack打包的体积
  • 压缩代码

  • 提取公共资源

  • Tree Shaking

  • Scope hoisiting

    构建后代码会存在大量闭包,造成体积大,运行时代码创建的函数作用域变多,内存开销大,Scope hoisiting将所有模块按照引用顺序放在一个函数作用域内,然后适当的重命名一些变量以防止变量名冲突

  • 图片压缩

  • 动态Polyfill

webpackrollup区别

  • webpack 拆分代码,按需加载
  • rollup 所有资源放在同一个地方,一次性加载,利用 tree-shake 特性来剔除项目中未使用的代码,减少冗余,但是 webpack2 已经逐渐支持 tree-shaking
  • webpack ES2015 模块打包支持
  • 应用使用 webpack,对于类库使用 rollup

webpack插件是如何实现的

webpack 本质是一种事件流机制,核心模块:tapable(Sync+Async)Hooks构造出=>Compiler(编译)+Compilation(创建bundles)

  • Compiler 对象代表了完整的 webpack 环境配置,这个对象在启动 webpack 时被一次性创建,并配置好所有可操作的设置,包括option,loader和plugin,当在 webpack 环境中应用一个插件时,插件将受到 compiler 对象的引用,可以使用它来访问 webpack 的主环境
  • Compilation 对象代表了一次资源版本的构建,当 webpack 开发环境中间时,每当检测到一个文件变化,就会创建新的 compilation,从而生成一组新的编译资源,一个 compilation 对象表现了当前的模块资源,编译生成的资源,变化的文件以及被跟踪依赖的状态信息.compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用
  • 创建一个插件函数,在其 prototype 上定一个 apply 方法,指定一个绑定到 webpack 自身事件钩子
  • 函数内,处理 webpack 内部示例的特定数据
  • 处理完成后,调用 webpack 提供的回调

前端工程化

前端工程化就是为了让前端开发能够“自成体系”,个人认为主要应该从模块化、组件化、规范化、自动化四个方面思考

  1. 技术选型
  2. 统一规范
  • 规范的代码可以促进团队合作
  • 规范的代码可以降低维护成本
  • 规范的代码有助于 code review
  • 养成代码规范的习惯,有助于程序员自身的成长
  1. 测试
  2. 部署
  3. 监控
  4. 性能优化
  5. 重构
0

评论区