- 箭头函数和普通函数的写法不同
- 箭头函数语法更加简洁、清晰,是 => 定义函数,但不能写成generator形式。
- 普通函数是function定义函数,可以定义成generator: function* ()。
- 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定并固定了。call、apply、bind 并不会影响其 this 的指向。
- 箭头函数不能作为构造函数使用,也不能使用new关键字(因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会改变,作为构造函数其的this要是指向创建的新对象)。
- 箭头函数没有自己的arguments。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。
- 箭头函数没有原型prototype。
变量提升方面:
- var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。
- let和const不存在变量提升问题(注意这个‘问题’后缀,其实是有提升的,只不过是let和const具有一个暂时性死区的概念,即没有到其赋值时,之前就不能用),即它们所声明的变量一定要在声明后使用,否则报错。
块级作用域方面:
- var不存在块级作用域
- let和const存在块级作用域
声明方面:
- var允许重复声明变量
- let和const在同一作用域不允许重复声明变量。其中const声明一个只读的常量(因为如此,其声明时就一定要赋值,不然报错)。一旦声明,常量的值就不能改变。
如果const声明了一个对象,对象里的属性是可以改变的。因为const声明的obj只是保存着其对象的引用地址,只要地址不变,就不会出错。
使用Object.freeze(obj)
冻结obj,就能使其内的属性不可变,但它有局限,就是obj对象中要是有属性是对象,该对象内属性还能改变,要全不可变,就需要使用递归等方式一层一层全部冻结。
Number类型的数字有精度限制,数值的精度只能到 53 个二进制位(相当于 16 个十进制位, 正负9007199254740992),大于这个范围的整数,就无法精确表示了。
Bigint没有位数的限制,任何位数的整数都可以精确表示。但是其只能用于表示整数,且为了与Number进行区分,BigInt 类型的数据必须添加后缀n。BigInt 可以使用负号(-),但是不能使用正号(+)。
另外number类型的数字和Bigint类型的数字不能混合计算。
12n+12; //报错
基本数据类型:
- 基本数据类型的值是不可变的(重新赋值属于改变属性名的指向了,而不是对值进行操作)。
- 基本数据类型不可以添加属性和方法,虽然不会报错,但也只是一瞬间转为了相应包装对象,操作完又转化回原基本数据类型,不会保存结果。
- 基本数据类型的赋值是简单赋值,比较是值的比较。
- 基本数据类型是存放在栈区的
引用数据类型:
- 引用类型的值是可以改变的,例如对象就可以通过修改对象属性值更改对象。
- 引用类型可以添加属性和方法。
- 引用类型的赋值是对象引用,即声明的变量标识符,存储的只是对象的指针地址,比较是引用(指针地址)的比较
- 引用类型是同时保存在栈区和堆区中的,栈区保存变量标识符和指向堆内存的地址。
BTW所有关于字符串和数字(基本数据类型)的方法都是带有返回值的,而不是改变原字符串或数字。
let a='abc';
a.split('');
console.log(a); //abc (没变)
JavaScript代码可能会修改DOM树的结构,所以DOM树解析过程中如果节点是JavaScript代码,就需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。这里就会产生阻塞,出现白屏问题。defer和async都可以用来解决JavaScript脚本阻塞问题。还有一种办法是把script标签放置在body的底部,好处是没有兼容性问题。
defer(延迟)。浏览器立即下载,但延迟执行,跟把script脚本放在body底部差不多。第一个延迟脚本会先于第二个延迟脚本执行,所以执行脚本之间存在依赖,需要有执行的先后顺序时,就可以使用defer。
async(异步),浏览器也会立即下载文件。但与defer不同的是,async脚本并不保证按照它们的先后顺序执行。适用于互不依赖的各脚本。
async/await优点:
- 是很好的语法糖,用同步代码的写法写异步代码,代码阅读相对容易。处理复杂流程时,在代码清晰度方面有优势。
- 对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
if (await fn() === 222) {}
**async/await缺点: **- 无法处理promise返回的reject对象,要借助try catch
- 在可以平行处理的异步请求时,还是promise比较方便,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成
- try catch内部的变量无法传递给下一个try catch。Promise和then/catch内部定义的变量,能通过then链条的参数传递到下一个then/catch,但是async/await的try内部的变量,如果用let和const定义则无法传递到下一个try catch,只能在外层作用域先定义好。
promise的一些问题:
- 一旦执行,无法中途取消,链式调用多个then中间不能随便跳出来
- 错误无法在外部被捕捉到,只能在内部进行预判处理,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
- Promise内部如何执行,监测起来很难,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
传值方式:
- GET通过地址栏来传值,将参数写在 URL 中 ? 的后面,并用 & 分隔不同参数。使用Request.QueryString来取得变量的值
- POST是通过提交表单来传值,将信息存放在 Message Body 中传送,参数‘不会’显示在 URL 中(Restful规范中是这样,但post在有需要时可以把参数放URL里)。通过Request.Form来获取变量的值。
长度限制:
- GET 通过 url 传参,浏览器会限制 url 的长度(http不会)。
- POST请求没有内容长度限制。
缓存:
- GET请求返回的内容会被浏览器缓存起来。
- 浏览器不会缓存POST请求返回的内容,除非手动设置
编码方式:
- GET 请求只能进行 url 编码
- POST 支持多种编码方式。
用途:
- GET对数据进行查询(读)
- POST主要对数据进行增删改(写)
幂等性(幂等表示执行相同的操作,结果也是相同的):
- GET是幂等的
- POST不是
安全性: GET 请求方式从浏览器的 URL 地址就可以看到参数;所以post更安全。
其实无论是 GET 还是 POST 其实都是不安全的,因为 HTTP 协议是明文传输,只要拦截封包便能轻易获取重要资讯。想要安全传输资料,必须使用 SSL/TLS来加密封包,也就是 HTTPS。
那为什么推崇使用post来处理敏感数据呢?
- get的记录会保存在浏览器,上网日志中
- Post的数据不会记录存储在浏览器的记录和网址访问记录中,这样会有更大的安全性。
一个误区:GET产生一个TCP数据包;POST产生两个TCP数据包。其说法是:
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务端响应200,请求成功。
- 对于POST方式的请求,浏览器会先发送http header给服务端,告诉服务端等一下会有数据过来,服务端响应100 continue,告诉浏览器我已经准备接收数据,浏览器再post发送一个data给服务端,服务端响应200,请求成功。
为其正名:上面所说的post会比get多一个tcp包其实不太严谨。多发的那个expect 100 continue header报文,是由客户端对http的post和get的请求策略决定的,目的是为了避免浪费资源,如带宽,数据传输消耗的时间等等。所以客户端会在发送header的时候添加expect 100去探探路,如果失败了就不用继续发送data,从而减少了资源的浪费。所以是否再发送一个包取决了客户端的实现策略,和get/post并没什么关系。有的客户端比如fireFox就只发送一个包。
使用框架好处:
- 使用框架工具写项目,在浏览器中代码依然是原生的HTML CSS JS。而框架帮开发者做了很多事情,开发者只关注业务逻辑就可以,极大的加快了开发速度。前端框架根本上是解决了UI 与状态同步问题,频繁操作 DOM 性能低下.中间步骤过多,易产生 bug且不易维护,而且心智要求较高不利于开发效率的一系列阻碍
- 组件化: 其中以 React 的组件化最为彻底,甚至可以到函数级别的原子组件,高度的组件化可以是我们的工程易于维护、易于组合拓展。
- 天然分层: JQuery 时代的代码大部分情况下是面条代码,耦合严重,现代框架不管是 MVC、MVP还是MVVM 模式都能帮助我们进行分层,代码解耦更易于读写。
- 生态: 现在主流前端框架都自带生态,不管是数据流管理架构还是 UI 库都有成熟的解决方案
框架缺点:
- 代码臃肿,使用者使用框架的时候会将整个框架引入,而框架封装了很多功能和组件,使用者必须按照它的规则使用,而实际开发中很多功能和组件是用不到的。
- 框架迭代更新速度非常快,需要时间熟悉它。上手需要一定成本。
相似之处
- 都使用了Virtual DOM(虚拟DOM)提高重绘性能
- 都有props的概念,允许组件间的数据传递
- 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性
- 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板。
思想差异和数据流 react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流 vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。
模板和jsx
- Vue鼓励写近似常规HTML的模板。写起来很接近标准 HTML元素,只是多了一些属性。
- React推荐你所有的模板通用JavaScript的语法扩展——JSX书写。
jsx更强大,能想怎么写就怎么写,但是缺点就是无法静态编译优化。模板可以静态编译优化,比如提升父节点,在编译阶段就知道了哪些元素肯定不会重新渲染。所以react就在运行时的优化做到极致,引入了fiber。
react虽然不能静态编译,但是能通过手动优化达到类似的效果,比如用memo和shouldComponentUpdate。我更喜欢这样的让开发者有更大控制权的模式,在写react的过程中也对这些优化有了更深的理解。
拓展来讲,像react-router,react-redux都是社区中很优秀的库,然后集成到react身上。我非常喜欢这样的模式,就是我本身是一个包含最少东西的库,但是有着很好可拓展性,让工具,插件,库来完善我的功能,而不是一开始就全部自带。类似的例子有:
- redux本身是很简洁的,连异步处理的能力都没有。但是给了一个enhancer的接口,通过可拓展性来拓展功能。
- 类组件就是集成,耦合的(vue)。函数组件(react) + hook(各种生态)就是自由了,可拓展的,解耦的。
- polkadot
拓展
react可以通过高阶组件HOC来扩展,而vue需要通过mixins来扩展。
原因高阶组件就是高阶函数,而React的组件本身就是纯粹的函数,所以高阶函数对React来说易如反掌。相反Vue.js使用HTML模板创建视图组件,这时模板无法有效的编译,因此Vue不采用HOC来实现。
存储位置:
- cookie的数据信息存放在客户端浏览器上
- session的数据信息存放在服务器上。
存储容量:
- 单个cookie保存的数据<=4KB,一个站点最多保存20个Cookie
- session存储并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西,并且设置session删除机制。
存储方式:
- cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据。
- session中能够存储任何类型的数据,包括且不限于string,integer,list,map等。
隐私策略:
- cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的
- session存储在服务器上,对客户端是透明的,不存在敏感信息泄漏的风险。
有效期:
- 开发可以通过设置cookie的属性,达到使cookie长期有效的效果。
- session依赖于名为JSESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session不能达到长期有效的效果。
服务器压力不同:
- cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie是很好的选择。
- session是保管在服务器端的,每个用户都会产生一个session。假如并发访问的用户十分多,会产生十分多的session,耗费大量的内存。
跨域支持
- :cookie支持跨域名访问(不是跨域,是跨域名,二级域名是可以共享cookie的)。
- session不支持跨域名访问。
- 相同点是都是保存在浏览器端、且同源的
- cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
- 存储大小限制:
- cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。
- sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
- 数据有效期不同:
- sessionStorage:仅在当前浏览器窗口关闭之前有效;
- localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
- cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
- 作用域不同:
- sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;
- localstorage和cookie也是在所有同源窗口中都是共享的
- webStorage(webstorage是本地存储,存储在客户端,包括localStorage和sessionStorage)支持事件通知机制,可以将数据更新的通知发送给监听者
- webStorage的api接口使用更方便
这个不结合event loop很难讲清楚
Ajax是什么:Ajax是(Asynchronous JavaScript and XML)的缩写。现在,允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax。核心使用XMLHttpRequest对象。
axios是什么:axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。
fetch是什么:Fetch被称为下一代Ajax技术,采用Promise方式来处理数据。是一种简洁明了的API,比XMLHttpRequest更加简单易用。
主要区别是 axios、fetch请求后都支持Promise对象API,ajax只能用回调函数。
- TCP 是面向连接的,UDP 是无连接的即发送数据前不需要先建立链接。
- TCP 是面向连接的可靠性传输,而 UDP 是不可靠的。通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达; UDP 尽最大努力交付,即不保证可靠交付。并且因为 TCP 可靠, 面向连接,不会丢失数据因此适合大数据量的交换。
- TCP 是面向字节流,UDP 面向报文,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如 IP 电话和视频会议等)。
- TCP 只能是 1 对 1 的,而UDP 支持 1 对 1,1 对多。
- TCP 的首部较大为 20 字节,而 UDP 只有 8 字节。
堆(heap)和栈(stack)的区别:
- 原始类型的数据会存在stack里面,如果复制的话就是存了一个copy在stack里。
- 引用类型
[object X] X = { Array, function, data, RegExp 等}
会存在heap里,在stack里面存的是指向数据的指针。如果复制的话,就是复制这个指针。
相同点
- 都是一样基于TCP的,都是可靠性传输协议。
- 都是应用层协议。
不同点
- WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
- WebSocket是需要握手进行建立连接的(相对HTTP来说,WebSocket是一种持久化的协议。它会基于HTTP协议,来完成一部分握手,HTTP握手部分完成,协议升级为WebSocket)。
- HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
- 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。
- HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
- http 和 https 用的端口不一样,http是 80,https是 443。
- HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
- px: px就是pixel的缩写,意为像素。px就是一张图片最小的一个点,一张位图就是千千万万的这样的点构成的。
- em: 参考物是父元素的font-size,具有继承的特点。如果自身定义了font-size按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值。
- rem: css3新单位,相对于根元素html(网页)的font-size,不会像em那样,依赖于父元素的字体大小,而造成混乱。
- vw: css3新单位,viewpoint width的缩写,视窗宽度,1vw等于视窗宽度的1%。
- vh: css3新单位,viewpoint height的缩写,视窗高度,1vh等于视窗高度的1%。
什么是loader? loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
什么是plugin? 在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
区别: 对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程。常用的:
- babel-loade处理js
- sass-loader, css-loader, style-loader处理样式
- file-loader,url-loader处理文件
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。常用的:
- HtmlWebPackPlugin:自动把bundle注入html
- CleanTerminalPlugin
- BundleAnalyzerPlugin:分析bundle里面具体包含什么和对应大小
- 三者都可以改变函数的this对象指向。
- 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
- 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
- bind 改变this指向后不会立即执行,而是返回一个永久改变this指向的函数便于稍后调用;apply, call则是立即调用
- 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
- 资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
- 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
- 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
- 影响关系:因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉(以前一个tab崩了就会整个浏览器崩),所以多进程要比多线程健壮。
- TS是JS的超集,JS 代码可以在无需任何修改的情况下与 TS 一同工作,同时可以使用编译器将 TS 代码转换为 JS。
- TS 通过类型注解提供编译时的静态类型检查,
- TS 中的数据要求带有明确的类型,JS不要求。
- TS 为函数提供了缺省参数值。
- TS 引入了 JS 中没有的“类”概念。
- TS 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
http 1.0(构建可扩展性)
- 版本信息现在会随着每个请求发送(HTTP1.0 被追加到GET行)
- 状态代码行也会在响应开始时发送,允许浏览器本身了解请求的成功或失败,并相应地调整其行为(如以特定方式更新或使用本地缓存)
- 引入了HTTP头的概念,无论是对于请求还是响应,允许传输元数据,并使协议非常灵活和可扩展。
- Content-Type标头告诉客户端实际返回的内容的内容类型。在Content-Type标头帮助下,增加了传输除纯文本HTML文件外的其他类型文档的能力。
http 1.1(标准化的协议) HTTP/1.0的多种不同的实现运用起来有些混乱,HTTP1.1是第一个标准化版本,重点关注的是校正HTTP设计中的结构性缺陷:
- 连接可以重复使用,节省了多次打开它的时间,以显示嵌入到单个原始文档中的资源。
- 增加流水线操作,允许在第一个应答被完全发送之前发送第二个请求,以降低通信的延迟。
- 支持响应分块。
- 引入额外的缓存控制机制。
- 引入内容协商,包括语言,编码,或类型,并允许客户端和服务器约定以最适当的内容进行交换。
- 通过 Host 头,能够使不同的域名配置在同一个IP地址的服务器。
- 安全性得到了提高
http 2.0(为了更优异的表现) HTTP/2在HTTP/1.1有几处基本的不同:
- HTTP2是二进制协议而不是文本协议。不再可读和无障碍的手动创建,改善的优化技术现在可被实施。
- 这是一个复用协议。并行的请求能在同一个链接中处理,移除了HTTP/1.x中顺序和阻塞的约束。
- 压缩了headers。因为headers在一系列请求中常常是相似的,其移除了重复和传输重复数据的成本。
- 其允许服务器在客户端缓存中填充数据,通过一个叫服务器推送的机制来提前请求。
数据库模型 | 非关系型 | 关系型 |
---|---|---|
存储方式 | 以类JSON的文档的格式存储 | 不同引擎有不同的存储方式 |
查询语句 | MongoDB查询方式(类似JavaScript的函数) | SQL语句 |
数据处理方式 | 基于内存,将热数据存放在物理内存中,从而达到高速读写 | 不同引擎有自己的特点 |
成熟度 | 新兴数据库,成熟度较低 | 成熟度高 |
广泛度 | NoSQL数据库中,比较完善且开源,使用人数在不断增长 | 开源数据库,市场份额不断增长 |
事务性 | 仅支持单文档事务操作,弱一致性 | 支持事务操作 |
占用空间 | 占用空间大 | 占用空间小 |
join操作 | MongoDB没有join | MySQL支持join |
其实他们的思路很相似,最重要的区别是中间有没有一个“中间商”。
发布订阅模式 消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
事件中心提供了订阅和发布功能。订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。
观察者模式 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
例子:公司招聘。
- 观察者模式:几个感兴趣的人在关注公司有没有发布前端职位,公司官网发了,他们立刻就知道了。
- 发布订阅模式:公司跟猎头说,我有新职位,招聘者跟猎头说,我对前端感兴趣。
区别
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
- 观察者模式是松散耦合的(有一定耦合度)。在发布订阅模式中,组件是完全解耦的,互相根本不知道彼此。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
- 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。