前端面试基础篇,凛冬将至!道友快来一起闭关修炼!

11,018 阅读35分钟

借用神剧《权游》的名句:“凛冬将至!”,互联网寒冬已然来临,这一年以来keep/滴滴/京东/腾讯/华为等大厂裁员事件不断爆出!惊醒着头发慢慢脱落、变少的我们,是时候闭关修炼了!

本文为前端面试基础篇,将以 面试题 && 答案【参考 (๑¯∀¯๑)】的形式,闭关修炼,希望你查漏补缺,完善你的知识体系!

面试,我们是认真的!

JavaScript 篇

1. JavaScript有⼏种类型的值

  • 栈:原始数据类型( Undefined , Null , Boolean , Number 、 String )
  • 堆:引⽤数据类型(对象、数组和函数)
  • 两种类型的区别是:存储位置不同
    • 原始数据类型直接存储在栈( stack )中的简单数据段,占据空间⼩、⼤⼩固定,属于被频 繁使⽤数据,所以放⼊栈中存储;
    • 引⽤数据类型存储在堆( heap )中的对象,占据空间⼤、⼤⼩不固定,如果存储在栈中,将会 影响程序运⾏的性能;引⽤数据类型在栈中存储了指针,该指针指向堆中该实体的起始地 址。当解释器寻找引⽤值时,会⾸先检索其 在栈中的地址,取得地址后从堆中获得实体

2. 介绍JavaScript有哪些内置对象

  • Object 是 JavaScript 中所有对象的⽗对象
  • 数据封装类对象: Object 、 Array 、 Boolean 、 Number 和 String
  • 其他对象: Function 、 Arguments 、 Math 、 Date 、 RegExp 、 Error

3. null,undefined 的区别

  • undefined 表示不存在这个值
  • undefined : 是⼀个表示"⽆"的原始值或者说表示"缺少值",就是此处应该有⼀个值,但 是还没有定义。 例如变量被声明了,但没有赋值时,就等于 undefined
  • null 表示⼀个对象被定义了,值为“空值”
  • null : 是⼀个对象(空对象, 没有任何属性和⽅法) 例如作为函数的参数,表示该函数的参数不是对象; 在验证 null 时,⼀定要使⽤ === ,因为 == ⽆法分别 null 和 undefined

4. 什么是事件代理

  • 事件代理( Event Delegation ),⼜称之为事件委托。是 JavaScript 中绑定事件的常⽤技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给⽗元素,让⽗元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
  • 使⽤事件代理的好处是:
    • 可以提⾼性能
    • 可以⼤量节省内存占⽤
    • 减少事件注册,⽐如在 table 上代理所有 td 的 click 事件

5. 同步和异步的区别

  • 同步:浏览器访问服务器请求,⽤户看得到⻚⾯刷新,重新发请求,等请求完,⻚⾯刷新, 新内容出现,⽤户看到新内容,进⾏下⼀步操作
  • 异步:浏览器访问服务器请求,⽤户正常操作,浏览器后端进⾏请求。等请求完,⻚⾯不 刷新,新内容也会出现,⽤户看到新内容

6. defer 和 async

  • defer 并⾏加载 js ⽂件,会按照⻚⾯上 script 标签的顺序执⾏
  • async 并⾏加载 js ⽂件,下载完成⽴即执⾏,不会按照⻚⾯上 script 标签的顺序执 ⾏

7. 什么是⾯向对象编程和⾯向过程编程

  • ⾯向过程就是分析出解决问题所需要的步骤,然后⽤函数把这些步骤⼀步⼀步实现,使⽤ 的时候⼀个⼀个依次调⽤就可以了
  • ⾯向对象是把构成问题事务分解成各个对象,建⽴对象的⽬的不是为了完成⼀个步骤,⽽ 是为了描叙某个事物在整个解决问题的步骤中的⾏为 ⾯向对象是以功能来划分问题,⽽不是步骤

8. ⾯向对象编程思想

  • 基本思想是使⽤对象,类,继承,封装等基本概念来进⾏程序设计

  • 优点

    • 易维护 采⽤⾯向对象思想设计的结构,可读性⾼,由于继承的存在,即使改变需求,那么维 护也只是在局部模块,所以维护起来是⾮常⽅便和较低成本的
    • 易扩展
    • 开发⼯作的重⽤性、继承性⾼,降低重复⼯作量
    • 缩短了开发周期

9. Javascript中callee和caller的作⽤?

  • caller 是返回⼀个对函数的引⽤,该函数调⽤了当前函数;
  • callee 是返回正在被执⾏的 function 函数,也就是所指定的 function 对象的正⽂

10. 事件的各个阶段

  • 1:捕获阶段 ---> 2:⽬标阶段 ---> 3:冒泡阶段
  • document ---> target ⽬标 ----> document
  • 由此, addEventListener 的第三个参数设置为 true 和 false 的区别已经⾮常清晰了
    • true 表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件
    • false 表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件

11. 闭包

  • 闭包就是能够读取其他函数内部变量的函数
  • 闭包是指有权访问另⼀个函数作⽤域中变量的函数,创建闭包的最常⻅的⽅式就是在⼀个 函数内创建另⼀个函数,通过另⼀个函数访问这个函数的局部变量,利⽤闭包可以突破作⽤链域
  • 闭包的特性:
    • 函数内再嵌套函数
    • 内部函数可以引⽤外层的参数和变量
    • 参数和变量不会被垃圾回收机制回收

