21届秋招前端面经 -- 淘宝

Vue和jquery区别

1.jQuery首先要获取到dom对象,然后对dom对象进行进行值的修改等操作

2.Vue是首先把值和js对象进行绑定,然后修改js对象的值,Vue框架就会自动把dom的值就行更新。可以简单的理解为Vue帮我们做了dom操作,我们以后用Vue就需要修改对象的值和做好元素和对象的绑定,Vue这个框架就会自动帮我们做好dom的相关操作。这种dom元素跟随JS对象值的变化而变化叫做单向数据绑定,如果JS对象的值也跟随着dom元素的值的变化而变化就叫做双向数据绑定

3.vue适用的场景:复杂数据操作的后台页面,表单填写页面。

jquery适用的场景:比如说一些html5的动画页面,一些需要js来操作页面样式的页面。

浏览器缓存

1、浏览器缓存的优缺点

优点:

  • 加快页面打开速度
  • 降低服务器压力
  • 减少网络损耗

缺点:

  • 缓存没有清理机制,这些缓存的文件会永久地保存在机器上
  • 给开发带来困扰,更新了开发内容,但是浏览器还是读取的原来缓存的内容,新的开发内容无法体现在浏览器上

2、web缓存的分类

web缓存存在于服务器和客户端之间

img

a.数据库数据缓存

Web应用,特别是社交网络服务类型的应用,往往关系比较复杂,数据库表繁多,如果频繁进行数据库查询,很容易导致数据库不堪重荷。为了提供查询的性能,会将查询后的数据放到内存中进行缓存,下次查询时,直接从内存缓存直接返回,提供响应效率。比如常用的缓存方案有memcached,redis等。

b.服务器端缓存

  • 代理服务器缓存

​ 代理服务器是浏览器和源服务器之间的中间服务器,浏览器先向这个中间服务器发起Web请求,经过处理后,再将请求转发到源服务器。代理服务器缓存的运作原理跟浏览器的运作原理差不多,只是规模更大。可以把它理解为一个共享缓存,不只为一个用户服务,一般为大量用户提供服务,因此在减少相应时间和带宽使用方面很有效,同一个副本会被重用多次。常见代理服务器缓存解决方案有Squid,Nginx,Apache等。

  • CDN缓存

CDN(Content delivery networks)缓存,也叫网关缓存、反向代理缓存。CDN缓存一般是由网站管理员自己部署,为了让他们的网站更容易扩展并获得更好的性能。浏览器先向CDN网关发起Web请求,网关服务器后面对应着一台或多台负载均衡源服务器,会根据它们的负载请求,动态将请求转发到合适的源服务器上。虽然这种架构负载均衡源服务器之间的缓存没法共享,但却拥有更好的处扩展性。从浏览器角度来看,整个CDN就是一个源服务器,浏览器和服务器之间的缓存机制,在这种架构下同样适用。

c.浏览器端缓存

浏览器缓存根据一套与服务器约定的规则进行工作,在同一个会话过程中会检查一次并确定缓存的副本足够新。如果你浏览过程中,比如前进或后退,访问到同一个图片,这些图片可以从浏览器缓存中调出而即时显现。

d.Web应用层缓存

应用层缓存指的是从代码层面上,通过代码逻辑和缓存策略,实现对数据,页面,图片等资源的缓存,可以根据实际情况选择将数据存在文件系统或者内存中,减少数据库查询或者读写瓶颈,提高响应效率。

3、浏览器缓存控制方法

a、meta标签控制

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

上述代码作用为告诉浏览器当前页面不需要被缓存,每次请求页面都需要去服务器请求资源。

b、http头信息控制

浏览器每次在向服务器发起 HTTP 请求获得请求结果后,会根据响应报文中 HTTP 头的缓存标识字段,来决定是否将请求结果存入浏览器缓存中。

浏览器每次在向服务器发起 HTTP 请求时,都会查找浏览器缓存中是否存在其相应的请求结果,然后根据缓存标识字段来决定其是直接使用之前缓存的副本还是再次向服务器发出 HTTP 请求。

对于每次浏览器第一次 HTTP 请求来说,浏览器缓存中并不存在其请求资源相应的副本,这时浏览器便会直接向服务器发出 HTTP 请求来获得相应的请求结果,并根据缓存标识字段,来决定是否将请求结果作为副本存入浏览器缓存中。

