提醒:我只是答案的搬运工,如果在浏览中发现有错误,欢迎评论中提出来,我好修改,谢谢!
同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作
异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容
undefined、null、string、boolean、number、symbol
DIV(division)是一个块级元素,可以包含段落、标题、表格,乃至诸如章节、摘要和备注等。
SPAN 是行内元素,SPAN 的前后是不会换行的,它没有结构的意义,纯粹是应用样式,当其他行内元素都不合适时,可以使用SPAN。块元素相当于内嵌元素在前后各加一个换行。其实,块元素和行内元素也不是一成不变的,只要给块元素定义display:inline,块元素就成了内嵌元素,同样地,给内嵌元素定义了display:block就成了块元素了。
CSS预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为CSS增加了一些编程的特性,将CSS作为目标生成文件,然后开发者就只要使用这种语言进行编码工作。通俗的说,CSS预处理器用一种专门的编程语言,进行Web页面样式设计,然后再编译成正常的CSS文件。CSS 后处理器是对 CSS 进行处理,并最终生成 CSS 的预处理器,它属于广义上的 CSS 预处理器。
通配符选择器:0 元素选择符:1 关系选择器:1 伪元素选择器:1 类选择符:10 属性选择器:10 伪类选择器:10 id选择器:100 内联样式:1000 !important声明的样式优先级最高
1.Object是JavaScript中所有对象的父对象
2.数据封装类对象:Object、Array、Boolean、Number和String
3.其他对象:Function、Arguments、Math、Date、RegExp、Error
在JS引擎中,通过标识符查找标识符的值,会从当前作用域向上查找,直到作用域找到第一个匹配的标识符位置。就是JS的作用域链。
解决命名冲突;提供复用性;提高代码可维护性
立即执行函数;AMD和CMD;Commonjs;ES Module。
window它是一个顶层对象,而不是另一个对象的属性即浏览器的窗口。
document对象是window对象的一个对象属性
document.write是直接写入到页面的内容流,如果在写之前没有调用document.open, 浏览器会自动调用open。每次写完关闭之后重新调用该函数,会导致页面被重写。
innerHTML则是DOM页面元素的一个属性,代表该元素的html内容。你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement。
innerHTML将内容写入某个DOM节点,不会导致页面全部重绘;innerHTML很多情况下都优于document.write,其原因在于其允许更精确的控制要刷新页面的那一个部分。
先将img标签的src链接设为同一张图片(比如空白图片),然后给img标签设置自定义属性(比如 data-src),然后将真正的图片地址存储在data-src中,当JS监听到该图片元素进入可视窗口时,将自定义属性中的地址存储到src属性中。达到懒加载的效果。
DOCTYPE是document type (文档类型) 的缩写。声明位于文档的最前面,处于标签之前,它不是html标签。主要作用是告诉浏览器的解析器使用哪种HTML规范或者XHTML规范来解析页面。
标准模式和兼容模式都是浏览器的呈现模式,浏览器究竟使用兼容模式还是标准模式呈现页面与网页中的DTD(文件类型定义)有关,DTD里面包含了文档的规则。
标准模式是指浏览器按照W3C标准来解析代码,呈现页面;兼容模式,是指浏览器按照自己的方式来解析代码,使用一种比较宽松的向后兼容的方式来显示页面。
使用Trident内核的浏览器:IE、Maxthon、TT、The World等;使用Gecko内核的浏览器:Netcape6及以上版本、FireFox、MozillaSuite/SeaMonkey;使用Presto内核的浏览器:Opera7及以上版本;使用Webkit内核的浏览器:Safari、Chrome。
用正确的标签做正确的事情;html语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于SEO;使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
iframe会阻塞主页面的Onload事件;搜索引擎的检索程序无法解读这种页面,不利于SEO;iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载;使用iframe之前需要考虑这两个缺点。如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。
块级元素:div ul ol li dl dt dd h1 h2 h3 h4等
行内元素:a b span img input select strong
空元素:br hr img input link meta
display:none 不显示对应的元素,在文档布局中不再分配空间(回流+重绘)
visibility:hidden 隐藏对应元素,在文档布局中仍保留原来的空间(重绘)
flex-direction
row:横向从左到右排列(左对齐),默认的排列方式。
row-reverse:反转横向排列(右对齐,从后往前排,最后一项排在最前面。
column:纵向排列。
column-reverse:反转纵向排列,从后往前排,最后一项排在最上面。
flex-wrap
nowrap(默认):不换行。
wrap:换行,第一行在上方。
wrap-reverse:换行,第一行在下方。
flex-flow
是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
justify-content
flex-start:从左到右排列
flex-end:从右到左排列
center:中间开始排列
space-between:平分
space-around:平分,且两边占1/2
align-items
flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。
Px 就是pixel的缩写,意为像素。px就是一张图片最小的一个点,一张位图就是千千万万的这样的点构成的,比如常常听到的电脑像素是1024x768的,表示的是水平方向是1024个像素点,垂直方向是768个像素点。
Em 参考物是父元素的font-size,具有继承的特点。如果自身定义了font-size按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值。
Rem css3新单位,相对于根元素html(网页)的font-size,不会像em那样,依赖于父元素的字体大小,而造成混乱。
% 一般广泛的讲是相对于父元素,但是并不是十分准确。
1、对于普通定位元素就是我们理解的父元素
2、对于position: absolute;的元素是相对于已定位的父元素
3、对于position: fixed;的元素是相对于 ViewPort(可视窗口)
Vw css3新单位,viewpoint width的缩写,视窗宽度,1vw等于视窗宽度的1%。
举个例子:浏览器宽度1200px, 1 vw = 1200px/100 = 12 px。
Vh css3新单位,viewpoint height的缩写,视窗高度,1vh等于视窗高度的1%。
举个例子:浏览器高度900px, 1 vh = 900px/100 = 9 px。
Vm css3新单位,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm
举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度, 1 vm = 900px/100 = 9 px。
cookie是客户端记录保存用户身份信息;session是服务器记录用户信息的
cookie是保存在客户端的,而session是保存在服务器的;
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
单个cookie在客户端的限制是4K,就是说一个站点在客户端存放的COOKIE不能超过4K。
将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中。
创建XMLHttpRequest对象。设置请求方式。调用回调函数。发送请求。
主流的前后端分离模式下,当前端调用后台接口时,由于是在非同一个域下的请求,从而会引发浏览器的自我安全保护机制,最终结果是接口成功请求并响应,但前端不能正常处理该返回数据。因此,当同时满足以下三个条件的情况下,就会出现跨域问题:浏览器限制;非同源请求(跨域);发送的是 XHR ( XMLHttpRequest ) 请求
通过jsonp跨域;document.domain + iframe跨域;location.hash + iframe跨域;window.name + iframe跨域;postMessage跨域;跨域资源共享(CORS);nginx代理跨域;nodejs中间件代理跨域;WebSocket协议跨域
静态链接和动态链接两者最大的区别就在于链接的时机不一样,静态链接是在形成可执行程序前,而动态链接的进行则是在程序执行时
动态链接出现的原因就是为了解决静态链接中提到的两个问题,一方面是空间浪费,另外一方面是更新困难。
浏览器DNS缓存;系统DNS缓存;路由器DNS缓存;网络运营商DNS缓存。
第一次握手:由浏览器发起请求,告诉服务器我要发起请求了;
第二次握手:由服务器发起,告诉浏览器我准备接收了,发送吧;
第三次握手:有浏览器发起,告诉服务器马上发,准备接收。
遇见HTML标记,浏览器调用HTML解析器解析成Token并构建成DOM树
遇见style/link标记,浏览器调用CSS解析器,处理css标记并构建CSSOM树
遇见Script标记,调用JavaScript解析器,处理script代码(绑定事件,修改DOM树CSSOM树)
将DOM树和CSSOM树合并成一个渲染树
根据渲染树来计算布局,计算每个节点的几何信息(布局)
将各个节点颜色绘制到屏幕上(渲染)
第一次挥手:由浏览器发起,发送给服务器,资源发送完毕(请求报文),你准备关闭吧
第二次挥手:由服务器发起,告诉浏览器资源接受完毕(请求报文),我准备关闭,你也准备吧
第三次挥手:由服务器发起,告诉浏览器我资源发送完毕(响应报文),你准备关闭吧
第四次挥手:由浏览器发起,告诉服务器资源接受完毕,我准备关闭(响应报文),你也准备吧
prototype允许我们在创建对象之后来改变对象或类的行为,并且这些通过prototype属性添加的字段或方法所有对象实例是共享的。
1.不同浏览器的标签默认的外补丁( margin )和内补丁(padding)不同?解决方案: css 里增加通配符 * { margin: 0; padding: 0; }
2.IE6双边距问题;在 IE6中设置了float , 同时又设置margin , 就会出现边距问题?解决方案:设置display:inline;
3.当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度?解决方案:超出高度的标签设置overflow:hidden,或者设置line-height的值小于你的设置高度
4.图片默认有间距?解决方案:使用float 为img 布局
5.边距重叠问题;当相邻两个元素都设置了margin 边距时,margin 将取最大值,舍弃最小值;?解决方案:为了不让边重叠,可以给子元素增加一个父级元素,并设置父级元素为overflow:hidden;
6.cursor:hand 显示手型在safari 上不支持?解决方案:统一使用 cursor:pointer
7.两个块级元素,父元素设置了overflow:auto;子元素设置了position:relative ;且高度大于父元素,在IE6、IE7会被隐藏而不是溢出?解决方案:父级元素设置position:relative
1.捕获阶段:事件传播由目标节点的祖先节点逐级传播到目标节点。先由文档的根节点document(window)开始触发对象,最后传播到目标节点,从外向内捕获事件对象;
2.目标阶段:事件到达目标对象,事件触发,如果事件不允许冒泡,事件会在这一阶段停止传播。
3.冒泡阶段:从目标节点逐级传播到document节点。
Static:元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分,行内元素则会创建一个或多个行框,置于其父元素中。
Relative:元素框偏移某个距离。元素仍保持其未定位前的形状,它原本所占的空间仍保留。
Absolute:元素框从文档流完全删除,并相对于其包含块定位。包含块可能是文档中的另一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。
Fixed:元素框的表现类似于将 position 设置为 absolute,不过其包含块是视窗本身。
1、GET方法:发送一个请求来取得服务器上的某一资源
2、POST方法:向URL指定的资源提交数据或附加新的数据
3、PUT方法:跟POST方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT指定了资源在服务器上的位置,而POST没有
4、HEAD方法:只请求页面的首部
5、DELETE方法:删除服务器上的某资源
6、OPTIONS方法:它用于获取当前URL所支持的方法。如果请求成功,会有一个Allow的头包含类似“GET,POST”这样的信息
7、TRACE方法:TRACE方法被用于激发一个远程的,应用层的请求消息回路
8、CONNECT方法:把请求连接转换到透明的TCP/IP通道
GET产生一个TCP数据包;POST产生两个TCP数据包(Firefox只发送一次)。GET在浏览器回退时是无害的,而POST会再次提交请求。GET产生的URL地址可以被Bookmark,而POST不可以。GET请求会被浏览器主动cache,而POST不会,除非手动设置。GET请求只能进行url编码,而POST支持多种编码方式。GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。GET请求在URL中传送的参数是有长度限制的,而POST没有。对参数的数据类型,GET只接受ASCII字符,而POST没有限制。GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。GET参数通过URL传递,POST放在Request body中。
JavaScript 中的 number 类型就是浮点型,JavaScript 中的浮点数采用IEEE-754 格式的规定,这是一种二进制表示法,可以精确地表示分数,比如 1/2,1/8,1/1024,每个浮点数占 64 位。但是,二进制浮点数表示法并不能精确的表示类似 0.1 这样的简单的数字,会有舍入误差。由于采用二进制,JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中,1/10(0.1)被表示为 0.00110011001100110011…… 注意 0011 是无限重复的,这是舍入误差造成的,所以对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算。
let 和 const 定义的变量不会出现变量提升,而 var 定义的变量会提升;let 和 const 是JS中的块级作用域;let 和 const 不允许重复声明(会抛出错误);let 和 const 定义的变量在定义语句之前,如果使用会抛出错误(形成了暂时性死区),而 var 不会;const 声明一个只读的常量,一旦声明,常量的值就不能改变(如果声明是一个对象,那么不能改变的是对象的引用地址)。
WebPack是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、Javascript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后生成了优化且合并后的静态资源。
Promise有三种状态:pengding(),resolved(已完成),rejected(已失败);Promise从Pending状态开始,如果成功就转到成功态,并执行resolve回调函数;如果失败就转到失败状态并执行reject回调函数。
优点:一旦状态改变,就不会再变,任何时候都可以得到这个结果;可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
缺点:无法取消 Promise;当处于pending状态时,无法得知目前进展到哪一个阶段。
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
async 和 await 相比直接使用 Promise 来说,优势在于处理 then 的调用链,能够更清晰准确的写出代码。缺点在于滥用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
相同点:都是异步请求的方式来获取服务端的数据
不同点:请求方式不同:$.get() 方法使用GET方法来进行异步请求的。$.post() 方法使用POST方法来进行异步请求的;参数传递方式不同:get请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给Web服务器的,这种传递是对用户不可见的;数据传输大小不同:get方式传输的数据大小不能超过2KB 而POST要大的多;安全问题: GET 方式请求的数据会被浏览器缓存起来,因此有安全问题。
状态0(请求未初始化):(XMLHttpRequest)对象已经创建或已被abort()方法重置,但还没有调用open()方法;
状态1(载入服务器连接已建立):已经调用open() 方法,但是send()方法未调用,尚未发送请求;
状态2(载入完成,请求已接收):send()方法已调用,HTTP请求已发送到web服务器,请求已经发送完成,未接收到响应;
状态3(交互,请求处理中):所有响应头部都已经接收到。响应体开始接收但未完成,即可以接收到部分响应数据;
状态4(请求完成,且相应已就绪):已经接收到了全部数据,并且连接已经关闭。
就是攻击者想尽一切办法将可以执行的代码注入到网页中。
持久型也就是攻击的代码被服务端写入进数据库中;非持久型相比于前者危害就小的多了,一般通过修改 URL 参数的方式加入攻击代码,诱导用户访问链接从而进行攻击。
转义输入输出的内容,对于引号、尖括号、斜杠进行转义;建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。
攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话,后端就以为是用户在操作,从而进行相应的逻辑。
对 Cookie 设置 SameSite 属性,表示 Cookie 不随着跨域请求发送,可以很大程度减少 CSRF 的攻击,但是该属性目前并不是所有浏览器都兼容;验证 Referer 来判断该请求是否为第三方网站发起的;服务器下发一个随机 Token,每次发起请求时将 Token 携带上,服务器验证 Token 是否有效。
HTTP 请求由三部分构成,分别为:请求行、首部、实体
请求行:基本由请求方法、URL、协议版本组成;首部:分为请求首部和响应首部
标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin
低版本IE盒子模型:宽度=内容宽度(content+border+padding)+ margin
RGBA和透明度; background-image background-origin(content-box/padding-box/border-box) background-size background-repeat; word-wrap(对长的不可分割单词换行)word-wrap:break-word; 文字阴影:text-shadow: 5px 5px 5px #FF0000;(水平阴影,垂直阴影,模糊距离,阴影颜色); font-face属性:定义自己的字体; 圆角(边框半径):border-radius 属性用于创建圆角; 边框图片:border-image: url(border.png) 30 30 round; 盒阴影:box-shadow: 10px 10px 5px #888888; 媒体查询:定义两套css,当浏览器的尺寸变化时会采用不同的属性
首先,需要把元素的宽度、高度设为0。然后设置边框样式。
width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;
浮动元素碰到包含它的边框或者浮动元素的边框停留。由于浮动元素不在文档流中,所以文档流的块框表现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。
浮动带来的问题:父元素的高度无法被撑开,影响与父元素同级的元素;与浮动元素同级的非浮动元素(内联元素)会跟随其后;若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示的结构。
.clearfix:after{
content:'.';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.clearfix {zoom:1;}
响应式网站设计(Responsive Web design)是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。
基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理。
页面头部必须有meta声明的viewport。
优雅降级观点认为应该针对那些最高级、最完善的浏览器来设计网站。
渐进增强观点则认为应关注于内容本身,优先考虑低版本。
块格式上下文(BFC)是 Web 页面的可视化 CSS 渲染的部分,是块级盒布局发生的区域,也是浮动元素与其他元素交互的区域。
一个 HTML 盒(Box)满足以下任意一条,会创建块格式化上下文:float的值不是none、 position的值不是static或relative、 display的值是table-cell、table-caption、inline-block、flex、或inline-flex、 overflow的值不是visible。
在 BFC 中,每个盒的左外边缘都与其包含的块的左边缘相接。
MVC模式【Model(模型)+View(视图)+controller(控制器)】
View通过Controller来和Model联系,Controller是View和Model的协调者,View和Model不直接联系,基本联系都是单向的。用户User通过控制器Controller来操作模板Model从而达到视图View的变化
MVP模式【是从MVC模式演变而来的,都是通过Controller/Presenter负责逻辑的处理+Model提供数据+View负责显示】
在MVP中,Presenter完全把View和Model进行了分离,主要的程序逻辑在Presenter里实现。
并且,Presenter和View是没有直接关联的,是通过定义好的接口进行交互,从而使得在变更View的时候可以保持Presenter不变。
MVVM模式【Model+View+ViewModel】
MVVM是把MVC里的Controller和MVP里的Presenter改成了ViewModel;View的变化会自动更新到ViewModel,ViewModel的变化也会自动同步到View上显示。这种自动同步是因为ViewModel中的属性实现了Observer,当属性变更时都能触发对应的操作。
Vue框架【MVVM】
优点:轻量级框架,语法简单,学习成本低;双向数据绑定;组件化开发;数据和结构的分离;虚拟DOM;运行速度快;灵活渐进式框架
缺点:不支持IE8;生态环境差,不如angular和react;不适合偏大型的项目
应用场景:小型应用
React框架【MVC】
优点:jsx语法创建虚拟DOM,极速的渲染性能;组件化开发,组件独立,方便重复使用;单向数据流;组件生命周期;跨浏览器兼容性好
缺点:不适合单独做一个完整的框架
应用场景:个性化需求、中型应用
Angular框架
优点:模板功能强大丰富,并且是声明式的,自带了丰富的Angular指令;是一个比较完善的前端框架,包含服务,模板,数据双向绑定,模块化,路由,过滤器,依赖注入等所有功能;自定义指令,自定义指令后可以在项目中多次使用。ng模块化比较大胆的引入了Java的一些东西(依赖注入);双向绑定(脏检查机制)
缺点:验证功能错误信息显示比较薄弱,需要写很多模板标签;ng提倡在控制器里面不要有操作DOM的代码,对于一些jQuery 插件的使用,如果想不破坏代码的整洁性,需要写一些directive去封装插件;从1.0.X升级到1.2.X,貌似有比较大的调整,没有完美兼容低版本,升级之后可能会导致一个兼容性的BUG;AngularJS 太笨重了,没有让用户选择一个轻量级的版本,当然1.2.X后,Angular也在做一些更改,比如把route,animate等模块独立出去,让用户自己去选择。
应用场景:在大型超大型web应用开发上。
相同点:组件化开发和Virtual DOM;支持props进行父子组件间数据通信;支持数据驱动视图,不直接操作真实DOM,更新状态数据界面就自动更新;支持服务端渲染;支持native的方案,react的React Native,Vue的Weex。
不同点:Vue支持双向数据流,React单向数据流;组件写法React推荐JSX,就是把HTML和css全都写进JavaScript中,Vue推荐是webpack+vue+loader单文件组件格式,就是HTML、css、JavaScript都写进一个文件;state对象在React应用中不可变,需要使用setState方法更新状态,在Vue中,state对象不是必须得,数据有data属性在vue对象中管理;Virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树,而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,及React中会需要shouldComponentUpdata这个生命周期函数方法来进行控制;React严格上只针对MVC的view层,而Vue则是MVVM模式
相同点:都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器;vue和angular绑定都可以用{{}}
不同点:vue相当于angular要变得小巧很多,运行速度比angular快;vue指令用v-xxx,angular用ng-xxx;vue中数据放在data对象里面,angular数据绑定在$scope上面;vue有组件化概念,angular中没有;AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢,Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的
相同点:都是单向数据流
不同点:React中没有指令,Angular提供了丰富的指令
使用父组件,通过props将变量传入子组件(如通过refs,父组件获取一个子组件的方法,简单包装后,将包装后的方法通过props传入另一个子组件)
生命周期是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程。
各个生命周期的作用
beforeCreate: 组件实例被创建之初,组件的属性生效之前
created: 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
beforeMount: 在挂载开始之前被调用:相关的 render 函数首次被调用
mounted: el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate: 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
update: 组件数据更新之后
activated: keep-alive 专属,组件被激活时调用
deadctivated: keep-alive 专属,组件被销毁时调用
beforeDestory: 组件销毁前调用
destroyed: 组件销毁后调用
v-if 的话就得说到 Vue 底层的编译了。当属性初始为 false 时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,所以总的来说在切换时开销更高,更适合不经常切换的场景。并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。
v-show 只是在 display: none 和 display: block 之间切换。无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。所以总的来说 v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。
vue 实现数据双向绑定主要是:采用数据劫持结合“发布者 - 订阅者”模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter、 getter,在数据变动时发布消息给订阅者,触发相应监听回调。
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
通信种类:父组件向子组件通信、子组件向父组件通信、隔代组件间通信、兄弟组件通信
实现通信方式:props、vue自定义事件、消息订阅与发布、vuex、slot
Props方式:通过一般属性实现父向子通信;通过函数属性实现子向父通信;缺点是隔代组件和兄弟组件间通信比较麻烦
Vue自定义事件:vue内置实现,可以代替函数类型的props(绑定监听:
消息订阅与发布:需要引入消息订阅与发布的实现库,如pubsub-js(订阅消息:PubSub.subscript(‘msg’,(msg,data)=>{}),发布消息:PubSub.publish(‘msg’,data));优点是此方式可实现任意关系组件间通信
Vuex:vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件;优点是对组件间关系没有限制,且相比于pubsub库管理更集中,更方便
Slot:专门用来实现父向子传递数据的标签;注意通信的标签模板是在父组件中解析好后再传递给子组件的
通过props来传值:静态传值就是直接通过props来传递;动态传值是通过v-bind来绑定一个要传递值的key,然后后面跟要传递的内容,不过这个内容是可以改变的。
通过ref来传值:在父组件引用的子组件中采用ref=’要传递的值的key’
emit是子组件向父组件的传值方式:子组件可以使用 $emit 触发父组件的自定义事件
Slot:父组件向子组件传递模板采用slot,如果父组件没传递模板,slot里面有内容的话,就会显示内容,如果有多个模板要进行传递,这需要在slot中通过命名(name)来区分。
computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容;watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。
对于 keep-alive 组件来说,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
State:一般在Vue组件中在计算属性computed中返回一个某个状态;
getter:是 store 的计算属性, getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算;
mutation:包含多个直接更新state的方法(回调函数)的对象;
action:包含多个时间回调函数的对象;
module:Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;基于上面一点,SPA 相对对服务器压力小;前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。
缺点:
初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
$router 为 VueRouter 实例,想要导航到不同 URL,则使用 $router.push 方法。
$route 为当前 router 跳转对象里面可以获取 name 、 path 、 query 、 params 等。
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
Proxy 的优势如下:
Proxy 可以直接监听对象而非属性;
Proxy 可以直接监听数组的变化;
Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
Object.defineProperty 的优势如下:
兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
优点:
保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
entry(入口):entry point,入口起点(可以有多个),webpack会从该起点出发,找出哪些文件时入口文件所依赖的,从而构建内部依赖关系图,并处理后输出到称之为bundles的文件中。
output(输出):指定经entry point处理后的bundles文件的输出路径(path)和名字(filename)。
loader(加载器):webpack 自身只能识别js文件,但是开发中会有css,图片等静态文件,webpack虽然自身不能识别,但是可以通过loader来处理;实现css、图片等文件的打包。
plugins(插件):从打包优化和压缩,一直到重新定义环境中的变量。
Redux基本理解:Redux是一个独立专门用于做状态管理的JS库,不是React插件库;它可以用在React,Angular,Vue等项目中,但基本与React配合使用;作用是集中式管理React应用中多个组件共享的状态和从后台获取的数据
第一种、利用数组自带的sort方法
-
let arr = [
1,
2,
3,
4,
5,
6,
7,
8,
9];
-
-
function foo(arr) {
-
-
let cloneArr = arr.concat();
-
-
cloneArr.sort(
(n1, n2) =>
Math.random() -
0.5)
-
-
return cloneArr;
-
-
}
-
-
console.log(foo(arr));
第二种、利用递归函数对比
-
let arr = [
1,
2,
3,
4,
5,
6,
7,
8,
9];
-
-
function foo(arr) {
-
-
let result = [];
-
-
let cloneArr = arr.concat();
-
-
(
function(){
-
-
if (!cloneArr.length) {
return }
-
-
let index =
Math.floor(
Math.random() * cloneArr.length);
-
-
result = result.concat(cloneArr.splice(index,
1));
-
-
arguments.callee();
-
-
})()
-
-
return result;
-
-
}
-
-
console.log(foo(arr));
第三种、洗牌算法
-
let arr = [
1,
2,
3,
4,
5,
6,
7,
8,
9];
-
-
function foo(arr) {
-
-
let cloneArr = arr.concat();
-
-
let len = cloneArr.length;
-
-
for (
let i =
0; i < len; i++) {
-
-
let index =
Math.floor(
Math.random() * cloneArr.length);
-
-
let temp = cloneArr[index];
-
-
cloneArr[index] = cloneArr[i];
-
-
cloneArr[i] = temp;
-
-
}
-
-
return cloneArr;
-
-
}
-
-
console.log(foo(arr));
第一种:知道元素的宽和高
-
div
.box{
-
-
weight:
200px;
-
-
height:
400px;
-
-
position:absolute;
-
-
left:
50%;
-
-
top:
50%;
-
-
margin-left:-
100px;
-
-
margin-top:-
200px;
-
-
}
第二种:不知道元素的宽和高
-
div
.box{
-
-
weight:
200px;
-
-
height:
400px;
-
-
position:absolute;
-
-
left:
50%;
-
-
top:
50%;
-
-
transform:
translate(-50%,-50%);
-
-
}
第三种:flex布局
-
父级元素:{
-
-
display:flex;
-
-
flex-direction:row;
-
-
justify-content:center;
-
-
align-items:center;
-
-
}
-
-
子级元素:{
-
-
flex:
1
-
-
}
组合继承核心是在子类的构造函数中通过 Parent.call(this) 继承父类的属性,然后改变子类的原型为 new Parent() 来继承父类的函数
-
function Parent(value) {
-
-
this.val = value;
-
-
}
-
-
Parent.prototype.getValue =
function () {
-
-
console.log(
this.val);
-
-
}
-
-
-
function Child(value) {
-
-
Parent.call(
this, value);
-
-
}
-
-
Child.prototype =
new Parent();
-
-
const child =
new Child(
1);
-
-
child.getValue();
// 1
-
-
child
instanceof Parent;
// true
寄生组合继承核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数
-
function Parent(value) {
-
-
this.val = value;
-
-
}
-
-
Parent.prototype.getValue =
function () {
-
-
console.log(
this.val);
-
-
}
-
-
-
function Child(value) {
-
-
Parent.call(
this, value);
-
-
}
-
-
Child.prototype =
Object.create(Parent.prototype, {
-
-
constructor: {
-
-
value: Child,
-
-
enumerable:
false,
-
-
writable:
true,
-
-
configurable:
true
-
-
}
-
-
})
-
-
const child =
new Child(
1);
-
-
child.getValue();
// 1
-
-
child
instanceof Parent;
// true
Class 继承核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)
-
class Parent {
-
-
constructor(value) {
-
-
this.val = value;
-
-
}
-
-
getValue() {
-
-
console.log(
this.val);
-
-
}
-
-
}
-
-
class Child extends Parent {
-
-
constructor(value) {
-
-
super(value);
-
-
}
-
-
}
-
-
let child =
new Child(
1);
-
-
child.getValue();
// 1
-
-
child
instanceof Parent;
// true
第一种:Array.filter() + indexOf
-
function distinct(a, b) {
-
-
let arr = a.concat(b);
-
-
return arr.filter(
(item, index)=> {
-
-
return arr.indexOf(item) === index
-
-
})
-
-
}
第二种:双重 for 循环
-
function distinct(a, b) {
-
-
let arr = a.concat(b);
-
-
for (
let i=
0, len=arr.length; i
-
-
for (
let j=i+
1; j
-
-
if (arr[i] == arr[j]) {
-
-
arr.splice(j,
1);
-
-
// splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
-
-
len--;
-
-
j--;
-
-
}
-
-
}
-
-
}
-
-
return arr
-
-
}
第三种:for...of + includes()
-
function distinct(a, b) {
-
-
let arr = a.concat(b)
-
-
let result = []
-
-
for (
let i
of arr) {
-
-
!result.includes(i) && result.push(i)
-
-
}
-
-
return result
-
-
}
第四种:Array.sort()
-
function distinct(a, b) {
-
-
let arr = a.concat(b)
-
-
arr = arr.sort()
-
-
let result = [arr[
0]]
-
-
for (
let i=
1, len=arr.length; i
-
-
arr[i] !== arr[i
-1] && result.push(arr[i])
-
-
}
-
-
return result
-
-
}
第五种:new Set()
-
function distinct(a, b) {
-
-
return
Array.from(
new
Set([...a, ...b]))
-
-
}
第六种:for...of + Object
-
function distinct(a, b) {
-
-
let arr = a.concat(b)
-
-
let result = []
-
-
let obj = {}
-
-
for (
let i
of arr) {
-
-
if (!obj[i]) {
-
-
result.push(i)
-
-
obj[i] =
1
-
-
}
-
-
}
-
-
return result
-
-
}
找出字符串中出现次数最多的字符,并统计次数
-
let str =
"asddfssssaasswef";
-
-
let obj = {};
-
-
for (
let i =
0; i < str.length; i++) {
-
-
if (!obj[str.charAt(i)]) {
-
-
obj[str.charAt(i)] =
1;
-
-
}
else {
-
-
obj[str.charAt(i)]++;
-
-
}
-
-
}
-
-
console.log(obj);
-
-
let max =
0;
-
-
let charmax;
-
-
for (
let key
in obj) {
-
-
if (obj[key] > max) {
-
-
max = obj[key];
-
-
charmax = key;
-
-
}
-
-
}
-
-
console.log(
`出现最多的字符是${charmax},出现了${max}次`);
请用JavaScript写一个方法:要求实现格式化输出,比如输入9999999,输出为9,999,999
-
function splitNum(n) {
-
-
let b =
parseInt(n).toString();
-
-
let len = b.length;
-
-
if (len <
3) {
return b }
-
-
let r = len %
3,
-
-
b1 = b.slice(
0, r) +
"," + b.slice(r, len).match(
/\d{3}/g).join(
","),
-
-
b2 = b.slice(r, len).match(
/\d{3}/g).join(
",")
-
-
return r >
0 ? b1 : b2;
-
-
}
-
-
console.log(splitNum(
9999999));
JavaScript中的闭包,及几种写法和用途
闭包是密闭的容器,类似于set、map容器,存储数据的;且是一个对象,存放数据的格式是key:value
形成的条件是函数嵌套,内部函数引用外部函数的局部变量
第一种:给函数添加一些属性
-
function Circle(r) {
-
-
this.r = r;
-
-
}
-
-
Circle.PI =
3.14159;
-
-
Circle.prototype.area =
function() {
-
-
return Circle.PI *
this.r *
this.r;
-
-
}
-
-
let c =
new Circle(
1.0);
-
-
alert(c.area());
第二种:声明一个变量,将一个函数当作值赋给变量
-
let Circle =
function() {
-
-
let obj =
new
Object();
-
-
obj.PI =
3.14159;
-
-
obj.area =
function( r ) {
-
-
return
this.PI * r * r;
-
-
}
-
-
return obj;
-
-
}
-
-
let c =
new Circle();
-
-
alert( c.area(
1.0 ) );
第三种:new 一个对象,然后给对象添加属性和方法
-
let Circle =
new
Object();
-
-
Circle.PI =
3.14159;
-
-
Circle.Area =
function( r ) {
-
-
return
this.PI * r * r;
-
-
}
-
-
alert( Circle.Area(
1.0 ) );
第四种:let obj = {}就是声明一个空的对象
-
let Circle={
-
-
"PI":
3.14159,
-
-
"area":
function(r){
-
-
return
this.PI * r * r;
-
-
}
-
-
};
-
-
alert( Circle.area(
1.0) );
用途可以读取函数内部的变量;让变量的值始终保持在内存中
在JavaScript中实现不可变对象
-
const obj = {
-
-
num:
10,
-
-
obj: {
-
-
content:
"mutable object"
-
-
}
-
-
}
-
-
function deepFreeze(obj) {
-
-
let propNames =
Object.getOwnPropertyNames(obj);
-
-
propNames.forEach(
function (name) {
-
-
let prop = obj[name];
-
-
if (
typeof prop ==
'object' && prop !==
null) {
-
-
deepFreeze(prop);
-
-
}
-
-
});
-
-
return
Object.freeze(obj);
-
-
}
-
-
deepFreeze(obj);
-
-
obj.num =
5;
-
-
obj.obj = {
content:
"changed!" }
-
-
console.log(obj);
如何阻止冒泡
-
function stopBubble(e) {
-
-
if ( e && e.stopPropagation ){
-
-
e.stopPropagation();
-
-
}
else {
-
-
window.event.cancelBubble =
true;
-
-
}
-
-
}
数组的排序方式
第一种:arrayObject.sort(sortby)
-
function sortNum(a, b) {
-
-
return a - b;
-
-
}
-
-
let arr = [
524,
684,
5,
69,
15];
-
-
let res = arr.sort(sortNum);
-
-
console.log(res);
第二种:冒泡排序
-
function bubbleSort(arr) {
-
-
for (
let i =
0; i < arr.length; i++) {
-
-
for (
let j =
0; j < arr.length; j++) {
-
-
if (arr[i] < arr[j]) {
-
-
let temp = arr[j];
-
-
arr[j] = arr[i];
-
-
arr[i] = temp;
-
-
}
-
-
}
-
-
}
-
-
return arr;
-
-
}
-
-
let arr = [
524,
684,
5,
69,
15];
-
-
console.log(bubbleSort(arr));
第三种:快速排序
-
function quickSort(arr) {
-
-
if (arr.length <=
1) {
-
-
return arr;
-
-
}
-
-
let pivotIndex =
Math.floor(arr.length /
2),
-
-
pivot = arr.splice(pivotIndex,
1)[
0],
-
-
lef = [],
-
-
rig = [];
-
-
for (
var i =
0; i < arr.length; i++) {
-
-
if (arr[i] < pivot) {
-
-
lef.push(arr[i]);
-
-
}
else {
-
-
rig.push(arr[i]);
-
-
}
-
-
}
-
-
return quickSort(lef).concat(pivot, quickSort(rig));
-
-
}
-
-
let arr = [
524,
684,
5,
69,
15];
-
-
console.log(quickSort(arr));
第四种:插入排序
-
function insertSort(arr, a) {
-
-
for (
let i =
1; i < arr.length; i++) {
-
-
if (arr[i] >= a) {
-
-
for (
let j = arr.length; j > i; j--) {
-
-
arr[j] = arr[j -
1];
-
-
}
-
-
arr[i] = a;
-
-
break;
-
-
}
-
-
}
-
-
return arr;
-
-
}
-
-
let arr = [
5,
15,
69,
524,
684];
-
-
console.log(insertSort(arr,
92));
写一个function ,清除字符串前后的空格
第一种:重写trim方法
-
let str=’ abc ’;
-
-
if(!
String.prototype.trim){
-
-
String.prototype.trim =
function(){
-
-
return
this.replace(
/^\s+/,
"").replace(
/\s+$/,
"");
-
-
}
-
-
};
-
-
console.log(str.trim());
第二种:写fntrim去掉左右空格
-
let str=’ abc ’;
-
-
function fntrim(str){
-
-
return str.replace(
/^\s+/,
"").replace(
/\s+$/,
"");
-
-
};
-
-
Console.log(fntrim(str));
多行文本超出隐藏
-
overflow
:hidden; //超出文本隐藏
-
-
text-overflow
:ellipsis; //溢出省略号显示
-
-
display
:-webkit-box; //将对象作为弹性伸缩盒子
-
-
-webkit-box-orient
:vertical; //设置伸缩盒子的子元素排列方式
-从上到下垂直排列
-
-
-webkit-line-clamp
:2; //这个属性不是
css的规范属性,需要组合上面两个属性,数组代表显示的行数。
数组快速反转
-
function reverse(arr){
-
-
let num=
parseInt(arr.length/
2);
-
-
for(
var i=
0;i
-
-
let temp = arr[i];
-
-
arr[i] = arr[arr.length-i
-1];
-
-
arr[arr.length-i
-1] = temp;
-
-
}
-
-
return arr;
-
-
}
-
-
var A=[
1,
10,
5,
13,
26,
50,
2];
-
-
console.log(reverse(A));
用JavaScript实现随机选取10-100之间的10个数,存入一个数组并排序
-
let iArray=[];
-
-
function getRandom(istart,iend){
-
-
let iChoice=iend-istart+
1,
-
-
res=
Math.floor(
Math.random()*iChoice+istart);
-
-
return res;
-
-
}
-
-
for(
let i=
0;i<
10;i++){
-
-
iArray.push(getRandom(
10,
100));
-
-
}
-
-
iArray.sort(
(a,b)=>a>b);
-
-
console.log(iArray);
手动实现promise函数
-
function Promise(fn) {
-
-
let data =
undefined, reason =
undefined;
-
-
let succallbacks = [];
-
-
let failcallbacks = [];
-
-
let status =
"pending";
-
-
this.then =
function (fulfilled, rejected) {
-
-
return
new
Promise(
function(resolve,reject) {
//返回一个新的promise
-
-
function suc(value) {
//成功
-
-
let ret =
typeof fulfilled ===
'function' && fulfilled(value) || value;
-
-
if( ret &&
typeof ret [
'then'] ==
'function'){
//判断 then中的 返回的是否是promise对象,如果是注册then方法
-
-
ret.then(
function(value){
-
-
resolve(value);
-
-
});
-
-
}
else {
-
-
resolve(ret);
-
-
}
-
-
}
-
-
function errback(reason) {
//失败
-
-
reason =
typeof rejected ===
'function' && rejected(reason) || reason;
-
-
reject(reason);
-
-
}
-
-
if (status ===
'pending') {
-
-
succallbacks.push(suc);
-
-
failcallbacks.push(errback);
-
-
}
else
if(status ===
'fulfilled'){
-
-
suc(data);
-
-
}
else {
-
-
errback(reason);
-
-
}
-
-
})
-
-
}
-
-
function resolve(value) {
-
-
setTimeout(
function () {
//加入延时
-
-
status =
"fulfilled";
-
-
data = value;
-
-
succallbacks.forEach(
(callback) => {
-
-
callback(value);
-
-
})
-
-
},
0)
-
-
}
-
-
function reject(value) {
-
-
setTimeout(
function () {
-
-
status =
"rejected";
-
-
reason = value;
-
-
failcallbacks.forEach(
(callback) => {
-
-
callback(value);
-
-
})
-
-
},
0)
-
-
}
-
-
fn(resolve, reject);
-
-
}
-
-
let p =
new
Promise(
(resolve, reject) => {
-
-
setTimeout(
() => {
-
-
resolve(
1);
-
-
},
1000)
-
-
}) ;
-
-
p.then(
data =>{
-
-
return
new
Promise(
(resolve,reject) => {
//then 方法返回的是一个promise对象,故执行 promise中的then注册该结果,在resolve
-
-
setTimeout(
() => { resolve(
2);},
1000)})
-
-
}).then(
data =>{
-
-
console.log(data);
-
-
})
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
浅拷贝Object.assign()
-
let obj = {
a: {
a:
"kobe",
b:
39} };
-
-
var initalObj =
Object.assign({}, obj);
-
-
initalObj.a.a =
"wade";
-
-
console.log(obj.a.a);
浅拷贝Array.prototype.concat()
-
let arr = [
1,
3, {
-
-
username:
'kobe'
-
-
}];
-
-
let arr2=arr.concat();
-
-
arr2[
2].username =
'wade';
-
-
console.log(arr);
浅拷贝Array.prototype.slice()
-
let arr = [
1,
3, {
-
-
username:
' kobe'
-
-
}];
-
-
let arr3 = arr.slice();
-
-
arr3[
2].username =
'wade'
-
-
console.log(arr);
深拷贝JSON.parse(JSON.stringify())
-
let arr = [
1,
3, {
-
-
username:
' kobe'
-
-
}];
-
-
let arr4 =
JSON.parse(
JSON.stringify(arr));
-
-
arr4[
2].username =
'duncan';
-
-
console.log(arr, arr4);
深拷贝递归
-
function deepClone(initalObj, finalObj) {
-
-
let obj = finalObj || {};
-
-
for (
var i
in initalObj) {
-
-
let prop = initalObj[i];
-
-
if(prop === obj) {
-
-
continue;
-
-
}
-
-
if (
typeof prop ===
'object') {
-
-
obj[i] = (prop.constructor ===
Array) ? [] : {};
-
-
arguments.callee(prop, obj[i]);
-
-
}
else {
-
-
obj[i] = prop;
-
-
}
-
-
}
-
-
return obj;
-
-
}
深拷贝函数库lodash
-
let _ =
require(
'lodash');
-
-
let obj1 = {
-
-
a:
1,
-
-
b: {
f: {
g:
1 } },
-
-
c: [
1,
2,
3]
-
-
};
-
-
let obj2 = _.cloneDeep(obj1);
-
-
console.log(obj1.b.f === obj2.b.f);
深拷贝jquery的$.extend
-
let $ =
require(
'jquery');
-
-
let obj1 = {
-
-
a:
1,
-
-
b: {
f: {
g:
1 } },
-
-
c: [
1,
2,
3]
-
-