说说你对闭包的理解

  • 使⽤闭包主要是为了设计私有的⽅法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增⼤内存使⽤量,使⽤不当很容易造成内存泄露。在js中,函数即 闭包,只有函数才会产⽣作⽤域的概念

  • 闭包的最⼤⽤处有两个,⼀个是可以读取函数内部的变量,另⼀个就是让这些变量始终保持在内存中

  • 闭包的另⼀个⽤处,是封装对象的私有属性和私有⽅法

  • 好处:能够实现封装和缓存等;

  • 坏处:就是消耗内存、不正当使⽤会造成内存溢出的问题 使⽤闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很⼤,所以不能滥⽤闭包,否 则会造成⽹⻚的性能问题,在IE中可能导致内存泄露

  • 解决⽅法是,在退出函数之前,将不使⽤的局部变量全部删除

12. 说说你对作⽤域链的理解

  • 作⽤域链的作⽤是保证执⾏环境⾥有权访问的变量和函数是有序的,作⽤域链的变量只能 向上访问,变量访问到 window 对象即被终⽌,作⽤域链向下访问变量是不被允许的
  • 简单的说,作⽤域就是变量与函数的可访问范围,即作⽤域控制着变量与函数的可⻅性和 ⽣命周期

13. JavaScript原型,原型链 ? 有什么特点?

  • 每个对象都会在其内部初始化⼀个属性,就是 prototype (原型),当我们访问⼀个对象的属性时
  • 如果这个对象内部不存在这个属性,那么他就会去 prototype ⾥找这个属性,这个 prototype ⼜会有⾃⼰的 prototype ,于是就这样⼀直找下去,也就是我们平时所说的原型链的概念
  • 关系: instance.constructor.prototype = instance._proto_
  • 特点:
  • JavaScript 对象是通过引⽤来传递的,我们创建的每个新对象实体中并没有⼀份属于 ⾃⼰的原型副本。当我们修改原型时,与之相关的对象也会继承这⼀改变
  • 当我们需要⼀个属性的时, Javascript 引擎会先看当前对象中是否有这个属性, 如果没有的就会查找他的 Prototype 对象是否有这个属性,如此递推下去,⼀直检索到 Object 内 建对象

14. Javascript如何实现继承?

  • 构造继承
  • 原型继承
  • 实例继承
  • 拷⻉继承
  • 原型 prototype 机制或 apply 和 call ⽅法去实现较简单,建议使⽤构造函数与原型混合⽅式
    function Parent(){
    this.name = 'wang'; }
    function Child(){
    this.age = 28; }
    Child.prototype = new Parent(); // 通过原型继承了Parent
    var demo = new Child();
    console.log(demo.age);
    console.log(demo.name); // 得到被继承的属性
    

15. javascript有哪些⽅法定义对象

  • 对象字⾯量: var obj = {};
  • 构造函数: var obj = new Object();
  • Object.create(): var obj = Object.create(Object.prototype);

16. 谈谈This对象的理解

  • this 总是指向函数的直接调⽤者(⽽⾮间接调⽤者)
  • 如果有 new 关键字, this 指向 new 出来的那个对象
  • 在事件中, this 指向触发这个事件的对象,特殊的是, IE 中的 attachEvent 中的 this 总是指向全局对象 Window

17. new操作符具体⼲了什么呢?

  • 创建⼀个空对象,并且 this 变量引⽤该对象,同时还继承了该函数的原型
  • 属性和⽅法被加⼊到 this 引⽤的对象中
  • 新创建的对象由 this 所引⽤,并且最后隐式的返回 this

18. 那些操作会造成内存泄漏?

  • 内存泄漏指任何对象在您不再拥有或需要它之后仍然存在
  • setTimeout 的第⼀个参数使⽤字符串⽽⾮函数的话,会引发内存泄漏
  • 闭包使⽤不当

19. Ajax原理

  • Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过 XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascrip t 来操作 DOM ⽽更新⻚⾯。使⽤户操作与服务器响应异步化。这其中最关键的⼀步就是从服 务器获得请求数据
  • Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是 aja x的核⼼机制
    /** 1. 创建连接 **/
    let xhr = null;
    xhr = new XMLHttpRequest()
    /** 2. 连接服务器 **/
    xhr.open('get', url, true)
    /** 3. 发送请求 **/
    xhr.send(null);
    /** 4. 接受请求 **/
    xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
    if(xhr.status == 200){
    success(xhr.responseText); } else {
    /** false **/
    fail && fail(xhr.status); } } }
    

ajax 有那些优缺点?

  • 优点:
    • 通过异步模式,提升了⽤户体验.
    • 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占⽤.
    • Ajax 在客户端运⾏,承担了⼀部分本来由服务器承担的⼯作,减少了⼤⽤户量下的服 务器负载。 Ajax 可以实现动态不刷新(局部刷新)
  • 缺点:
    • 安全问题 AJAX 暴露了与服务器交互的细节。
    • 对搜索引擎的⽀持⽐较弱。 不容易调试。

20. 如何解决跨域问题?

先了解一下什么是同源策略

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

同源策略限制以下几种行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 Js对象无法获得
  • AJAX 请求不能发送

