借用神剧《权游》的名句:“凛冬将至!”,互联网寒冬已然来临,这一年以来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。