JavaScript面试题汇总
1.display:none 与 visibility:hidden 的区别是什么?
display:none和visibility:hidden都可以实现元素在视窗的不可见,
display:none不会保留元素的位置,结构发生了改变,所以触发了回流和重绘;
visibility:hidden会保留元素的位置,结构没有发生改变,所以只是触发了重绘。
2. new 的原理是什么?通过 new 的方式创建对象和通过字面量创建有什么区别?
new的过程主要完成了以下操作
- 创建一个空对象
- 获取构造函数
- 设置空对象的原型
- 绑定 this 并执行构造函数
- 确保返回值为对象
完成以上步骤,我们可以自己模拟一个new
通过new创建的对象和通过字面量创建的对象本质上是一样的,字面量内部也是使用new来完成的。但是通过字面量的方式比较简单,也不用调用构造函数,性能较好,所以推荐使用字面量的方式。
3.说一下call、apply、bind的区别
首先,call,apply,bind都是用于改变this指向的
它们的区别主要有两点,call和apply是立即调用的,bind是返回一个新函数;
另一个区别就是传递参数的不同。call传递参数时,从第二个参数开始依次进行传递;apply传递参数时,第二个参数是一个数组,数组里面的每一项依次是向函数传递的参数;bind可以在使用时直接传递参数,也可以在 新函数调用时传递参数。
4.什么是闭包?闭包的应用场景?
函数的返回结果是一个内部函数,并且被外部变量引用,如果内部函数引用了外部函数中定义的变量,就形成了闭包。闭包可以在内部函数访问到外部函数作用域,使用闭包可以将函数中的变量存储在内存之中,保护变量不会被污染,但是使用闭包有可能会导致内存泄漏,不能滥用。
闭包的应用场景:
- 进行模块封装,用闭包的方式可以防止变量污染全局
- 在循环中创建闭包,防止取到意外的值 如果我们要用循环给多个元素绑定事件,函数执行后引用的是同一个i,使用闭包就可以防止这样的问题出现。
5.什么是ts?为什么要使用ts?
TypeScript是一种面向对象的编程语言。它是JavaScript的超集。
typescript可以约束我们的编码习惯,还能起到注释的作用。比如当我们看到一函数后我们立马就能知道这个函数的用法,需要传什么值,返回值是什么类型一目了然,对大型项目的维护性有很大的提升。
6.简述原生js发送ajax请求的步骤
- 创建ajax对象(存在兼容问题)
- 配置请求信息(用来明确请求方式、确定请求地址)
- 发送请求
- 接受响应(存在兼容问题)
get请求的发送:
post请求的发送:
7.简述浏览器强缓存和协商缓存
当客户端需要请求一个服务端资源的时候,会率先检查浏览器中是否有缓存,这时的检查其实就是在判断是否有强缓存。
强缓存可以通过设置两种 HTTP Header 实现:Expires和 Cache-Control。
1. Expires
Expires是服务端返回的响应头,设置的是一个具体到年月日时分秒的时间,如果客户端此次发送请求的时间在Expires之前,则会直接触发缓存,不再去发起请求。
1. Cache-Control
Cache-Control同样是服务端返回的一个响应头,实际开发中常设置其中的max-age。设置的是相对时间,单位为秒。如果客户端此次发送请求的时间在max-age之内,则会直接触发缓存,不再去发起请求。
协商缓存
协商缓存就是客户端在没有匹配到强缓存的前提下,向服务端发起了请求,而服务端则会使用两种方式来判断。
1. Last Modified 与 If-Modified-Since
服务端在上一次响应客户端响应时,会返回一个Lase Modified的响应头,对应的值是一个该文件最后一次修改时间。
当客户端再一次请求该资源时,浏览器会自动为请求添加If-Modified-Since请求头,且该请求头携带上一次Last-Modified的值。
服务端接收到If-Modified-Since请求头后,会和服务端所储存的该资源最后修改时间作对比,如果没有任何变化,服务端会响应304,客户端就会直接从缓存获取数据。
1. Etag与If-None-Match
Etag是服务端根据请求资源的内容所生成的特殊标识,服务端下次发起对该资源的请求时,请求头会携带If-None-Match字段,该字段携带着上次服务端返回的Etag的值.
服务端接收到If-None-Match之后,会和该资源的标识进行对比,如果相同则认为资源未发生变化,响应304,客户端自动使用资源缓存。
8.和=什么区别
对于 == 来说,如果对比双方的类型不一样,就会进行类型转换 ,===:用来检测两个操作数是否严格相等,不会进行类型转换
- 首先会判断两者类型是否相同。相同的话就是比大小了
- 类型不相同的话,那么就会进行类型转换
- 会先判断是否在对比 null和 undefined,是的话就会返回 true
- 判断两者类型是否为 string和 number,是的话就会将字符串转换为 number
- 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
- 判断其中一方是否为 object 且另一方为 string、number或者 symbol,是的话就会,则调用对象的valueOf()方法。如果对象没有valueOf()方法,则调用 toString(),把 object 转为原始类型再进行判断
9.ts和js的区别
Typescript 是 JavaScript 的超集,可以被编译成 JavaScript 代码。
用 JavaScript 编写的合法代码,在 TypeScript 中依然有效。
Typescript 是纯面向对象的编程语言,包含类和接口的概念。
程序员可以用它来编写面向对象的服务端或客户端程序,并将它们编译成 JavaScript 代码。
TypeScript 引入了很多面向对象程序设计的特征,包括:
- interfaces 接口
- classes 类
- enumerated types 枚举类型
- generics 泛型
- modules 模块
主要不同点如下:
- TS 是一种面向对象编程语言,而 JS 是一种脚本语言(尽管 JS 是基于对象的)。
- TS 支持可选参数, JS 则不支持该特性。
- TS 支持静态类型,JS 不支持。
- TS 支持接口,JS 不支持接口。
10.‘true’==true的返回结果是什么?为什么?(相等运算符在计算的时候遵循的规则是什么?)
答案:
返回结果是false。
分析:
首先,相等运算符在比较不同的数据类型之前会强制转换。
在转换不同的数据类型时,相等运算符遵循下列基本规则:
- 如果有一边是布尔,会在比较之前将其转换为数值,false --> 0,true --> 1;
- 如果有一边是字符串,另一边是数值,在比较之前先将字符串转换为数值;
- 如果有一边是对象,另一边不是,则调用对象的valueOf()方法,用得到的基本类型按照前两条规则进行比较。
那么,‘true’==true,根据规则1,就转换为’true’==1,再根据规则2,将字符串’true’转换为数值,转换为数值会调用Number()函数,Number()函数转换规则如下:
-
如果是Boolean值,true和false将分别被转换为1和0;
-
如果是数字值,只是简单的传入和返回;
-
如果是null值,返回0;
-
如果是undefined,返回NaN;
-
如果是字符串,遵循下列规则:
(1)如果字符串只包含数字(包括前面带加号或负号的情况),则将其转换为十进制数值,即'1'会变成1,'111'会变成111,'01234'会变成1234,这里的前面的0会被省略; (2)如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,如果前面有0,如02.1,也会忽略前面的0); (3)如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值; (4)如果字符串是空的(不包含任何字符),则将其转换为0; (5)如果字符串中包含除上述格式之外的字符,则将其转换为NaN。
那么,按照规则5(5),'true’会被转换为NaN,最后就变为对NaN==1进行判断求值,又有NaN与任何值都不相等,包括NaN本身,所以最终的结果为false。
整个判断流程为:‘true’ == true --> ‘true’ == 1 --> NaN == 1 --> false。
11.Promise为什么可以实现链式调用,以及链式调用的执行顺序?
Promise为什么可以实现链式调用?
promise 的 then/catch 方法执行后会也返回一个 promise。
链式调用的执行顺序?
关于执行顺序,这里有两个结论:
1、当执行 then 方法时,如果前面的 promise 已经是 resolved 状态,则直接将回调放入微任务队列中;
2、当一个 promise 被 resolve 时,会遍历之前通过 then 给这个 promise 注册的所有回调,将它们依次放入微任务队列中。
关于结论1:
执行then方法是同步的,而then中的回调是异步的,同步代码执行时,会先将then中的回调先放入微任务队列,等同步任务执行完毕后,再依次取出执行,同时,在同步执行then方法时,会进行判断:
1、如果前面的 promise 已经是 resolved 状态,则会立即将回调推入微任务队列(但是执行回调还是要等到所有同步任务都结束后);
2、如果前面的 promise 是 pending 状态则会将回调存储在 promise 的内部(promse内部的一个数组中),一直等到 promise 被 resolve 才将回调推入微任务队列。
关于结论2:
对于会遍历之前通过 then 给这个 promise 注册的所有回调这句话的理解,来考虑一种情景,假设实例化一个promise,该promise被延迟resolve(比如将resolve的调用放入setTimeout函数中,设置延迟时间为3秒),但因为then方法是同步的,那么,在这个promise被resolve之前,可能已经通过then方法注册了几个回调函数,那么这几个回调函数不会被执行,也不会被放入到微任务队列中,它们会被这个promise内部储存起来,等到这个promise被resolve后,才会对之前储存的这几个回调函数进行遍历依次推入微任务队列(这里先存入的回调函数就先推入微任务队列),此时如果没有同步任务就会逐个取出再执行。
这里需要注意的点:
1、对于普通的 promise 来说,当执行完 resolve 函数时,promise 状态就为 resolved;
2、对于 then 方法返回的 promise 它是没有 resolve 函数的,取而代之只要 then 中回调函数代码执行完毕,这个 then 返回的 promise 就算被 resolve。
(原文参考链接:https://mp.weixin.qq.com/s/VfP9_8kUx6hyrII7LVae6g)
12.简单说一下es6装饰器,在react中,装饰器可以用来装饰函数组件吗?
介绍:
装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。装饰器是一种函数,写成@ + 函数名,它可以放在类和类方法的定义前面。装饰器函数的参数就是被修饰的类和类方法本身。如果觉得一个参数不够用,可以在装饰器外面在封装一层函数,该函数再返回一个函数,用来接收类和类方法。
在react中,装饰器可以用来装饰函数组件吗?
答案:
不能。
分析:
装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
react函数组件本身是个函数,函数是存在函数提升的。在代码编译阶段,函数被定义,装饰器开始执行,但由于函数提升,函数体还没有任何内容,所以装饰器也就起不到他应有的装饰作用(也就是增强或者扩展函数功能)。(感觉这段之前的描述不太准确,故作修改。— 20.09.02改)
13.平时你在开发的时候是怎样的前后端联调的,怎么去上线的,上线出现bug你是怎么处理的?
首先前端后端正式进入联调的时候我们需要使用后端给我们提供的真实接口地址进行测试,所以这个时候在我们的项目中将之前的假的接口地址给替换成真实的接口, 所以在一开始的时候我们需要把接口的配置放在一个配置文件之中,这样方便我们好调试,你也可以使用本地配置host的方式来达到真实环境下的不同域名请求的接口的调试,在这个过程中可能出现后端的接口不能满足前端之前编写的代码的数据格式,这个时候你可以根据具体情况前端进行处理或者让后端帮你在改一下接口的数据格式,也难免不了数据缺少这样的情况,也有可能是产生真实情况下域名产生跨域问题,这个时候你需要使用具体的方案去解决这个问题,比如jsonp跨域,服务器代理,后端cros处理。联调成功之后呢我们需要对代码提审然后进行上线了。
将代码交给你自的组长或者leader由他们进行上线,一般上线需要运维工程师的参与。但是上线可能会出现一系列的问题,比如测试环境中代码正常运行,一旦上线之后用户使用时,出现了bug,我们如何处理,这个时候我们需要代码回滚,但这个时候代码回滚又会出现前后端接口不一致的问题,怎么去解决。两个思路如果bug轻微我们可以暂时已提示的方式告诉用户请重试这样的话,然后我们需要对代码重新进行review和排除bug。如果bug问题较为严重,我们需要对该模块的直接暂停功能,代码进行回滚,重新审查一遍,修复bug。后端公司内部人员使用的项目可以不需要考虑这么多,等待bug修复完成之后然后把补丁打上去就行了。一般在公司会有专门的团队出一套关于bug紧急修复的方案,为了应对上线的项目出现bug的问题。
14.什么是Typescript?和js的区别
TypeScript 是 JavaScript 的一个超集
- TypeScript 可以使用 JavaScript 中的所有代码和编码概念,TypeScript 是为了使 JavaScript 的开发变得更加容易而创建的。例如,TypeScript 使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序
- TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。
- JavaScript 代码可以在无需任何修改的情况下与 TypeScript 一同工作,同时可以使用编译器将 TypeScript 代码转换为 JavaScript。
- TypeScript 通过类型注解提供编译时的静态类型检查。
- TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
- TypeScript 为函数提供了缺省参数值。
- TypeScript 引入了 JavaScript 中没有的“类”概念。
- TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
https://my.oschina.net/u/3991187/blog/4415774
15.js为什么要延迟加载,有哪些方法可以实现延迟加载
原因:js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
我了解到的几种方式是:
- 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
- 给 js 脚本添加 defer属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- 给 js 脚本添加 async属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
16.箭头函数和普通函数的区别
1.箭头函数是匿名函数,不能作为构造函数,不能使用new
2.箭头函数不绑定arguments,取而代之用rest参数...解决
3.箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
4.箭头函数不能改变this指向
5.箭头函数没有原型属性
6.箭头函数不能当做Generator函数,不能使用yield关键字
17.说说你对H5语义化标签的理解
用正确的标签做正确的事情。
html语义化让页面的内容结构化,结构更清晰,便于对浏览器,搜索引擎解析;即使在没有css样式的情况下,也以一种文档格式显示,并且是容易阅读的;搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO;使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
尽可能少的使用无语义的标签div和span在语义不明显时,既可以使用div或者时,尽量用p,因为p在默认情况下有上下间距,对兼容特殊终端有利。
不要使用纯样式的标签,如:b,font,u等,改用css设置。
表单域要用fieldset标签包起来,并用legend标签说明标签的用途。
每个input标签对应的文本框都需要使用label标签,并且通过为input设置id属性,在label标签中设置for=someID来说明文本和相对应的input关联起来
18. 如何优化网站性能
1. 减少 HTTP 请求数量
在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数),一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的请求数量可以很大程度上对网站性能进行优化。
1. 控制资源文件加载优先级
浏览器在加载HTML内容时,是将HTML内容从上至下依次解析,解析到link或者script标签就会加载href或者src对应链接内容,为了第一时间展示页面给用户,就需要将CSS提前加载,不要受 JS 加载影响。一般情况下都是CSS在头部,JS在底部。
1. 利用浏览器缓存
浏览器缓存是将网络资源存储在本地,等待下次该资源,直请求该资源时,如果资源已经存在就不需要到服务器重新请求接在本地读取该资源。
1. 减少重排(Reflow)
基本原理:重排是DOM的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证 DOM 树上的所有其它结点的visibility属性,这也是Reflow低效的原因。如果Reflow的过于频繁,CPU使用率就会急剧上升。
1. 减少 DOM 操作
19.Bom对象有哪些,列举window对象
- window对象 ,是JS的最顶层对象,其他的BOM对象都是window对象的属性;
- document对象,文档对象;
- location对象,浏览器当前URL信息;
- navigator对象,浏览器本身信息;
- screen对象,客户端屏幕信息;
- history对象,浏览器访问历史信息;
20.src和href的区别
它们之间的主要区别可以用这样一句话来概括:src用于替代这个元素,而href用于建立这个标签与外部资源之间的关系。
href (Hypertext Reference) 超文本引用href这个属性指定web资源的位置,从而定义当前元素(如锚点a)或当前文档(如链接)与目标锚点或目标资源之间的联系。
例如当我们写:
<link href="style.css" rel="stylesheet" />
浏览器知道这是个样式表文件,html的解析和渲染不会暂停,css文件的加载是同时进行的,这不同于在style标签里面的内置样式,用@import添加的样式是在页面载入之后再加载,这可能会导致页面因重新渲染而闪烁。所以我们建议使用link而不是@import。
src (Source)源这个属性是将资源嵌入到当前文档中元素所在的位置。例如当我们写:
<script src="script.js"></script>
当浏览器解析到这句代码时,页面的加载和解析都会暂停直到浏览器拿到并执行完这个js文件。这就像是把js文件里的内容全部注入到这个script标签中,类似于img,img标签是一个空标签,它的内容就是由src这个属性定义,浏览器会暂停加载直到这个图片加载完成。这也是为什么要将js文件的加载放在body最后的原因(在前面)。
21.cookie、sessionStorage和localStorage的区别
一、 概念的理解
webstorage本地存储
- webstorage是本地存储,存储在客户端,包括localStorage和sessionStorage
- localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。存放数据大小为一般为5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信
- sessionStorage仅在当前会话下有效,关闭页面或浏览器后被清除。存放数据大小为一般为5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信。源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
- WebStorage的目标
提供一种在cookie之外存储会话数据的路径
提供一种存储大量可以跨会话存在的数据的机制
HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储)
- 作用域的不同:
不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的
- 存储大小:
localStorage和sessionStorage的存储数据大小一般都是:5MB
- 存储位置:
localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信
- 存储内容类型:
localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
- 获取方式:
localStorage:window.localStorage;;sessionStorage:window.sessionStorage;
- 应用场景:
localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据,而sessionStorage:敏感账号一次性登录
- WebStorage的优点:
存储空间更大:cookie为4KB,而WebStorage是5MB
节省网络流量:WebStorage不会传送到服务器,存储在本地的数据可以直接获取,也不会像cookie一样美词请求都会传送到服务器,所以减少了客户端和服务器端的交互,节省了网络流量
对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage会非常方便
快速显示:有的数据存储在WebStorage上,再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多,所以速度更快
安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题
WebStorage提供了一些方法,数据操作比cookie方便
setItem (key, value) —— 保存数据,以键值对的方式储存信息。
getItem (key) —— 获取数据,将键值传入,即可获取到对应的value值。
removeItem (key) —— 删除单个数据,根据键值移除对应的信息。
clear () —— 删除所有的数据
key (index) —— 获取某个索引的key
cookie
- HTTP Cookie简称cookie,在HTTP请求发送Set-Cookie HTTP头作为响应的一部分。通过name=value的形式存储
- cookie的构成:
名称:name(不区分大小写,但最好认为它是区分的)
值:value(通过URL编码:encodeURIComponent)
域
路径
失效时间:一般默认是浏览器关闭失效,可以自己设置失效时间
安全标志:设置安全标志后只有SSL连接的时候才发送到服务器
- cookie的作用:主要用于保存登录信息
- 生命期为只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。 存放数据大小为4K左右 。有个数限制(各浏览器不同),一般不能超过20个。与服务器端通信:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
- cookie的优点:具有极高的扩展性和可用性
通过良好的编程,控制保存在cookie中的session对象的大小
通过加密和安全传输技术,减少cookie被破解的可能性
只有在cookie中存放不敏感的数据,即使被盗取也不会有很大的损失
控制cookie的生命期,使之不会永远有效。这样的话偷盗者很可能拿到的就 是一个过期的cookie
- cookie的缺点:
cookie的长度和数量的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉
安全性问题。如果cookie被人拦掉了,那个人就可以获取到所有session信息。加密的话也不起什么作用
有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务端保存一个计数器。若吧计数器保存在客户端,则起不到什么作用
sessionStorage
- sessionStorage是Storage类型的一个对象,拥有clear(),getItem(name),key(index),removeItem(name),setItem(name,value)方法
- sessionStorage对象存储特定于某个会话的数据,也就是该数据只保持到浏览器关闭
- 将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据
- sessionStorage为临时保存
localStorage
- localStorage也是Storage类型的一个对象
- 在HTML5中localStorage作为持久保存在客户端数据的方案取代了globalStorage(globalStorage必须指定域名)
- localStorage会永久存储会话数据,除非removeItem,否则会话数据一直存在
- 将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用
- localStorage为永久保存
图示说明:
注意:时刻注意XSS注入的风险,因为可以在控制台直接访问它们,所以不要存入敏感数据
二、区别的比较
本地储存localStorage与cookie的区别
- cookie在浏览器与服务器之间来回传递
sessionStorage和localStorage不会把数据发给服务器,仅在本地保存
- 数据有效期不同
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
sessionStorage:仅在当前浏览器窗口关闭前有效
localStorage 始终有效,长期保存
- cookie数据还有路径的概念,可以限制cookie只属于某个路径下
存储大小也不同,cookie数据不能超过4k,sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
- 作用域不用
sessionStorage不在不同的浏览器窗口中共享
localStorage在所有同源窗口中都是共享的
cookie也是在所有同源窗口中都是共享的
WebStorage 支持事件通知机制,可以将数据更新的通知发送给监听者。Web Storage 的 api 接口使用更方便
cookie、session和localStorage的区别
- cookie的内容主要包括:名字、值、过期时间、路径和域,路径与域一起构成cookie的作用范围。若不设置时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就会消失,这种生命期为浏览器会话期的cookie被称为会话cookie
- 会话cookie一般不存储在硬盘而是保存在内存里,当然这个行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再打开浏览器这些cookie仍然有效直到超过设定的过期时间。对于保存在内存里的cookie,不同的浏览器有不同的处理方式session机制。
- 当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。
cookie和session的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
- session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
- 单个cookie保存的数*据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
- 建议将登录信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中
- session保存在服务器,客户端不知道其中的信心;cookie保存在客户端,服务器能够知道其中的信息
- session中保存的是对象,cookie中保存的是字符串
- session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到,而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的
web Storage和cookie的区别
- Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的,cookie的大小是受限的,并且每次请求一个新的页面的时候cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可跨域调用
- web storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie
- 但是cookie也是不可或缺的,cookie的作用是与服务器进行交互,作为http规范的一部分而存在的,而web Storage仅仅是为了在本地“存储”数据而生
sessionStorage、localStorage、cookie都是在浏览器端存储的数据,其中sessionStorage的概念很特别,引入了一个“浏览器窗口”的概念,sessionStorage是在同源的同窗口中,始终存在的数据,也就是说只要这个浏览器窗口没有关闭,即使刷新页面或进入同源另一个页面,数据仍然存在,关闭窗口后,sessionStorage就会被销毁,同时“独立”打开的不同窗口,即使是同一页面,sessionStorage对象也是不同的
- Web Storage的好处
减少网络流量:一旦数据保存在本地之后,就可以避免再向服务器请求数据,因此减少不必要的数据请求,减少数据在浏览器和服务器间不必要的来回传递
快速显示数据:性能好,从本地读数据比通过网络从服务器上获得数据快得多,本地数据可以及时获得,再加上网页本身也可以有缓存,因此整个页面和数据都在本地的话,可以立即显示
临时存储:很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用sessionStorage非常方便
浏览器本地存储与服务器端存储的区别
- 数据既可以在浏览器本地存储,也可以在服务器端存储
- 浏览器可以保存一些数据,需要的时候直接从本地存取,sessionStorage、localStorage和cookie都是由浏览器存储在本地的数据
- 服务器端也可以保存所有用户的所有数据,但需要的时候浏览器要向服务器请求数据
- 服务器端可以保存用户的持久数据,如数据库和云存储将用户的大量数据保存在服务器端 ,服务器端也可以保存用户的临时会话数据,服务器端的session机制,如jsp的session对象,数据保存在服务器上
- 服务器和浏览器之间仅需传递session id即可,服务器根据session id找到对应用户的session对象,会话数据仅在一段时间内有效,这个时间就是server端设置的session有效期
- 服务器端保存所有的用户的数据,所以服务器端的开销较大,而浏览器端保存则把不同用户需要的数据分别保存在用户各自的浏览器中,浏览器端一般只用来存储小数据,而非服务可以存储大数据或小数据服务器存储数据安全一些,浏览器只适合存储一般数据
sessionStorage、localStorage和cookie的区别
- 相同点是都是保存在浏览器端、且同源的
- cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
- 存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
- 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
- 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
- web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
- web Storage的api接口使用更方便
sessionStorage与页面js数据对象的区别
- 页面中一般的js对象的生存期仅在当前页面有效,因此刷新页面或转到另一页面这样的重新加载页面的情况,数据就不存在了
- sessionStorage只要同源的同窗口中,刷新页面或进入同源的不同页面,数据始终存在,也就是说只要浏览器不关闭,数据仍然存在
22.解决跨域的方法
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
一、 通过jsonp跨域
通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
1.)原生实现:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.demo2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
alert(JSON.stringify(res));
}
</script>
23.js处理代码异步的几种方式(使用方法和优缺点)
一、回调函数(callback)
是啥:一个函数a被当做另一个函数b的参数,在b的函数体内去执行a
function b(callback){
callback();
}
function a(){}
b(a)
优点:能让a函数的逻辑在适当的位置才被执行
缺点:代码逻辑不清,不利于代码的阅读和维护,各个部分之间高度耦合
优化: 变成异步回调
function b(callback){
setTime(()=>{
callback();
},1000)
}
function a(){}
b(a)
二、事件监听
是啥: 当某个事件被触发时,才执行绑定在这个事件上的函数
document.on('click',fn)
优点:不同的事件能绑定不同的函数,利于后期代码模块化
缺点:同个事件只能绑定一个函数。并且整个代码都采用事件驱动,执行逻辑不是很清楚
优化: attachEvent和addEvenListener方法 (可以添加许多事件,添加的事件不会覆盖已存在的事件 )
document.addEvenListener("click",fn,false);
三、发布/订阅(观察者模式)
是啥:观察者模式类似一个外卖平台派单的过程,系统通过平台发布一个单子,外卖员一直刷着平台的消息并接收到一条单子,整个过程是一次消息的发布和订阅。
优点 :整个消息的发布和订阅都能比较清晰的被看到,代码逻辑易懂。
缺点 :发布者和订阅者如果各自对应多个时,并且各自有关联,容易造成循环调用。即使没有关联,也容易造成通知的耗时加长。
四、promise对象(模式)
是啥:JavaScript 中,所有代码都是单线程的,也就是同步执行的。而 Promise 就为异步编程提供了一种解决方案。
//一、Promise 对象是由关键字 new 及其构造函数来创建的。
const promise = new Promise((resolve, reject) => {
if (success) {
resolve(value); //成功的消息
} else {
reject(error); //失败的消息
}
});
//二、then方法处理成功和失败的回调
promise.then(function(value) {
// success
}, function(error) {
// failure
});
五、async await
是啥:async是“异步”的意思,async用于声明一个函数是异步的。await意思是“等待”,用来等待async 所声明的异步函数的执行结果,并且await只能在async函数中使用。
async function sync() {
const result= await new Promise(resolve => {
setTimeout(() => {
resolve("async await result");
}, 1000);
});
console.log(result);
}
sync()
24.简述ES6的新特性
1. const和let
let: 声明在代码块有用的变量
特点:
- 在存在变理提升(不能在变量声明之前使用)
- let的暂时性死区: 其实与1差不多,只要在块作用域有声明,就不能在本作用域声明前用主个变量。
- 不允许重复声明。
const: 声明一个只读的变量
特点: - 一但声明,这个值不能被改变(对于引用类型,是引用的地址不能被改变)
- 声明必须赋值(其中当声明的变量为引用类型如Array,如果没有直接更改引用的地址,可以使用const)
2. 解构赋值
按照一定模式从数组或对象中提取值,然后对变量进行赋值(先提取,再赋值)
3. 模板字符串
特点:
可以换行,但是所有的空格和换行会被保留。
${}中可以使用任意的javaScript表达试、运算、引用对象属性、函数调用等。结果是其返回值
4. 函数的扩展
特点: - 函数体内的this = 定义时所在的对象
- 不可以当作构造函数(不能用new)
- 不可以用arguments对像,可以用rest
- 不可以用yield命令(不能用作Generator函数)
5. 数组的扩展 - 扩展运算符
- 用于代替数组的apply
call apply bind的区别:
用于改变this的指向, 第一个参数为this指向的对像,后面的参数是作为函数的参数。
区加在于:call apply 会即调用,而bind是生成一个等调用的函数。call bind参数是一个个用逗号罗列,而apply 是传入一个数组。
6. 对象的扩展 - 属性的简写(方法名同样可以简写)
- 属性名可使用表达式
7. Object.is() - Object.assign()
- 对像的一些遍历:Object.keys(), Object.values(), Object.entries()
- 扩展运算符(常用)(es2017新增,在webpack中要另外的babel转换)
8. symbol
javascript又新增的一种数据类型(第七种,另外6种为:Undefined、Null、Boolean、String、Number、Object)
注:symbol作为对象的属性名时不会被for…in,for…of,Object.keys()识别;可以改用Reflect.ownkeys方法.
9. Set、Map
-
Set(数组去重)
特点:- 类似数组,但其成员是唯一的。
- 是一个构造函数。
-
Map(使用实例的set,get,delete方法增,查,删,也可以在new 时接受一个数组)
特点:- 为了解决javascript的对象只能用字符串作为键的问题。
10. Promise(异步编程的一种解决方案。)
特点:
- 为了解决javascript的对象只能用字符串作为键的问题。
1. 状态不受外界影响(有三种状态:padding, fulfilled,redected)
2. 一旦状态改变就不会再变。
11. class
12. Module(一种将程序拆分成一个个小模块的支持,或者说是可以将一个个小模块加入到程序中去)
在ES6的module之前,比较流行的模块加载方案有:CommonJS和AMD,前者用于服务器(node),后者用于浏览器。
区别:
1. CommondJS和AMD是运行时加载的。
2. module是编译时加载的。
3. CommondJS输出的是值的复制,而ES6输出的是值的引用
ES6模板默认使用严格模式
- 变里必须声明后再使用
- 函数的参数不能有同名属性
- 不能使用width
- 禁止this指向全局对象
13. Generator与async、await
Generator是ES6提供的一种异步编程解决方案。使异步写法更像同步
Async await是ES2017的标准,是generator的一个语法糖。
25. 什么是服务端渲染,服务端渲染的优点
服务端渲染:在早期的时候,由于页面比较简单,前后端分离还没有做的比较完善,所以当时一般页面渲染还是在服务端完成html文件的拼装,然后浏览器接收到这个文件,就可以直接解析展示。
如今前端页面的复杂性提高,前端已经不再仅仅是普通的页面展示了,现在前端页面功能更加完善,也更加复杂。同时伴随着ajax的兴起,使得现在越来越崇尚前后端分离的开发方式。后端不再提供完整的html页面,而是提供一些API使得前端可以获取需要的json数据,然后前端拿到数据后在前端完成html页面的拼装,然后展示在浏览器上,这就是客户端渲染。
优点
- 前端耗时少。因为后端拼接完了html,浏览器只需要直接渲染出来。
- 有利于SEO。因为在后端有完整的html页面,所以爬虫更容易爬取获得信息,更有利于seo。
- 无需占用客户端资源。即解析模板的工作完全交由后端来做,客户端只要解析标准的html页面即可,这样对于客户端的资源占用更少*,尤其是移动端,也可以更省电。
- 后端生成静态化文件。即生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 。
缺点
1.不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,则对于前端复杂度高的项目,不利于项目高效开发。另外,如果是服务器端渲染,则前端一般就是写一个静态html文件,然后后端再修改为模板,这样是非常低效的,并且还常常需要前后端共同完成修改的动作; 或者是前端直接完成html模板,然后交由后端。另外,如果后端改了模板,前端还需要根据改动的模板再调节css,这样使得前后端联调的时间增加。
2.占用服务器端资源。即服务器端完成html模板的解析,如果请求较多,会对服务器造成一定的访问压力。而如果使用前端渲染,就是把这些解析的压力分摊了前端,而这里确实完全交给了一个服务器
26. div+css 的布局较 table 布局有什么优点?
- 改版的时候更方便 只要改 css 文件
- 页面加载速度更快、结构化清晰、页面显示简洁
- 表现与结构相分离
- 易于优化(seo)搜索引擎更友好,排名更容易靠前
-
Es6 module和commonjs的区别
-
前者建立模块依赖关系是在运行时,后者是在编译时
-
在模块导入方面,CommonJS导入的是值拷贝,ES6 Module导入的是只读的变量映射
-
ES6 Module通过其静态特性可以进行编译过程中的优化,并且具备处理循环依赖的能力
28. 浏览器端 Event loop
- 执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去检查微任务(microTask)队列是否为空,如果为空的话,就执行Task(宏任务),否则就一次性执行完所有微任务
- 每次单个宏任务执行完毕后,检查微任务(microTask)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务(microTask)后,设置微任务(microTask)队列为null,然后再执行宏任务,如此循环
29. 什么是闭包及闭包的用途
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
- 闭包的用途
- 模块块级作用域
- 所谓块级作用域就是指在循环中定义的变量,一旦循环结束,变量也随之销毁,它的作用范围只在这一小块
- 储存变量
- 闭包的另一个特点是可以保存外部函数的变量,内部函数保留了对外部函数的活动变量的引用,所以变量不会被释放 (把一些不经常变动计算起来又比较复杂的值保存起来,节省每次的访问时间)
- 封装私有变量
- 函数当作一个范围,函数内部的变量就是私有变量,在外部无法引用,但是我们可以通过闭包的特点来访问私有变量
30. instanceof 的原理是什么?
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
思路
- 首先获取类型的原型
- 然后获得对象的原型
- 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
31. 什么是BFC,介绍一下BFC的触发方式以及特性 (块级格式化上下文)
1. 触发条件
- 根元素 html标签就是一个bfc
- float的值不为none
- overflow的值不为visible
- display的值为 inline-block/ table-cell/ table-caption/ flex/ inline-flex
- position的值为absolute或fixed
2. 特性 - Box垂直方向的距离由margin决定,属于同一个BFC的两个相邻box的margin会发生重叠 (应用:可以解释为什么高度塌陷可以用overflow:hidden等方法解决)
- 计算BFC的高度时,浮动元素也参与计算 (应用:可以解释为什么高度塌陷可以用overflow:hidden等方法解决)
- BFC的区域不会与float box发生重叠 (应用:自适应两栏布局或者三栏布局<圣杯布局和双飞翼布局>)
- BFC内部的Box会在垂直方向,一个接一个的放置
- 每个元素的margin box的左边会与包含块border box的左边相接触(对于从左到右的格式化,否则相反),即使存在浮动也会如此
- BFC就是页面上的一个独立容器,容器里面的元素不会影响到外面的元素
32. 网页从输入网址到渲染完成经历了哪些过程?
- 输入网址
- 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址
- 与web服务器建立TCP连接
- 浏览器向web服务器发送http请求
- web服务器响应请求,并返回指定url的数据
- 浏览器下载web服务器返回的数据及解析html源文件
- 生成DOM树,解析css和js,渲染页面,直至显示完成
33. TCP 三次握手四次挥手
1. 三次握手
三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP窗口大小信息
- 第一次握手:
- 客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号字段里
- 第二次握手:
- 服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号设置为客户的ISN加1以.即X+1
- 第三次握手:
- 客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1
2. TCP 四次挥手
TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)
- 客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1
- 总结
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,完成三次握手,客户端与服务器开始传送数据。由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。TCP四次挥手之后就关闭该连接
34. 深拷贝与浅拷贝
浅拷贝
当一个对象拷贝另一个对象的数据的时候,只要一个对象的数据发生改变另一个对象的数据也会发生改变
因为浅拷贝拷贝的是引用的地址,(所以必须在对象是多层才能拷贝,单层拷贝的是数值,多层说明里面套着对象,所以拷贝的是地址。)
实现方式:
1. Object.assign() 作用:将第二个参数及以后的参数合并到第一个对象里。
```javascript
var obj = {a:{name:"kaiqin",age:19}};
var obj1 = Object.assign({},obj);
obj1.a.name="wang"
console.log(obj1) // { a: { name: 'wang', age: 19 } }
console.log(obj) // { a: { name: 'wang', age: 19 } }
- 使用 for in 循环,遍历每一个属性,将他们赋值给新的对象。 要求对象必须是多层的状态下才能实现浅拷贝
var obj = { a: {name:“kaiqin”,age:19 } } ;
var obj2 = {a:1,b:2,c:3};
//多层
function copy(obj){
var newObj = {};
for(var key in obj){
newObj[key] = obj[key];
}
return newObj;
}
//单层
var obj1 = copy(obj);
obj1.a.name=“wang”
console.log(obj1) // { a: { name: ‘wang’, age: 19 } }
console.log(obj) // { a: { name: ‘wang’, age: 19 } } - $.extend()
-
布尔值 如果填true的情况下是深拷贝 什么也不写就是浅拷贝
-
目标对象
-
…后面所有的对象 都是需要合并的对象
var obj = {a:{name:“kaiqin”,age:19}};
var obj1 = {b:{name:“wang”,age:19}};var obj2 = $.extend({},obj,obj1)
obj2.a.name=“zhang”;
console.log(obj2) // a: {name: “zhang”, age: 19} b: {name: “wang”, age: 19}
console.log(obj) // a: {name: “zhang”, age: 19}
-
深拷贝
当一个对象拷贝另一个对象的数据的时候,其中一个对象的数据发生变化不会影响另一个对象的数据
因为深考贝拷贝的是对象的数据而不是地址
实现方式
-
对象是单层的情况下 Object.assign()
var obj = {a:1,b:2,c:3}
var obj1 = Object.assign({},obj);
obj1.a = 30;
console.log(obj1,obj) -
$.extend()
var obj = {a:{name:“kaiqin”,age:19}};
var obj1 = {b:{name:“wang”,age:19}};
var obj2 = $.extend(true,{},obj,obj1);
obj2.a.name=“zhang”;
console.log(obj2)
console.log(obj) -
JSON.parse、JSON.stringfiy 不能拷贝函数,但用在拷贝数据库数据时,不影响。因为数据库没有函数。所以推荐使用
其原理是:先将对象转换为字符串、再转换成对象,此时地址一定发生了变化,所以可以实现浅拷贝。
var obj1 = {b:{name:“wang”,age:19}};
var obj2 = JSON.parse(JSON.stringify(obj1)); //此时地址发生了改变。
obj2.b.name = “kaiqin”;
console.log(obj1,obj2) -
递归
- 我们肯定要定义一个方法,那么这个方法最终应该返回一个深拷贝的数据
- 既然要返回一个数据,我们首先就要定义一个数据,但是数据是对象还是数组?所以需要判断,如果要拷贝的数据是数组,即定义一个数组,如果是一个对象,即定义一个对象
- 方法里面怎么拷贝啊?还是一样的利用for in 循环,在循环内部,需要判断,如果是类型是简单类型,直接拷贝,如果是引用类型,就需要在一次的将引用类型里面的值取出来
var obj = {
a:1,
c:{
c1:1,
c2:2
}
};
function deepCopy(obj){
// 判断拷贝的数据是对象还是数组 生成定义的数据
var copy = Array.isArray(obj)? []:{}
for(key in obj){
// 循环的时候如果此项为引用类型,需要 在一次的将引用类型里面的值取出来
if(typeof obj[key] == ‘object’){
// 再次调用该方法取数据
copy[key] = deepCopy(obj[key])
}else{
copy[key] = obj[key]
}
}
return copy
}
var b = deepCopy(obj)
console.log(b)
-
什么是promise与async、await
promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果),是一个异步解决的方案。
基本语法:
let p = new Promise((resolve,reject) => {
//...
resolve('success')
});
p.then(result => {
console.log(result);//success
});
三个状态:pending(执行中)、success(成功)、rejected(失败)
错误捕获:Promise.prototype.catch用于指定Promise状态变为rejected时的回调函数,可以认为是.then的简写形势,返回值跟.then一样
let p = new Promise((resolve,reject) => {
reject('error');
});
p.catch(result => {
console.log(result);
})
编写一个简单的promise
const fn = function (resolve, reject) {
console.log("Promise execution!");
let date = new Date();
let time = date.getTime();
console.log(time);
if (time % 2 == 0) {
resolve("She loves me!");
} else {
reject("She doesn't love me!");
}
};
const defer = new Promise(fn);
defer.then(
function (value) {
console.log("resolve: " + value);
},
function (reason) {
console.log("reject: " + reason);
}
);
async、await
简洁:异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,Generater的语法糖。
用法
async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。
规则:
- async和await是配对使用的,await存在于async的内部。否则会报错
- await表示在这里等待一个promise返回,再接下来执行
- await后面跟着的应该是一个promise对象
写法
async function demo() {
let result01 = await sleep(100);
//上一个await执行之后才会执行下一句
let result02 = await sleep(result01 + 100);
let result03 = await sleep(result02 + 100);
// console.log(result03);
return result03;
}
demo().then(result => {
console.log(result);
});
错误捕获
如果是reject状态,可以用try-catch捕捉
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('error');
},1000);
});
async function demo(params) {
try {
let result = await p;
}catch(e) {
console.log(e);
}
}
demo();
区别
1 promise是ES6,async/await是ES7
2 async/await相对于promise来讲,写法更加优雅
3 reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误,
2)async/await既可以用.then又可以用try-catch捕捉
- 什么是递归
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回
37.node的优缺点以及适用场景
优点:
因为 Node 是基于事件驱动和无阻塞I/O的,所以非常适合处理并发请求,因此构建在Node 上的代理服务器相比其他技术实现(如Ruby)的服务器表现要好得多
与 Node 代理服务器交互的客户端代码是由 javascript语言编写的,因此客户端和服务器端技术统一
缺点:
- Node 是一个相对新的开源项目,所以不太稳定,更新比较快,向下不兼容
- Node适合于I/O密集型,不适合CPU密集型
- 解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起
- 只支持单核CPU,不能充分利用CPU
- 可靠性比较低,一旦代码某个环节崩溃,整个系统都会崩溃
- debug不太方便
- 解决方案:
- 使用Nginx进行反向代理,负载均衡,开多个进程,绑定多个端口
- 开多个进程监听同一个端口,使用cluster模块
使用场景:
- 实时应用:如在线聊天,实时通知推送等等(如 socket.io)
- 分布式应用:通过高效的并行 I/O 使用已有的数据
- 工具类应用:海量的工具,小到前端压缩部署(如 grunt),大到桌面图形界面应用程序
- 游戏类应用:游戏领域对实时和并发有很高的要求(如网易的 pomelo 框架)
- 总而言之,Node适合运用在高并发、I/O密集、少量业务逻辑的场景。
##38.webpack 是怎么处理 vue 格式的文件的?
- webpack 是使用 vue-loader 处理 vue 格式的文件的,
- 在 15 版本之后,如果要使用 vue-loader ,需要配合自带的插件 VueLoaderPlugin 一起使用
- http的缓存机制
关于HTTP的缓存机制来说,这些缓存策略都会体现在HTTP的头部信息的字段上,这些策略会根据是否需要重新向服务器发起请求可以分为强缓存和协商缓存两大类。
- 强缓存:请求某个资源文件时,服务端就会在response
header中对该资源文件做缓存配置:cache-control,常见的设置是max-age public private
no-cache no-store immutable等。当用户打开某页面,浏览器会判断缓存是否过期,没有过期就会从缓存中读取数据。 - 协商缓存:协商缓存就是需要客户端和服务器两端进行交互的;每次请求回来的response
header中的etag和last-modified;下次请求带上,服务端会进行标识和对比,如果资源更新了就会返回新的资源和对应的etag和last-modified;反之资源没有变。
- 什么是数组扁平化,实现扁平化的方法有哪些?
- 数组扁平化,就是将一个复杂的嵌套多层的数组,一层一层的转化为层级较少或者只有一层数组
实现的方法:
-
arr.flat(Infinity) 底层原理:通过foreach遍历和递归的方式进行一层一层的遍历
-
arr.toString.split(“,”)
-
reduce和递归来实现
-
foreach遍历和递归
-
回流和重绘
- 回流:当rendertree的一部分或者全部元素因改变了自身的宽高,布局,显示或隐藏,或元素内部的文字结构发生变化,导致需要重新构建页面的时候,回流就产生了
- 重绘: 当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就产生了重绘
- 结论:回流必定触发重绘,而重绘不一定触发回流
- HTML5 存储类型有什么区别?
HTML5 能够本地存储数据,在之前都是使用 cookies 。HTML5 提供了两种本地存储方案: localStorage 用于持久化的本地存储,数据永远不会过期,关闭浏览器也不会丢失。 sessionStorage 同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储
-
使用箭头函数应注意什么?
-
用了箭头函数,this就不是指向window,而是父级(指向是可变的)
-
不能够使用arguments对象
-
不能用作构造函数,所以不能使用new命令,否则会报错
-
不可以使用yield命令,因此箭头函数不能用作 Generator 函数
-
请介绍一下Node事件循环的流程
在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。
- 节流和防抖
- 防抖:
指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间 - 节流:
指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率,滚动浏览器滚动条的时候,更新页面上的某些布局内容或者去调用后台的某接口查询内容。
- html5语义化标签
- 结构清晰
- 可读性好
- 便于团队维护开发
- 搜索引擎可根据标签的语言确定上下文和权重问题
- 容易被搜索引擎抓住机会
- map和forEach的区别
map和forEach都是对数组的遍历,但是他们是有区别的,forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
例如,在react经常需要使用数据进行渲染元素的时候,那么这时候,我们就需要使用map了,返回一个新的元素的数组进行渲染。而forEach就是不需要返回新的数据,需要在原数组的每一项上修改的时候,我们经常会用到。
- 容器组件和UI组件的区别
- 容器组件里面有逻辑操作,可以提供数据和行为给UI组件,并且容器组件时有状态的
- UI组件时无状态的,只可以通过props去接受数据,主要用来展示组件内容,是无状态的
-
提供网页加载速度的方式?(优化)
-
减少http的请求
-
使用服务端渲染
-
懒加载图片
-
添加Expires请求头,缓存http访问的组件,下次访问的时候减少不必要的http请求,提高加载速度。
-
使用CDN,减小服务器负担
-
启用GZIP压缩,压缩必要资源,从而给用户发送最小的HTML文件和CSS/JS等资源
-
localstorage sessionstorage和cookie的区别
基本概念
- cookie:它的主要用于保存登陆信息,比如登陆某个网站市场可以看到’记住密码’,这就是通过在cookie中存入一段辨别用户身份的数据来实现的。
- sessionStorage:会话,是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但是页面关闭后,sessionStorage中的数据就会被清空。
- localStorage:是HTML5标准中新加入的技术,当然早在IE6时代就有一个userData的东西用于本地存储,而当时考虑到浏览器的兼容性,更通用的方案是使用flash。如今localStorage被大多数浏览器所支持。
区别
-
存储大小
cookie:一般不超过4K(因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识)sessionStorage:5M或者更大 localStorage:5M或者更大
-
数据有效期
cookie:一般由服务器生成,可以设置失效时间;若没有设置时间,关闭浏览器cookie失效,若设置了时间,cookie就会存放在硬盘里,过期才失效sessionStorage:仅在当前浏览器窗口关闭之前有效,关闭页面或者浏览器会被清除 localStorage:永久有效,窗口或者浏览器关闭也会一直保存,除非手动永久清除,因此用作持久数据
-
作用域
cookie:在所有同源窗口中都是共享的sessionStorage:在同一个浏览器窗口是共享的(不同浏览器、同一个页面也是不共享的) localStorage:在所有同源窗口中都是共享的
-
通信
cookie:十种携带在同源的http请求中,即使不需要,故cookie在浏览器和服务器之间来回传递;如果使用cookie保存过多数据会造成性能问题sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信;不会自动把数据发送给服务器,仅在本地保存 localStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信;不会自动把数据发送给服务器,仅在本地保存
-
易用性
cookie:需要自己进行封装,原生的cookie接口不够友好sessionStorage:原生接口可以接受,可以封装来对Object和Array有更好的支持 localStorage:原生接口可以接受,可以封装来对Object和Array有更好的支持
应用场景
cookie:判断用户是否登录过网站,以便实现下次自动登录或记住密码;保存事件信息等
sessionStorage:敏感账号一次性登录;单页面用的较多(sessionStorage 可以保证打开页面时 sessionStorage 的数据为空)
localStorage:常用于长期登录(判断用户是否已登录),适合长期保存在本地的数据
- 内存泄漏的问题及解决方式
什么是内存泄漏
内存泄露是指:内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
React中解决方式
componentWillUnmount(){
this.setState = (state,callback)=>{
return;
};
}
53.作用域
1、什么是作用域
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
2、作用域类型
全局作用域、函数作用域、块状作用域、动态作用域。
对象 类型
global/window 全局作用域
function 函数作用域(局部作用域)
{} 块状作用域
this 动态作用域
3、全局作用域
变量在函数或者代码块{ }外定义,即为全局作用域。不过,在函数或者代码块{ }内未定义的变量也是拥有全局作用域的(不推荐)。
var carName = "Volvo";
//此处可以调用carName变量
function myFunction(){
//函数内可调用carName变量
}
上述代码中变量carName就是在函数外定义的,它是拥有全局作用域的。这个变量可以在任意地方被读取或者修改,当然如果变量在函数内没有声明(没有使用 var 关键字),该变量依然为全局变量。
//此处可以调用carName变量
function myFunction(){
carName = "Volvo";
//函数内可调用carName变量
}
以上实例中 carName 在函数内,但是拥有全局作用域,它将作为 global 或者 window 的属性存在。
在函数内部或代码块中没有定义的变量实际上是作为 window/global 的属性存在,而不是全局变量。换句话说没有使用 var 定义的变量虽然拥有全局作用域,但是它是可以被 delete 的,而全局变量不可以。
4、函数作用域
在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!
function bar(){
var testValue = 'inner';
}
console.log(testValue) //报错:ReferenceError:testValue is not defined
如果想读取函数内的变量,必须借助 return 或者闭包。
function bar(value){
var testValue = 'inner';
return testValue+value;
}
console.log(bar('fun')) //"innerfun"
这是借助return的方式,下面是闭包的方式:
function bar(value){
var testValue = 'inner';
var result = testValue+value;
function innser(){
return result
};
return innser();
}
console.log(bar('fun')) //"innerfun"
通俗的讲,return 是函数对外交流的出口,而 return 可以返回的是函数,根据作用域的规则,函数内部的子函数是可以获取函数作用域内的变量的。
说到这其实大家会想到嵌套函数的作用域问题,如果 inner 函数再嵌套函数呢?这就涉及到另一个概念:作用域链。
仔细观察上图,其实不难理解作用域链是什么,因为你可以按照原型链那样去理解。任何一个作用域链都是一个堆栈,首先先把全局作用域压入栈底,再按照函数的嵌套关系一次压入堆栈。在执行的时候就按照这个作用域链寻找变量。
5、块状作用域
在其他编程语言中,块状作用域是很熟悉的概念,但是在JavaScript中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识
if(true){
let a = 1
console.log(a)
}
在这个代码中,if 后 { } 就是“块”,这个里面的变量就是拥有这个块状作用域,按照规则,{ }之外是无法访问这个变量的。ES6中的let。
6、动态作用域
在 JavaScript 中很多同学对 this 的指向时而清楚时而模糊,其实结合作用域会对 this 有一个清晰的理解。不妨先来看下这段代码:
window.a = 3
function test () {
console.log(this.a)
}
test.bind({ a : 2 })() //2
test() //3
在这里bind已经把作用域的范围进行了修改指向了{ a:2},而 this 指向的是当前作用域对象,是不是可以清楚的理解了呢?
接下来我们再思考另一个问题:作用域是在代码编写的时候就已经决定了呢,还是在代码执行的过程中才决定的?
var carName = "Volvo";
//此处可调用 carName 变量
function myFunction(){
//函数内可调用 carName 变量
}
在看看这段代码,写代码的时候就知道 carName 就是全局作用域,函数内部的用 var 定义的变量就是函数作用域。这个也就是专业术语:词法作用域。
通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。
看看下面的代码是遵循了动态作用域还是静态作用域呢?
function foo(){
console.log(a) //2 (不是3!)
}
function bar(){
var a=3;
foo();
}
var a=2;
bar()
为什么会这样?
如果按照动态作用域分析:当 foo() 不能为 a 解析出一个变量引用时,它不会沿着嵌套的作用域链向上走一层,而是沿着调用栈向上走,以找到 foo() 是 从何处 被调用的。因为 foo() 是从 bar() 中被调用的,它就会在 bar() 的作用域中检查变量,并且在这里找到持有值 3 的 a。
如果按照静态作用域分析:foo执行的时候没有找到 a 这个变量,它会按照代码书写的顺序往上找,也就是 foo 定义的外层,就找到了 var a=2 ,而不是 foo 调用的 bar 内找。
所以结果就是 2。
从这个示例可以看出 JavaScript 默认采用词法(静态)作用域,如果要开启动态作用域请借助 bind、with、eval 等。