常用跨域解决方案

  • 通过jsonp跨域
  • 跨域资源共享(CORS)
  • nginx代理跨域
  • nodejs中间件代理跨域

(PS:篇幅有限,具体用法自行百度)

21. 说说 event loop

  • ⾸先, js 是单线程的,主要的任务是处理⽤户的交互,⽽⽤户的交互⽆⾮就是响应 DOM 的增删改,使⽤事件队列的形式,⼀次事件循环只处理⼀个事件
  • 响应,使得脚本执⾏相对连续,所以有了事件队列,⽤来储存待执⾏的事件
  • 那么事件队列的事件从哪⾥被 push 进来的呢。那就是另外⼀个线程叫事件触发线程做的事情了
  • 它的作⽤主要是在定时触发器线程、异步 HTTP 请求线程 满⾜特定条件下的回调函数 push 到事件队列中,等待 js 引擎空闲的时候去 执⾏,当然js引擎执⾏过程中有优先级之分,⾸先js引擎在⼀次事件循环中, 会先执⾏js线程的主任务,然后会去查找是否有微任务 microtask(promise) ,如果有那就优先执⾏微任务,如果没有,在去查找 宏任务 macrotask(setTimeout、setInterval) 进⾏执⾏

22. 什么是单线程,和异步的关系

  • 单线程 - 只有⼀个线程,只能做⼀件事
  • 原因 - 避免 DOM 渲染的冲突
    • 浏览器需要渲染 DOM
    • JS 可以修改 DOM 结构
    • JS 执⾏的时候,浏览器 DOM 渲染会暂停
    • 两段 JS 也不能同时执⾏(都修改 DOM 就冲突了)
    • webworker ⽀持多线程,但是不能访问 DOM
  • 解决⽅案 - 异步

23. 是否⽤过 jQuery 的 Deferred

使用jQuery Deferred如下:

总结如下:

  • 无法改变 JS 异步和单线程的本质
  • 只能从写法上杜绝 callback 这种形式
  • 它是一种语法糖形式,但是解耦了代码
  • 很好的体现:开放封闭原则

24. 异步编程的实现⽅式

  • 回调函数
    • 优点:简单、容易理解
    • 缺点:不利于维护,代码耦合⾼
  • 事件监听(采⽤时间驱动模式,取决于某个事件是否发⽣):
    • 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
    • 缺点:事件驱动型,流程不够清晰
  • 发布/订阅(观察者模式)
    • 类似于事件监听,但是可以通过‘消息中⼼ʼ,了解现在有多少发布者,多少订阅者
  • Promise对象
    • 优点:可以利⽤then⽅法,进⾏链式写法;可以书写错误时的回调函数;
    • 缺点:编写和理解,相对⽐较难
  • Generator函数
    • 优点:函数体内外的数据交换、错误处理机制
    • 缺点:流程管理不⽅便
  • async函数
    • 优点:内置执⾏器、更好的语义、更⼴的适⽤性、返回的是Promise、结构清晰。
    • 缺点:错误处理机制

25. 说说你对promise的了解

  • 依照 Promise/A+ 的定义, Promise 有四种状态:
  • pending: 初始状态, ⾮ fulfilled 或 rejected.
  • fulfilled: 成功的操作.
  • rejected: 失败的操作.
  • settled: Promise 已被 fulfilled 或 rejected ,且不是 pending
  • 另外, fulfilled 与 rejected ⼀起合称 settled
  • Promise 对象⽤来进⾏延迟( deferred ) 和异步( asynchronous ) 计算

Promise 的构造函数

  • 构造⼀个 Promise ,最基本的⽤法如下:
  let promise = new Promise(function(resolve, reject) {
    if (...) { // succeed
        resolve(result);
    } else { // fails
        reject(Error(errMessage));
    } 
   });
  • Promise 实例拥有 then ⽅法(具有 then ⽅法的对象,通常被称为 thenable )。 它的使⽤⽅法如下:
    promise.then(onFulfilled, onRejected)
  • 接收两个函数作为参数,⼀个在 fulfilled 的时候被调⽤,⼀个在 rejected 的时候被 调⽤,接收参数就是 future , onFulfilled 对应 resolve , onRejected 对应 reject

26. 谈谈你对AMD、CMD的理解

  • CommonJS 是服务器端模块的规范, Node.js 采⽤了这个规范。 CommonJS 规范加载模 块是同步的,也就是说,只有加载完成,才能执⾏后⾯的操作。 AMD 规范则是⾮同步加载 模块,允许指定回调函数
  • AMD 推荐的⻛格通过返回⼀个对象做为模块对象, CommonJS 的⻛格通过对 module.exports 或 exports 的属性赋值来达到暴露模块对象的⽬的

es6模块 CommonJS、AMD、CMD

  • CommonJS 的规范中,每个 JavaScript ⽂件就是⼀个独⽴的模块上下⽂( module context ),在这个上下⽂中默认创建的属性都是私有的。也就是说,在⼀个⽂件定义的 变量(还包括函数和类),都是私有的,对其他⽂件是不可⻅的。
  • CommonJS 是同步加载模块,在浏览器中会出现堵塞情况,所以不适⽤
  • AMD 异步,需要定义回调 define ⽅式
  • es6 ⼀个模块就是⼀个独⽴的⽂件,该⽂件内部的所有变量,外部⽆法获取。如果你希 望外部能够读取模块内部的某个变量,就必须使⽤ export 关键字输出该变量 es6 还可 以导出类、⽅法,⾃动适⽤严格模式

