前端高频面试题三(转载收藏)

120 阅读10分钟

前端工程化

webpack配置,webpack4.0有哪些优化点

module.exports={
	entry: {},
	output: {},
	plugins: [],
	module: [rules:[{}]]
}

webpack如何实现代码分离

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk
  • 动态导入:通过模块的内联函数调用来分离代码。

常见的Webpack Loader? 如何实现一个Webpack Loader(NO)

loader: 是一个导出为函数的javascript模块,根据rule匹配文件扩展名,处理文件的转换器。

file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)

url-loader: 与file-loader类似,区别是用户可以设置一个阈值,大于阈值会交给file-loader处理,小于阈值时返回文件base64 形式编码 (处理图片和字体)

image-loader:加载并且压缩图片文件

babel-loader:把 ES6 转换成 ES5

sass-loader:将SCSS/SASS代码转换成CSS

css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS

postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 eslint-loader:通过 ESLint 检查 JavaScript 代码

常见的Webpack Plugin? 如何实现一个Webpack Plugin(NO)

plugin:本质是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)

uglifyjs-webpack-plugin:压缩js文件

clean-webpack-plugin:目录清除

mini-css-extract-plugin:分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)

loader和plugin对比?

  • Loadermodule.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
  • Plugin plugins 中单独配置,类型为数组,每一项是一个Plugin的实例,参数都通过构造函数传入。

前端模块化,CMD、AMD、CommonJS

CommonJS

CommonJS是服务器端模块的规范,由Node推广使用,webpack也采用这种规范编写

commonJs规范:

CommonJS模块规范主要分为三部分:模块定义、模块标识、模块引用

  • 模块定义:module对象:在每一个模块中,module对象代表该模块自身。 export属性:module对象的一个属性,它向外提供接口。输出模块变量的最好方法是使用module.exports对象。一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。
  • 模块标识:传递给require方法的参数,必须是符合小驼峰命名的字符串,或者以 . 、.. 、开头的相对路径,或者绝对路径。
  • 模块引用:加载模块使用require(同步加载),该方法读取一个文件并执行,返回文件内部的module.exports对象。

优势:

在后端,JavaScript的规范远远落后并且有很多缺陷,这使得难以使用JavaScript开发大型应用。比如:没有模块系统、标准库较少、没有标准接口、缺乏包管理系统、列表内容

  1. CommonJS模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间相比之下就不太好。
  2. CommonJS规范定义模块十分简单,接口十分简洁。
  3. CommonJS模块规范支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系
  4. CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段

缺点:

没有并行加载机制

由于CommonJS是同步加载模块,这对于服务器端是很不好的,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。

所以浏览器端不是很适合Common.Js,出现另一种规范AMD

AMD

AMD 是运行在浏览器环境的一个异步模块定义规范 ,是RequireJS 在推广过程中对模块定义的规范化产出。

AMD规范

AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块

优点

用户体验好,因为没有延迟,依赖模块提前执行了。

CMD

CMD是一个通用模块定义规范;是SeaJs推广过程中对模块定义的规范化产出

CMD规范

CMD推崇依赖就近,只有在用到某个模块的时候才会去require

优点

性能好,因为只有用户需要的时候才执行。

面试手写代码系列

防抖节流

函数防抖在一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。

防抖

//定义:触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。
//搜索框搜索输入。只需用户最后一次输入完,再发送请求
//手机号、邮箱验证输入检测 onchange oninput事件
//窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
const debounce = (fn, wait, immediate) => {
      let timer = null;
      return function (...args) {
        if (timer) clearTimeout(timer);
        if (immediate && !timer) {
          fn.call(this, args);
        }
        timer = setTimeout(() => {
          fn.call(this, args);
        }, wait);
      };
    };
const betterFn = debounce(() => console.log("fn 防抖执行了"), 1000, true);
document.addEventListener("scroll", betterFn);
复制代码

节流

//定义:当持续触发事件时,保证隔间时间触发一次事件。
//1. 懒加载、滚动加载、加载更多或监听滚动条位置;
//2. 百度搜索框,搜索联想功能;
//3. 防止高频点击提交,防止表单重复提交;
function throttle(fn,wait){
    let pre = 0;
    return function(...args){
        let now = Date.now();
        if( now - pre >= wait){
            fn.apply(this,args);
            pre = now;
        }
    }
}
function handle(){
    console.log(Math.random());
}
window.addEventListener("mousemove",throttle(handle,1000));
复制代码