HTTP 保持已缓存数据与服务器数据之间充分一致的机制称为文档过期服务器再验证。而从浏览器缓存分类来看,也有将其分为强制缓存协商缓存

下面我就文档过期服务器再验证的机制做详细的介绍,下面表述的过程都是指浏览器缓存中已经存在其相应资源副本的情况。

文档过期

当浏览器发起 HTTP 请求时,会根据浏览器缓存中的缓存标识字段来验证文档(资源副本)是否过期。

上述说的缓存标识字段便是 ExpiresCache-Control

Expires 是服务器端在响应请求时用来规定资源的失效时间。

Cache-Control 是服务器端在响应请求时用来规定资源是否需要被浏览器缓存以及缓存的有效时间等。

在这里插入图片描述

Cache-Control 主要取值如下:

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)
  • no-cache:必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌(ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载
  • no-store:所有内容都不会被缓存或 Internet 临时文件中
  • must-revalidation/proxy-revalidation:如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证
  • max-age=xxx:缓存的内容将在 xxx 秒后失效

这里需要注意的是,no-cache 的作用是指跳过文档过期的验证而直接进行服务器再验证,而 no-store 是指资源禁止被缓存。

Expires 是 HTTP 1.0 的字段,而 Cache-Control 是 HTTP 1.1 的字段,当 Expires 与 Cache-Control 同时存在时,Cache-Control 的优先级要高于 Expires

在浏览器缓存中根据 Expires 和 Cache-Control 的值来验证文档(资源副本)是否过期的过程,称为 HTTP 的文档过期验证机制。若是文档没有过期,则浏览器会直接使用缓存中的文档作为返回结果,若是文档已经过期了,则需要进行服务器再验证。

服务器再验证

在浏览器缓存中,还保存了其它关于资源副本的描述字段,这些字段都是服务器返回信息头带过来的,如 Last-Modified 和 Etag。

在这里插入图片描述

Last-Modified 是服务器端在响应请求时用来说明资源的最后修改时间

Etag 是服务器端在响应请求时用来说明资源在服务器端的唯一标识

cookie

1、为什么会有cookie

Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。比如记住密码,这就是cookie的作用,当我们再次访问的时候,方便服务器直接根据我们的cookie来直接取上一次取过的东西(对于每一个cookie服务器会对这个cookie存储上一次我们拿过的数据,下一次对于同一个cookie的时候,就直接在这里取)

2、什么是Cookie

Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),(服务器告诉浏览器设置一下cookie),浏览器自动会将Cookiekey/value保存到某个目录下的文本文件内,下次请求同一网站时也会自动发送该Cookie给服务器,即添加在请求头部(前提是浏览器设置为启用cookie)。

3、Cookie 的特点

  • Cookie 具有保质期

即有永久的也含有临时的,每个浏览器都含有自己的cookie,每次请求的时候,都会根据domain来发送相应的cookie,可通过设置expires 、max-age来设定保存日期,不设置的话默认是临时存储,即关闭浏览器就消失。

document.cookie = 'expires=时间/max-age=秒'
  • 满足同源策略

虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。而且path也必须一样才能相互访问彼此的cookie,需要注意不同浏览器对path访问规定不一样,对于chrome,path必须为当前目录,设置为其他目录无效,只能当前页面只能访问当前目录及其以上的cookie

  • Cookie内存大小受限制

Cookie有个数和大小的限制,大小一般是4k

5种常见的网络I/O模型

  • blocking I/O – 阻塞类型的I/O

    阻塞模式在准备数据和拷贝数据两个过程都是阻塞的,在这期间客户端一直卡着,双方也没有数据交流。

  • nonblocking I/O – 非阻塞类型的I/O

    非阻塞模式下,如果数据还没有准备好,服务端会返回error,客户端根据这个error判断后,再次发起recv,直到数据准备好,这个过程虽然客户端也在这里卡着了,但是这期间一直和服务有着信息交换。

  • I/O Multiplexing – 多路复用型I/O

多路复用型IO,有的也称为事件驱动型IO,常用select,poll,epoll来处理多个IO连接状态。当某个IO连接的数据准备好了,select返回,通知用户进行read操作,这种IO的好处是一次可以监听多个连接,坏处是要两次调用,两次返回,单个连接和非阻塞IO没有多大的性能提升。

  • Signal-Driven I/O – 信号驱动型I/O