27. eval是做什么的

  • 它的功能是把对应的字符串解析成 JS 代码并运⾏
  • 应该避免使⽤ eval ,不安全,⾮常耗性能( 2 次,⼀次解析成 js 语句,⼀次执⾏)
  • 由 JSON 字符串转换为JSON对象的时候可以⽤ eval,var obj =eval('('+ str +')')

28. javascript 代码中的"use strict";是什么意思

  • use strict 是⼀种 ECMAscript 5 添加的(严格)运⾏模式,这种模式使得 Javascript 在更严格的条件下运⾏,使 JS 编码更加规范化的模式,消除 Javascript 语法的⼀些不合 理、不严谨之处,减少⼀些怪异⾏为

29. js延迟加载的⽅式有哪些

  • defer 和 async 、动态创建 DOM ⽅式(⽤得最多)、按需异步载⼊ js

30. defer和async

  • defer 并⾏加载 js ⽂件,会按照⻚⾯上 script 标签的顺序执⾏
  • async 并⾏加载 js ⽂件,下载完成⽴即执⾏,不会按照⻚⾯上 script 标签的顺序执 ⾏

31. 渐进增强和优雅降级

  • 渐进增强 :针对低版本浏览器进⾏构建⻚⾯,保证最基本的功能,然后再针对⾼级浏览器 进⾏效果、交互等改进和追加功能达到更好的⽤户体验。
  • 优雅降级 :⼀开始就构建完整的功能,然后再针对低版本浏览器进⾏兼容

32. 说说严格模式的限制

  • 变量必须声明后再使⽤
  • 函数的参数不能有同名属性,否则报错
  • 不能使⽤ with 语句
  • 禁⽌ this 指向全局对象

33. 如何通过JS判断⼀个数组

  • instanceof ⽅法
    • instanceof 运算符是⽤来测试⼀个对象是否在其原型链原型构造函数的属性
    let arr = [];
    arr instanceof Array; // true
  • constructor ⽅法
    • constructor 属性返回对创建此对象的数组函数的引⽤,就是返回对象相对应的构造 函数
    let arr = [];
    arr.constructor == Array; //true
  • 最简单的⽅法 这种写法,是 jQuery 正在使⽤的
    Object.prototype.toString.call(value) == '[object Array]'
    // 利⽤这个⽅法,可以写⼀个返回数据类型的⽅法
    let isType = function (obj) {
    return Object.prototype.toString.call(obj).slice(8,-1); }   
  • ES5 新增⽅法 isArray()
    let a = new Array(123);
    let b = new Date();
    console.log(Array.isArray(a)); //true
    console.log(Array.isArray(b)); //false   

34. map与forEach的区别

  • forEach ⽅法,是最基本的⽅法,就是遍历与循环,默认有3个传参:分别是遍历的数组 内容 item 、数组索引 index 、和当前遍历数组 Array
  • map ⽅法,基本⽤法与 forEach ⼀致,但是不同的,它会返回⼀个新的数组,所以在 callback需要有 return 值,如果没有,会返回 undefined

35. JS 数组和对象的遍历⽅式,以及⼏种⽅式的⽐较

  • for in 循环
  • for 循环
  • forEach
    • 这⾥的 forEach 回调中两个参数分别为 value , index
    • forEach ⽆法遍历对象 IE不⽀持该⽅法; Firefox 和 chrome ⽀持
    • forEach ⽆法使⽤ break , continue 跳出循环,且使⽤ return 是跳过本次循 环
  • 这两种⽅法应该⾮常常⻅且使⽤很频繁。但实际上,这两种⽅法都存在性能问题
  • 在⽅式⼀中, for-in 需要分析出 array 的每个属性,这个操作性能开销很⼤。⽤在 key 已知的数组上是⾮常不划算的。所以尽量不要⽤ for-in ,除⾮你不清楚要处理哪 些属性,例如 JSON 对象这样的情况
  • 在⽅式2中,循环每进⾏⼀次,就要检查⼀下数组⻓度。读取属性(数组⻓度)要⽐读局部 变量慢,尤其是当 array ⾥存放的都是 DOM 元素,因为每次读取都会扫描⼀遍⻚⾯上 的选择器相关元素,速度会⼤⼤降低

36. 数组去重⽅法总结

⽅法⼀、利⽤ES6 Set去重(ES6中最常⽤)

    function unique (arr) {
    return Array.from(new Set(arr)) }
    var arr = [1,2,3,4,5,,5,4,3,2,1]
    console.log(unique(arr))
    //[1, 2, 3, 4, 5]

⽅法⼆、利⽤for嵌套for,然后splice去重(ES5中最常⽤)

双层循环,外层循环元素,内层循环时⽐较值。值相同时,则删去这个值。

    function unique(arr){
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){ //第⼀个等同于第⼆个,splice⽅法删除
                     arr.splice(j,1);
                     j--; 
                } 
            } 
        }
        return arr; 
    }

⽅法三、利⽤indexOf去重