对象深浅拷贝

//浅拷贝 
1. Object.assign(target,source)
2. es6对象扩展运算符。
//深拷贝    
function deepClone(obj) {
      if (!obj || typeof obj !== "object") return;
      let newObj = Array.isArray(obj) ? [] : {};
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
        }
      }
      return newObj;
}
复制代码

数组去重,数组对象去重

//数组
const arr = [2,7,5,7,2,8,9];
console.log([...new Set(arr)]); // [2,7,5,8,9];
//对象
const list = [{age:18,name:'张三'},{age:18,name:'李四'},{age:18,name:'王五'}]
let hash = {};
const newArr = arr.reduce((item, next) => {
    hash[next.age] ? '' : hash[next.age] = true && item.push(next);
    return item;
}, []);
console.log(list);
复制代码

数组扁平化

function flatten(arr) {
      return arr.reduce((result, item) => {
        return result.concat(Array.isArray(item) ? flatten(item) : item);
      }, []);
}
复制代码

职业技能规划、人事面试

  • 未来准备往哪方面发展?精通/全干
  • 对于职业规划的个人见解
  • 自身价值的体现

离职原因

  • 个人职业规划原因
  • 公司原因

其他

  1. 你未来一到三年的一个职业规划是什么?
  2. 你都是怎么去学习和关注新技术的?
  3. 你近几年工作中有哪些心得或总结?
  4. 你觉得你在工作中的优缺点是什么?
  5. 你过来我们公司,你的优势是什么?
  6. 有些过开源项目吗?
  7. 写过 npm 包吗,写过 webpack 插件吗?
  8. 看过哪些框架或者类库的源码,有什么收获?

补充

ES6里的symble

它的功能类似于一种标识唯一性的ID,每个Symbol实例都是唯一的。 Symbol类型的key是不能通过Object.keys()或者for...in来枚举的, 它未被包含在对象自身的属性名集合(property names)之中。 所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。 // 使用Object的API Object.getOwnPropertySymbols(obj) // [Symbol(name)]

// 使用新增的反射API Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']

ES6里的set和map

  • Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。
  • Set对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

vue的key

  1. key的作用主要是为了高效的更新虚拟DOM,其原理是vuepatch过程中通过key可以精准判断两个节点是否是同一个,

从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。 2. 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug 3. vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们, 否则vue只会替换其内部属性而不会触发过渡效果。

普通函数和箭头函数的区别

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
  2. 箭头函数不绑定arguments,取而代之用rest参数...解决
  3. 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
  4. 箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
  5. 箭头函数没有原型属性
  6. 箭头函数不能当做Generator函数,不能使用yield关键字

总结:

  • 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如call() , bind() , apply()
  • 普通函数的this指向调用它的那个对象

JS函数柯里化

  1. 参数复用
  2. 提前确认
  3. 延迟运行
// 普通的add函数
function add(x, y) {
    return x + y
}

// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}

add(1, 2)           // 3
curryingAdd(1)(2)   // 3

实现继承口述

原型链继承 写个父类、子类 子类的原型为父类的实例 子类.prootype = new 父类 修正子类原型为子类本身 子类.prototype.constructor=子类 new 子类即可调用父类方法 构造函数继承 写个父类、子类 在子类中父类.call(this) 即可实现

mapState, mapGetters, mapActions, mapMutations

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性 mapMutations 其实跟mapState 的作用是类似的,将组件中的 methods 映射为 store.commit 调用

计算一个函数的难易程度公式

image.png 如上图可以粗略的分为两类,多项式量级和非多项式量级。其中,非多项式量级只有两个:O(2n) 和 O(n!) 对应的增长率如下图所示

image.png

vue源码理解

xxxxx...

osi7层模型,tcp5层模型

osi7层模型:物理层-数据链路层-传输层-网络层-应用层-会话层-表示层

tcp5层模型:物理层-数据链路层-传输层-网络层-应用层