首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

  • Asynchronous I/O – 异步I/O

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作

SSL工作原理

1、SSL的分层结构

SSL协议分层
如图,SSL位于应用层和传输层之间,它能够为不论什么基于TCP等可靠连接的应用层协议提供安全性保证。SSL协议本身分为两层:

  1. 上层为SSL握手协议(SSL handshake protocol)、SSLpassword变化协议(SSL change cipher spec protocol)和SSL警告协议(SSL alert protocol)。
  2. 底层为SSL记录协议(SSL record protocol)。

当中:

  • SSL握手协议:是SSL协议很重要的组成部分。用来协商通信过程中使用的加密套件(加密算法、密钥交换算法和MAC算法等)、在server和client之间安全地交换密钥、实现server和client的身份验证。
  • SSLpassword变化协议:client和server端通过password变化协议通知对端。随后的报文都将使用新协商的加密套件和密钥进行保护和传输。
  • SSL警告协议:用来向通信对端报告告警信息,消息中包括告警的严重级别和描写叙述。
  • SSL记录协议:主要负责对上层的数据(SSL握手协议、SSLpassword变化协议、SSL警告协议和应用层协议报文)进行分块、计算并加入MAC值、加密。并把处理后的记录块传输给对端。

2、SSL握手过程

SSL通过握手过程在client和server之间协商会话参数,并建立会话。会话包括的主要参数有会话ID、对方的证书、加密套件(密钥交换算法、数据加密算法和MAC算法等)以及主密钥(master secret)。通过SSL会话传输的数据,都将采用该会话的主密钥和加密套件进行加密、计算MAC等处理。

不同情况下,SSL握手过程存在差异。

以下将分别描写叙述以下三种情况下的握手过程:

  • 仅仅验证server的SSL握手过程
    • SSL客户端(也是TCP的客户端)在TCP链接建立之后,发出一个ClientHello来发起握手,这个消息里面包含了自己可实现的算法列表和其它一些需要的消息,SSL的服务器端会回应一个ServerHello,这里面确定了这次通信所需要的算法,然后发过去自己的证书(里面包含了身份和自己的公钥)。Client在收到这个消息后会生成一个秘密消息,用SSL服务器的公钥加密后传过去,SSL服务器端用自己的私钥解密后,会话密钥协商成功,双方可以用同一份会话密钥来通信了。
  • 验证server和client的SSL握手过程
    • SSLserver发送Certificate Request消息。请求SSLclient将其证书发送给SSLserver。
    • SSLclient通过Certificate消息将携带自己公钥的证书发送给SSLserver。SSLserver验证该证书的合法性
    • SSLclient计算已交互的握手消息、主密钥的Hash值。利用自己的私钥对其进行加密,并通过Certificate Verify消息发送给SSLserver。
    • SSLserver计算已交互的握手消息、主密钥的Hash值。利用SSLclient证书中的公钥解密Certificate Verify消息,并将解密结果与计算出的Hash值比较。假设二者同样,则SSLclient身份验证成功。
  • 恢复原有会话的SSL握手过程
    • SSLclient发送Client Hello消息,消息中的会话ID设置为计划重用的会话的ID。
    • SSLserver假设同意重用该会话,则通过在Server Hello消息中设置同样的会话ID来应答。这样,SSLclient和SSLserver就能够利用原有会话的密钥和加密套件。不必又一次协商。
    • SSLclient发送Change Cipher Spec消息,通知SSLserver报文将采用原有会话的密钥和加密套件进行加密和MAC计算。
    • SSLclient计算已交互的握手消息的Hash值,利用原有会话的密钥和加密套件处理Hash值,并通过Finished消息发送给SSLserver,以便SSLserver推断密钥和加密套件是否正确。
    • 相同地。SSLserver发送Change Cipher Spec消息,通知SSLclient报文将采用原有会话的密钥和加密套件进行加密和MAC计算。
    • SSLserver计算已交互的握手消息的Hash值,利用原有会话的密钥和加密套件处理Hash值,并通过Finished消息发送给SSLclient。以便SSLclient推断密钥和加密套件是否正确。