新建⼀个空的结果数组, for 循环原数组,判断结果数组是否存在当前元 素,如果有相同的值则跳过,不相同则 push 进数组

    function unique(arr) {
        if (!Array.isArray(arr)) {
         console.log('type error!')
         return
        }
        var array = [];
        for (var i = 0; i < arr.length; i++) {
            if (array .indexOf(arr[i]) === -1) {
             array .push(arr[i]) 
            } 
        }
         return array; 
    }

⽅法四、利⽤sort()

利⽤ sort() 排序⽅法,然后根据排序后的结果进⾏遍历及相邻元素⽐对

    function unique(arr) {
        if (!Array.isArray(arr)) {
          console.log('type error!')
          return; 
        }
         arr = arr.sort()
        var arrry= [arr[0]];
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] !== arr[i-1]) {
             arrry.push(arr[i]); } 
        }
        return arrry; 
    }

⽅法五、利⽤对象的属性不能相同的特点进⾏去重

    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var arrry= [];
        var obj = {};
        for (var i = 0; i < arr.length; i++) {
            if (!obj[arr[i]]) {
             arrry.push(arr[i])
             obj[arr[i]] = 1 } else {
             obj[arr[i]]++
            } 
        }
        return arrry; 
    }

⽅法六、利⽤includes

    function unique(arr) {
        if (!Array.isArray(arr)) {
             console.log('type error!')
             return
        }
        var array =[];
        for(var i = 0; i < arr.length; i++) {
        if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
            array.push(arr[i]); } 
        }
        return array
    }

⽅法七、利⽤hasOwnProperty

利⽤ hasOwnProperty 判断是否存在对象属性

    function unique(arr) {
        var obj = {};
        return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof
        }) 
    }

⽅法⼋、利⽤filter

    function unique(arr) {
        return arr.filter(function(item, index, arr) {
        //当前元素,在原始数组中的第⼀个索引==当前索引值,否则返回当前元素
        return arr.indexOf(item, 0) === index; }); 
    }

⽅法九、利⽤递归去重

    function unique(arr) {
        var array= arr;
        var len = array.length;
        array.sort(function(a,b){ //排序后更加⽅便去重
            return a - b; 
        })
        function loop(index){
            if(index >= 1){
                if(array[index] === array[index-1]){
                 array.splice(index,1); }
                loop(index - 1); //递归loop,然后数组去重 
              } 
            }
        loop(len-1);
        return array; 
    }

⽅法⼗、利⽤Map数据结构去重

创建⼀个空 Map 数据结构,遍历需要去重的数组,把数组的每⼀个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就 是去重后的结果

    function arrayNonRepeatfy(arr) {
        let map = new Map();
        let array = new Array(); // 数组⽤于返回结果
        for (let i = 0; i < arr.length; i++) {
            if(map .has(arr[i])) { // 如果有该key值
                map .set(arr[i], true); 
            } else {
                map .set(arr[i], false); // 如果没有该key值
                array .push(arr[i]); 
            } 
        }
        return array ; 
    }

⽅法⼗⼀、利⽤reduce+includes

    function unique(arr){
        return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cu
    }

⽅法⼗⼆、[...new Set(arr)]

    [...new Set(arr)]
    //代码就是这么少----(其实,严格来说并不算是⼀种,相对于第⼀种⽅法来说只是简化了代码)

37. 深浅拷⻉

浅拷⻉

  • Object.assign
  • 或者展开运算符

深拷⻉

  • 可以通过 JSON.parse(JSON.stringify(object)) 来解决
  • 该⽅法也是有局限性的
    • 会忽略 undefined
    • 不能序列化函数
    • 不能解决循环引⽤的对象

38. 防抖/节流

防抖和节流本质是不⼀样的。

  • 防抖 是将多次执⾏变为最后⼀次执⾏
  • 节流 是将多次执⾏变成每隔⼀段时间执⾏

39. 谈谈变量提升?

  • 这是因为函数和变量提升的原因
  • 通常提升的解释是说将声明的代码移动到了顶部, 这其实没有什么错误,便于⼤家理解。
  • 但是更准确的解释应该是:在⽣成执⾏环境时,会有两个阶段。
    • 第⼀个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函 数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存⼊内存中,变量 只声明并且赋值为 undefined
    • 所以在第⼆个阶段,也就是代码执⾏阶段,我们可 以直接提前使⽤
  • 在提升的过程中,相同的函数会覆盖上⼀个函数,并且函数优先于变量提升

40. let var const

let

  • 允许你声明⼀个作⽤域被限制在块级中的变量、语句或者表达式
  • let绑定不受变量提升的约束,这意味着let声明不会被提升到当前
  • 该变量处于从块开始到初始化处理的“暂存死区”

var

  • 声明变量的作⽤域限制在其声明位置的上下⽂中,⽽⾮声明变量总是全局的
  • 由于变量声明(以及其他声明)总是在任意代码执⾏之前处理的,所以在代码中的任意位 置声明变量总是等效于在代码开头声明

const

  • 声明创建⼀个值的只读引⽤ (即指针)
  • 基本数据当值发⽣改变时,那么其对应的指针也将发⽣改变,故造成 const 申明基本数 据类型时
  • 再将其值改变时,将会造成报错, 例如 jsconst a = 3 ; a = 5 时 将会报错
  • 但是如果是复合类型时,如果只改变复合类型的其中某个 Value 项时, 将还是正常使⽤

41. 怎么判断两个对象相等?

可以转换为字符串来判断

    JSON.stringify(obj1)==JSON.stringify(obj2);//true or false

42. 谈⼀谈箭头函数与普通函数的区别?

  • 函数体内的 this 对象,就是定义时所在的对象,⽽不是使⽤时所在的对象
  • 不可以当作构造函数,也就是说,不可以使⽤ new 命令,否则会抛出⼀个错误
  • 不可以使⽤ arguments 对象,该对象在函数体内不存在。如果要⽤,可以⽤ Rest 参数 代替
  • 不可以使⽤ yield 命令,因此箭头函数不能⽤作 Generator 函数

43. 谈⼀谈你理解的函数式编程

  • 简单说,"函数式编程"是⼀种"编程范式"(programming paradigm),也就是如何编写程 序的⽅法论
  • 它具有以下特性:闭包和⾼阶函数、惰性计算、递归、函数是"第⼀等公⺠"、只⽤"表达式"

44. ⽤过哪些设计模式?

  • ⼯⼚模式:
    • ⼯⼚模式解决了重复实例化的问题,但还有⼀个问题,那就是识别问题,因为根本⽆法
    • 主要好处就是可以消除对象间的耦合,通过使⽤⼯程⽅法⽽不是 new 关键字
  • 构造函数模式
    • 使⽤构造函数的⽅法,即解决了重复实例化的问题,⼜解决了对象识别的问题,该模式 与⼯⼚模式的不同之处在于
    • 直接将属性和⽅法赋值给 this 对象;

Web 相关篇

1. 对web标准、可⽤性、可访问性的理解

  • 可⽤性(Usability):产品是否容易上⼿,⽤户能否完成任务,效率如何,以及这过程中 ⽤户的主观感受可好,是从⽤户的⻆度来看产品的质量。可⽤性好意味着产品质量⾼,是 企业的核⼼竞争⼒
  • 可访问性(Accessibility):Web内容对于残障⽤户的可阅读和可理解性
  • 可维护性(Maintainability):⼀般包含两个层次,⼀是当系统出现问题时,快速定位并解 决问题的成本,成本低则可维护性好。⼆是代码是否容易被⼈理解,是否容易修改和增强 功能。

2. 说说从输⼊URL到看到⻚⾯发⽣的全过程,越详细越好

  • ⾸先浏览器主进程接管,开了⼀个下载线程
  • 然后进⾏HTTP请求(DNS查询、IP寻址等等),中间会有三次捂⼿,等待响应,开始下载 响应报⽂
  • 将下载完的内容转交给Renderer进程管理
  • Renderer进程开始解析css rule tree和dom tree,这两个过程是并⾏的,所以⼀般我会把 link标签放在⻚⾯顶部
  • 解析绘制过程中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内 容,遇到时候缓存的使⽤缓存,不适⽤缓存的重新下载资源
  • css rule tree和dom tree⽣成完了之后,开始合成render tree,这个时候浏览器会进⾏ layout,开始计算每⼀个节点的位置,然后进⾏绘制
  • 绘制结束后,关闭TCP连接,过程有四次挥⼿

3.说⼀下浏览器的缓存机制

浏览器缓存机制有两种,⼀种为强缓存,⼀种为协商缓存

  • 对于强缓存,浏览器在第⼀次请求的时候,会直接下载资源,然后缓存在本地,第⼆次请 求的时候,直接使⽤缓存
  • 对于协商缓存,第⼀次请求缓存且保存缓存标识与时间,重复请求向服务器发送缓存标识 和最后缓存时间,服务端进⾏校验,如果失效则使⽤缓存

协商缓存相关设置

  • Exprires :服务端的响应头,第⼀次请求的时候,告诉客户端,该资源什么时候会过 期。 Exprires 的缺陷是必须保证服务端时间和客户端时间严格同步。
  • Cache-control:max-age :表示该资源多少时间后过期,解决了客户端和服务端时间必 须同步的问题
  • If-None-Match/ETag :缓存标识,对⽐缓存时使⽤它来标识⼀个缓存,第⼀次请求的时 候,服务端会返回该标识给客户端,客户端在第⼆次请求的时候会带上该标识与服务端进 ⾏对⽐并返回 If-None-Match 标识是否表示匹配
  • Last-modified/If-Modified-Since :第⼀次请求的时候服务端返回 Last-modified 表明请求的资源上次的修改时间,第⼆次请求的时候客户端带上请求头 If-ModifiedSince ,表示资源上次的修改时间,服务端拿到这两个字段进⾏对⽐

4. HTTP状态码及其含义

  • 1XX :信息状态码
  • 100 Continue 继续,⼀般在发送 post 请求时,已发送了 http header 之后服务端
  • 将返回此信息,表示确认,之后发送具体参数信息
  • 2XX :成功状态码
  • 200 OK 正常返回信息
  • 201 Created 请求成功并且服务器创建了新的资源
  • 202 Accepted 服务器已接受请求,但尚未处理
  • 3XX :重定向
  • 301 Moved Permanently 请求的⽹⻚已永久移动到新位置。
  • 302 Found 临时性重定向。
  • 303 See Other 临时性重定向,且总是使⽤ GET 请求新的 URI 。
  • 304 Not Modified ⾃从上次请求后,请求的⽹⻚未修改过。
  • 4XX :客户端错误
  • 400 Bad Request 服务器⽆法理解请求的格式,客户端不应当尝试再次使⽤相同的内 容发起请求。
  • 401 Unauthorized 请求未授权。
  • 403 Forbidden 禁⽌访问。
  • 404 Not Found 找不到如何与 URI 相匹配的资源。
  • 5XX: 服务器错误
  • 500 Internal Server Error 最常⻅的服务器端错误。
  • 503 Service Unavailable 服务器端暂时⽆法处理请求(可能是过载或维护)。