浏览器缓存机制(HTTP定义的三种缓存机制)

  • 强制缓存

    服务端设置响应头Cache-Control:max-age=xxx,并且设置Expires响应头过期时间,客户端自行判断是否读取缓存。

    通常我们对于强制缓存的设置是服务端告诉客户端你刚刚已经请求过一次了,我们约定好十分钟内你再过来请求都直接读取缓存吧,意思也就是当客户端在十分钟内多次请求的话只有第一次会下载页面内容,其他的请求都是直接走缓存,不管我们页面在这期间有没有变化都不会影响客户端读取缓存。

  • 协商缓存

    • 修改时间:通过文件的最后修改时间判断该不该读取缓存,服务端设置响应头Last-Modified,客户端把上次服务端响应头中的Last-modified值通过if-modified-since 传递给服务端 , 服务端通过比较当前文件的修改时间和上次修改时间(上次传给客户端的值),如果相等那么说明文件修改时间没变也就是没变化
    • 文件内容:通过文件的内容来判断该不该读取缓存,服务端通过把文件内容读取出来,通过md5进行base64加密得出hash值,把这个值设置响应头Etag,客户端下一次请求通过if-none-match带过来,服务端再比对当前文件内容加密得出的hash值和上次是否一样,如果一样说明文件内容没有发生改变,这种方式是最准确的方式,但是也是最耗性能

箭头函数与普通函数的区别

  • 普通function的声明在变量提升中是最高的,箭头函数没有函数提升
  • 箭头函数没有this,函数体内部的this对象就是定义的时候所在的对象而不是使用时所在的对象
  • 箭头函数没有arguments对象,该对象在函数体内不存在,如果要用,可以使用rest参数
  • 箭头函数是匿名函数,不能作为构造函数,不能被new,没有property
  • call和apply方法只有参数,没有作用域
  • 不可以使用yield命令,因此箭头函数不能做Generator函数
  • 箭头函数没有原型属性

new操作符干了什么呢?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。

2、属性和方法被加入到 this 引用的对象中。

3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

JS数组添加元素的三种方式

1、push() 结尾添加

数组.push(元素)

参数 描述
newelement1 必需。要添加到数组的第一个元素。
newelement2 可选。要添加到数组的第二个元素。
newelementX 可选。可添加多个元素。

2、unshift() 头部添加

数组.unshift(元素)

参数 描述
newelement1 必需。向数组添加的第一个元素。
newelement2 可选。向数组添加的第二个元素。
newelementX 可选。可添加若干个元素。

3、splice() 方法向/从数组指定位置添加/删除项目,然后返回被删除的项目。

参数 描述
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, …, itemX 可选。向数组添加的新项目。

js数组比较

var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";

a == c;//true
b == c;//true
a == b;//false
//两个具有相同内容的数组进行 == 比较时不会相等,因为JavaScript里面Array是对象,==或===操作符只能比较两个对象是否是同一个实例,也就是是否是同一个对象引用。目前JavaScript没有内置的操作符判断对象的内容是否相同。

我们可以将数组转为字符串比较,但是会出现隐患,调试困难。

ES6新特性

  • let、const

  • 解构赋值

    ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

    解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错

  • 箭头函数

    • ES5 中的函数

      var fn = function () {
              
               console.log('我是es5') }
      
    • ES6中的箭头函数

      const fn = () => {
              
               console.log('我是es6') }
      // 等价于
      const fn = () => console.log('我是es6')
      //就是将ES5中的function()去掉,变成() =>,且当函数体只有一句程序的时候,大括号也能省略
      
    • 比较ES5中的函数和箭头函数:

      ES5中的函数可以使函数声明或者函数表达式,但是箭头函数只能是函数表达式,因此只在表达式有效的时候才能使用。
      
      什么是表达式有效时?
         1、存储在变量中
         2、当做参数传递给函数
         3、存储在对象属性中
      
  • 剩余参数

    剩余参数语法允许我们将一个不定数量的参数表示为一个数组。剩余参数一般和解构配合使用。

  • promise

  • for…of、for…in

    • for…of 循环将只循环访问对象中的值

    • for…in缺点: 循环循环访问所有可枚举的属性,意味着如果向数组的原型中添加任何其他属性或者方法,这些属性或方法也会出现在循环中,浪费了资源

    • for循环缺点:需要跟踪计时器和退出条件

      虽然 for 循环在循环数组时的确具有优势,但是某些数据结构不是数组,因此并非始终适合使用 loop 循环

    • foreach()循环缺点:它是一个数组方法,只用于数组中,局限性较大

  • 模板字面量(模板字符串)

    //在ES6之前,将字符串连接到一起的方法是 + 或者 concat() 方法,模板字面量本质上是包含嵌入式表达式的字符串字面量。
    //模板字面量用倒引号 ( `` )(而不是单引号 ( '' ) 或双引号( "" ))表示,可以包含用 ${expression} 表示的占位符
    let mes = `${
            
            dog1.name} and ${
            
            dog2.name} are dog`
    