5. 介绍⼀下你对浏览器内核的理解?

  • 主要分成两部分:渲染引擎( layout engineer 或 Rendering Engine )和 JS 引擎
  • 渲染引擎:负责取得⽹⻚的内容( HTML 、 XML 、图像等等)、整理讯息(例如加⼊ CSS 等),以及计算⽹⻚的显示⽅式,然后会输出⾄显示器或打印机。浏览器的内核的不 同对于⽹⻚的语法解释会有不同,所以渲染的效果也不相同。所有⽹⻚浏览器、电⼦邮件 客户端以及其它需要编辑、显示⽹络内容的应⽤程序都需要内核
  • JS 引擎则:解析和执⾏ javascript 来实现⽹⻚的动态效果
  • 最开始渲染引擎和 JS 引擎并没有区分的很明确,后来JS引擎越来越独⽴,内核就倾向于 只指渲染引擎

6. 请描述⼀下 cookies , sessionStorage 和 localStorage 的区别?

  • cookie 是⽹站为了标示⽤户身份⽽储存在⽤户本地终端(Client Side)上的数据(通常 经过加密)
  • cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回 传递
  • sessionStorage 和 localStorage 不会⾃动把数据发给服务器,仅在本地保存
  • 存储⼤⼩:
    • cookie 数据⼤⼩不能超过4k
    • sessionStorage 和 localStorage 虽然也有存储⼤⼩的限制,但⽐ cookie ⼤得 多,可以达到5M或更⼤
  • 有期时间:
    • localStorage 存储持久数据,浏览器关闭后数据不丢失除⾮主动删除数据
    • sessionStorage 数据在当前浏览器窗⼝关闭后⾃动删除
    • cookie 设置的 cookie 过期时间之前⼀直有效,即使窗⼝或浏览器关闭

7. 为什么利⽤多个域名来存储⽹站资源会更有效?

  • CDN 缓存更⽅便
  • 突破浏览器并发限制
  • 节约 cookie 带宽
  • 节约主域名的连接数,优化⻚⾯响应速度
  • 防⽌不必要的安全问题

8. 重绘和回流(重排)是什么,如何避免?

  • DOM的变化影响到了元素的⼏何属性(宽⾼),浏览器重新计算元素的⼏何属性,其他元素 的⼏何
  • 属性和位置也会受到影响,浏览器需要重新构造渲染树,这个过程称为重排,浏览器将受 到影响的部分
  • 重新绘制到屏幕上的过程称为重绘。引起重排的原因有
    • 添加或者删除可⻅的DOM元素,
    • 元素位置、尺⼨、内容改变,
    • 浏览器⻚⾯初始化
    • 浏览器窗⼝尺⼨改变,重排⼀定重绘,重绘不⼀定重排

减少重绘和重排的⽅法:

  • 不在布局信息改变时做 DOM 查询
  • 使⽤ cssText 或者 className ⼀次性改变属性
  • 使⽤ fragment
  • 对于多次重排的元素,如动画,使⽤绝对定位脱离⽂档流,让他的改变不影响到其他元素

9. 常⻅web安全及防护原理

  • sql 注⼊原理
    • 就是通过把 SQL 命令插⼊到 Web 表单递交或输⼊域名或⻚⾯请求的查询字符串,最终 达到欺骗服务器执⾏恶意的SQL命令
  • 总的来说有以下⼏点
    • 永远不要信任⽤户的输⼊,要对⽤户的输⼊进⾏校验,可以通过正则表达式,或限制⻓ 度,对单引号和双 "-" 进⾏转换等 永远不要使⽤动态拼装SQL,可以使⽤参数化的 SQL 或者直接使⽤存储过程进⾏数据查询存取
    • 永远不要使⽤管理员权限的数据库连接,为每个应⽤使⽤单独的权限有限的数据库连接
    • 不要把机密信息明⽂存放,请加密或者 hash 掉密码和敏感的信息

XSS原理及防范

  • Xss(cross-site scripting) 攻击指的是攻击者往 Web ⻚⾯⾥插⼊恶意 html 标签或 者 javascript 代码。⽐如:攻击者在论坛中放⼀个看似安全的链接,骗取⽤户点击后, 窃取 cookie 中的⽤户私密信息;或者攻击者在论坛中加⼀个恶意表单,当⽤户提交表单 的时候,却把信息传送到攻击者的服务器中,⽽不是⽤户原本以为的信任站点

XSS防范⽅法

  • ⾸先代码⾥对⽤户输⼊的地⽅和变量都需要仔细检查⻓度和对 ”<”,”>”,”;”,”’” 等字符 做过滤;其次任何内容写到⻚⾯之前都必须加以encode,避免不⼩⼼把 html tag 弄出 来。这⼀个层⾯做好,⾄少可以堵住超过⼀半的XSS 攻击

XSS与CSRF有什么区别吗?

  • XSS 是获取信息,不需要提前知道其他⽤户⻚⾯的代码和数据包。 CSRF 是代替⽤户完成 指定的动作,需要知道其他⽤户⻚⾯的代码和数据包。要完成⼀次 CSRF 攻击,受害者必 须依次完成两个步骤
  • 登录受信任⽹站 A ,并在本地⽣成 Cookie
  • 在不登出 A 的情况下,访问危险⽹站 B

CSRF的防御

  • 服务端的 CSRF ⽅式⽅法很多样,但总的思想都是⼀致的,就是在客户端⻚⾯增加伪随机数
  • 通过验证码的⽅法

10. WebSocket

由于 http 存在⼀个明显的弊端(消息只能有客户端推送到服务器端,⽽服 务器端不能主动推送到客户端),导致如果服务器如果有连续的变化,这时只 能使⽤轮询,⽽轮询效率过低,并不适合。于是 WebSocket 被发明出来

相⽐与 http 具有以下有点

  • ⽀持双向通信,实时性更强;
  • 可以发送⽂本,也可以⼆进制⽂件;
  • 协议标识符是 ws ,加密后是 wss ;
  • 较少的控制开销。连接创建后, ws 客户端、服务端进⾏数据交换时,协议控制的数据包 头部较⼩。在不包含头部的情况下,服务端到客户端的包头只有 2~10 字节(取决于数据 包⻓度),客户端到服务端的的话,需要加上额外的4字节的掩码。⽽ HTTP 协议每次通信 都需要携带完整的头部;
  • ⽀持扩展。ws协议定义了扩展,⽤户可以扩展协议,或者实现⾃定义的⼦协议。(⽐如⽀ 持⾃定义压缩算法等)
  • ⽆跨域问题。

11. ajax、axios、fetch区别 jQuery ajax

jQuery ajax

优缺点:

  • 本身是针对 MVC 的编程,不符合现在前端 MVVM 的浪潮
  • 基于原⽣的 XHR 开发, XHR 本身的架构不清晰,已经有了 fetch 的替代⽅案
  • JQuery 整个项⽬太⼤,单纯使⽤ ajax 却要引⼊整个 JQuery ⾮常的不合理(采取个性 化打包的⽅案⼜不能享受CDN服务)

axios 优缺点:

  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求 ⽀持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • ⾃动转换 JSON 数据
  • 客户端⽀持防⽌ CSRF/XSRF

fetch

优缺点:

  • fetcht 只对⽹络请求报错,对 400 , 500 都当做成功的请求,需要封装去处理
  • fetch 默认不会带 cookie ,需要添加配置项
  • fetch 不⽀持 abort ,不⽀持超时控制,使⽤ setTimeout 及 Promise.reject 的实 现的超时控制并不能阻⽌请求过程继续在后台运⾏,造成了量的浪费
  • fetch 没有办法原⽣监测请求的进度,⽽XHR可以

12. 项⽬做过哪些性能优化?

  • 减少 HTTP 请求数
  • 减少 DNS 查询
  • 使⽤ CDN
  • 避免重定向
  • 图⽚懒加载
  • 减少 DOM 元素数量
  • 减少 DOM 操作
  • 使⽤外部 JavaScript 和 CSS
  • 压缩 JavaScript 、 CSS 、字体、图⽚等
  • 优化 CSS Sprite
  • 使⽤ iconfont
  • 字体裁剪
  • 多域名分发划分内容到不同域名
  • 尽量减少 iframe 使⽤
  • 避免图⽚ src 为空
  • 把样式表放在 link 中
  • 把 JavaScript 放在⻚⾯底部

13. 负载均衡

多台服务器共同协作,不让其中某⼀台或⼏台超额⼯作,发挥服务器的最⼤作 ⽤

  • http 重定向负载均衡:调度者根据策略选择服务器以302响应请求,缺点只有第⼀次有 效果,后续操作维持在该服务器 dns负载均衡:解析域名时,访问多个 ip 服务器中的⼀ 个(可监控性较弱)
  • 反向代理负载均衡:访问统⼀的服务器,由服务器进⾏调度访问实际的某个服务器,对统 ⼀的服务器要求⼤,性能受到 服务器群的数量

14. CDN

内容分发⽹络,基本思路是尽可能避开互联⽹上有可能影响数据传输速度和稳 定性的瓶颈和环节,使内容传输的更快、更稳定。

15. babel原理

ES6、7 代码输⼊ -> babylon 进⾏解析 -> 得到 AST (抽象语法树)-> plugin ⽤babel-traverse 对 AST 树进⾏遍历转译 ->得到新的 AST 树-> ⽤ babel-generator 通过 AST 树⽣成 ES5 代码

16. 谈谈你对重构的理解

  • ⽹站重构:在不改变外部⾏为的前提下,简化结构、添加可读性,⽽在⽹站前端保持⼀致 的⾏为。也就是说是在不改变UI的情况下,对⽹站进⾏优化, 在扩展的同时保持⼀致的UI
  • 对于传统的⽹站来说重构通常是:
    • 表格( table )布局改为 DIV+CSS
    • 使⽹站前端兼容于现代浏览器(针对于不合规范的 CSS 、如对IE6有效的)
    • 对于移动平台的优化
    • 针对于 SEO 进⾏优化

结束篇

如果觉得本文对你修炼有用,可以点个 star。