js声明函数

在这里插入图片描述

new一个箭头函数会怎么样?

  • 箭头函数、没有prototype、没有自己的this指向、不可以使用arguments、自然不可以new。
  • new操作实质上是定义一个具有构造函数内置对象的实例。
    1. 创建一个javascript空对象 {};
    2. 将要实例化对象的原形链指向该对象原形。
    3. 绑定该对象为this的指向
    4. 返回该对象。

[1,2,3,4,5,6,7,8,9,10,11,12,13].map(parseInt)

  • 输出:

    [1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15]

  • 原因:

    • map函数

    将数组的每个元素传递给指定的函数处理,并返回处理后的数组,所以 [‘1’,‘2’,‘3’].map(parseInt) 就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。即分别求出 parseInt(‘1’,0), parseInt(‘2’,1), parseInt(‘3’,2)的结果。

    • parseInt函数(重点)

      1. 概念:以第二个参数为基数来解析第一个参数字符串,通常用来做十进制的向上取整(省略小数)如:parseInt(2.7) //结果为2
      2. 特点:接收两个参数parseInt(string,radix)

      string:字母(大小写均可)、数组、特殊字符(不可放在开头,特殊字符及特殊字符后面的内容不做解析)的任意字符串,如 ‘2’、‘2w’、‘2!’

      radix:解析字符串的基数,基数规则如下:

      1)区间范围介于2~36之间;

      2)当参数为 0,parseInt() 会根据十进制来解析;

      3)如果忽略该参数,默认的基数规则:

       如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数;parseInt("0xf")   //15 
      
       如果 string 以 0 开头,其后的字符解析为八进制或十六进制的数字;parseInt("08")   //8
      
       如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数;parseInt("88.99f")   //88
      
       只有字符串中的第一个数字会被返回。parseInt("10.33")   //返回10;
      
       开头和结尾的空格是允许的。parseInt(" 69 10 ")   //返回69
      
       如果字符串的第一个字符不能被转换为数字,返回 NaN。parseInt("f")  //返回NaN  而parseInt("f",16)  //返回15
      
  • 分析结果:
    [‘1’,‘2’,‘3’].map(parseInt)即:

parseInt(‘1’,0);radix 为 0,parseInt() 会根据十进制来解析,所以结果为 1;

parseInt(‘2’,1);radix 为 1,超出区间范围,所以结果为 NaN;

parseInt(‘3’,2);radix 为 2,用2进制来解析,应以 0 和 1 开头,所以结果为 NaN。

0.1+0.2 !== 0.3问题,为什么,怎么解决

  • 原因:

    在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004 ,所以条件判断结果为false。

  • 解决方法:

    设置一个误差范围值,通常称为”机器精度“,对于Javascript来说,这个值通常是2-52,即使用ES6中的Number.EPSILON,这个值正等于2-52。这个值非常非常小,在底层计算机已经帮我们运算好,并且无限接近0,但不等于0,。这个时候我们只要判断(0.1+0.2)-0.3小于Number.EPSILON,在这个误差的范围内就可以判定0.1+0.2===0.3为true。

  • 兼容性:

    在chrome中支持这个属性,但是IE并不支持(笔者的版本是IE10不兼容)。

vuex中mutation和action的区别

  • 流程顺序:

    “相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation。

  • 角色定位

    基于流程顺序,二者扮演不同的角色。

    • Mutation:专注于修改State,理论上是修改State的唯一途径。
    • Action:业务代码、异步请求。
  • 限制:角色不同,二者有不同的限制。

    • Mutation:必须同步执行。
    • Action:可以异步,但不能直接操作State。

vue首屏优化

vue中v-for的key值是做什么的,仅仅使用数组下标作为key值有什么缺点

  • 使用v-for更新已渲染的元素列表时,默认用就地复用策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素;
  • 我们在使用的使用经常会使用index(即数组的下标)来作为key,但是不推荐,因为此时如果我们在中间插入一条数据,所插位置后面的所有数据全部需要被重新渲染
    • 最好的办法是使用数组中不会变化的那一项作为key值,对应到项目中,即每条数据都有一个唯一的id,来标识这条数据的唯一性;使用id作为key值。

进程线程

  • 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。
  • 线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。

JS事件循环

  • 遇到同步任务直接执行,遇到异步任务分类为宏任务(macro-task)和微任务(micro-task)。

  • 宏任务(macro-task)、微任务(micro-task):

    • 宏任务包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
    • 微任务包括:process.nextTick, Promises, Object.observe, MutationObserver。
  • 第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。

    console.log(1);
    setTimeout(function() {
          
          
        console.log(2);
    })
    var promise = new Promise(function(resolve, reject) {
          
          
        console.log(3);
        resolve();
    })
    promise.then(function() {
          
          
        console.log(4);
    })
    console.log(5);
    
    //上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行。
    //遇到了 setTimeout ,就会等到过了指定的时间后将回调函数放入到宏任务的任务队列中。
    //遇到 Promise,将 then 函数放入到微任务的任务队列中。
    //整个事件循环完成之后,会去检测微任务的任务队列中是否存在任务,存在就执行。
    //第一次的循环结果打印为: 1,3,5,4。
    //接着再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。
    //检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
    //最终的结果就是 1,3,5,4,2。
    
    console.log('第一次循环主执行栈开始')
    setTimeout(function() {
          
          
      console.log('第二次循环开始,宏任务队列的第一个宏任务执行中')
      new Promise(function(resolve) {
          
          
         console.log('第二次循环的宏任务队列的第一个宏任务的微任务继续执行')
         resolve()
      }).then(function() {
          
          
         console.log('第二次循环的微任务队列的微任务执行')
      })
    }, 0)
    new Promise(function(resolve) {
          
          
      console.log('第一次循环主执行栈进行中...')
      resolve()
    }).then(function() {
          
          
     console.log('第一次循环微任务,第一次循环结束')
     setTimeout(function() {
          
          
        console.log('第二次循环的宏任务队列的第二个宏任务执行')
        })
    })
    console.log('第一次循环主执行栈完成')
    
    //打印顺序为:
    //第一次循环主执行栈开始
    //第一次循环主执行栈进行中...
    //第一次循环主执行栈完成
    //第一次循环微任务,第一次循环结束
    //第二次循环开始,宏任务队列的第一个宏任务执行中
    //第二次循环的宏任务队列的第一个宏任务的微任务继续执行
    //第二次循环的微任务队列的微任务执行
    //第二次循环的宏任务队列的第二个宏任务执行
    

sort

  • 对数组排序时,若为数值:

    let num = [1,3,5,-2,-8,6];
    num.sort(function(a,b){
          
          return b - a});
    //[6, 5, 3, 1, -2, -8]
    num.sort(function(a,b){
          
          return a - b});
    //[-8, -2, 1, 3, 5, 6]
    

setTimeOut实现setInterVal

//setInterVal:每隔一段时间重复调用该代码,直至调用被取消
  let num = 0,
      max = 10,
      intercalId = null;
  function incrementNumber() {
    
    
    num++;
    if(num == max){
    
    
      clearInterval(intercalId);
      alert("Done");
    }
    intercalId = setInterval(incrementNumber, 500);

  }
  //setTimeOut:多少秒后执行该代码
  //setTimeOut实现setInterVal
  let num = 0,
      max = 10;
  function IncrementNumber1() {
    
    
      num++;
      if(num < max){
    
    
        setTimeout(IncrementNumber1,500);
        console.log(num);
      }
      else {
    
    
        console.log("Done");
      }
  }
  setTimeout(IncrementNumber1,500)

猜你喜欢

转载自blog.csdn.net/Guoxuxinwen/article/details/109183938