javascript macrotaskk机制和microtask机制的区别以及对应的函数

深入浅出JavaScript事件循环机制(下) - 知乎专栏
{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"contributes":[{"sourceColumn":{"lastUpdated":,"description":"日常打代码 学会做总结","permission":"COLUMN_PUBLIC","memberId":,"contributePermission":"COLUMN_PUBLIC","translatedCommentPermission":"all","canManage":true,"intro":"技术自留地","urlToken":"luckyabby","id":30138,"imagePath":"v2-bd5c0f80461.jpg","slug":"luckyabby","applyReason":"0","name":"一只萌媛的自我修炼","title":"一只萌媛的自我修炼","url":"/luckyabby","commentPermission":"COLUMN_ALL_CAN_COMMENT","canPost":true,"created":,"state":"COLUMN_NORMAL","followers":273,"avatar":{"id":"v2-bd5c0f80461","template":"/{id}_{size}.jpg"},"activateAuthorRequested":false,"following":false,"imageUrl":"/v2-bd5c0f80461_l.jpg","articlesCount":14},"state":"accepted","targetPost":{"titleImage":"/v2-ce7f979e0e0_r.jpg","lastUpdated":,"imagePath":"v2-ce7f979e0e0.jpg","permission":"ARTICLE_PUBLIC","topics":[225,769],"summary":"在上一篇文章里面我大致介绍了JavaScript的事件循环机制,但是最后还留下了一段代码和几个问题。 那我们先从这段代码开始看哇(function test() {\n setTimeout(function() {console.log(4)}, 0);\n new Promise(function executor(resolve) {\n console.log(1)…","copyPermission":"ARTICLE_COPYABLE","translatedCommentPermission":"all","likes":0,"origAuthorId":0,"publishedTime":"T22:44:19+08:00","sourceUrl":"","urlToken":,"id":2682270,"withContent":false,"slug":,"bigTitleImage":false,"title":"深入浅出JavaScript事件循环机制(下)","url":"/p/","commentPermission":"ARTICLE_ALL_CAN_COMMENT","snapshotUrl":"","created":,"comments":0,"columnId":30138,"content":"","parentId":0,"state":"ARTICLE_PUBLISHED","imageUrl":"/v2-ce7f979e0e0_r.jpg","author":{"bio":"努力成为一只被自己认可的程序媛","isFollowing":false,"hash":"ca055e9eb1","uid":407600,"isOrg":false,"slug":"xia-yan-luo-47","isFollowed":false,"description":"一个有意思的人吧\n话虽然多但是不喜欢尬聊\n有狗勿强撩\nBlog: \nGithub: /LuckyAbby","name":"柳兮","profileUrl":"/people/xia-yan-luo-47","avatar":{"id":"v2-132df24b3dadee9cbc8f17cae8f39624","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"memberId":,"excerptTitle":"","voteType":"ARTICLE_VOTE_CLEAR"},"id":606156}],"title":"深入浅出JavaScript事件循环机制(下)","author":"xia-yan-luo-47","content":"在上一篇文章里面我大致介绍了JavaScript的事件循环机制,但是最后还留下了一段代码和几个问题。那我们先从这段代码开始看哇(function test() {\n
setTimeout(function() {console.log(4)}, 0);\n
new Promise(function executor(resolve) {\n
console.log(1);\n
for( var i=0 ; i&10000 ; i++ ) {\n
i == 9999 && resolve();\n
console.log(2);\n
}).then(function() {\n
console.log(5);\n
console.log(3);\n})()\n在这段代码里面,setTimeout和Promise都被称之为任务源,来自不同任务源的回调函数会被放进不同的任务队列里面。setTimeout的回调函数被放进setTimeout的任务队列之中。而对于Promise,它的回调函数并不是传进去的executer函数,而是其异步执行的then方法里面的参数,被放进Promise的任务队列之中。也就是说Promise的第一个参数并不会被放进Promise的任务队列之中,而会在当前队列就执行。其中setTimeout和Promise的任务队列叫做macro-task(宏任务),当然如我们所想,还有micro-task(微任务)。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver其中上面的setImmediate和process.nextTick是Node.JS里面的API,浏览器里面并没有,这里就当举例,不必纠结具体是怎么实现的。事件循环的顺序是从script开始第一次循环,随后全局上下文进入函数调用栈,碰到macro-task就将其交给处理它的模块处理完之后将回调函数放进macro-task的队列之中,碰到micro-task也是将其回调函数放进micro-task的队列之中。直到函数调用栈清空只剩全局执行上下文,然后开始执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次执行macro-task中的一个任务队列,执行完之后再执行所有的micro-task,就这样一直循环。分析执行过程下面分析的思路按照波同学之前所写的中的思路进行分析。以之前的栗子作为分析的对象,来分析事件循环机制究竟是怎么执行代码的(function test() {\n
setTimeout(function() {console.log(4)}, 0);\n
new Promise(function executor(resolve) {\n
console.log(1);\n
for( var i=0 ; i&10000 ; i++ ) {\n
i == 9999 && resolve();\n
console.log(2);\n
}).then(function() {\n
console.log(5);\n
console.log(3);\n})()\n注意下面所有图中的setTimeout任务队和最后的函数调用栈中存放的都是setTimeout的回调函数,并不是整个setTimeout定时器。1.首先,script任务源先执行,全局上下文入栈。2.script任务源的代码在执行时遇到setTimeout,作为一个macro-task,将其回调函数放入自己的队列之中。2.script任务源的代码在执行时遇到setTimeout,作为一个macro-task,将其回调函数放入自己的队列之中。3.script任务源的代码在执行时遇到Promise实例。Promise构造函数中的第一个参数是在当前任务直接执行不会被放入队列之中,因此此时输出 1 。3.script任务源的代码在执行时遇到Promise实例。Promise构造函数中的第一个参数是在当前任务直接执行不会被放入队列之中,因此此时输出 1 。4.在for循环里面遇到resolve函数,函数入栈执行之后出栈,此时Promise的状态变成Fulfilled。代码接着执行遇到console.log(2),输出2。4.在for循环里面遇到resolve函数,函数入栈执行之后出栈,此时Promise的状态变成Fulfilled。代码接着执行遇到console.log(2),输出2。5.接着执行,代码遇到then方法,其回调函数作为micro-task入栈,进入Promise的任务队列之中。6.代码接着执行,此时遇到console.log(3),输出3。6.代码接着执行,此时遇到console.log(3),输出3。7.输出3之后第一个宏任务script的代码执行完毕,这时候开始开始执行所有在队列之中的micro-task。then的回调函数入栈执行完毕之后出栈,这时候输出57.输出3之后第一个宏任务script的代码执行完毕,这时候开始开始执行所有在队列之中的micro-task。then的回调函数入栈执行完毕之后出栈,这时候输出58.这时候所有的micro-task执行完毕,第一轮循环结束。第二轮循环从setTimeout的任务队列开始,setTimeout的回调函数入栈执行完毕之后出栈,此时输出4。8.这时候所有的micro-task执行完毕,第一轮循环结束。第二轮循环从setTimeout的任务队列开始,setTimeout的回调函数入栈执行完毕之后出栈,此时输出4。总结总的来说就是:不同的任务会放进不同的任务队列之中。先执行macro-task,等到函数调用栈清空之后再执行所有在队列之中的micro-task。等到所有micro-task执行完之后再从macro-task中的一个任务队列开始执行,就这样一直循环。当有多个macro-task(micro-task)队列时,事件循环的顺序是按上文macro-task(micro-task)的分类中书写的顺序执行的。测试说到这里,我们应该都明白了,下面是一个复杂的代码段(改自),里面有混杂着的micro-task和macro-task,自己画图试试流程哇,然后再用node执行看看输出的顺序是否一致。console.log('golb1');\nsetImmediate(function() {\n
console.log('immediate1');\n
process.nextTick(function() {\n
console.log('immediate1_nextTick');\n
new Promise(function(resolve) {\n
console.log('immediate1_promise');\n
resolve();\n
}).then(function() {\n
console.log('immediate1_then')\n
})\n})\nsetTimeout(function() {\n
console.log('timeout1');\n
process.nextTick(function() {\n
console.log('timeout1_nextTick');\n
new Promise(function(resolve) {\n
console.log('timeout1_promise');\n
resolve();\n
}).then(function() {\n
console.log('timeout1_then')\n
setTimeout(function() {\n
\tconsole.log('timeout1_timeout1');\n
process.nextTick(function() {\n
console.log('timeout1_timeout1_nextTick');\n
setImmediate(function() {\n
\tconsole.log('timeout1_setImmediate1');\n
});\n})\nnew Promise(function(resolve) {\n
console.log('glob1_promise');\n
resolve();\n}).then(function() {\n
console.log('glob1_then')\n})\nprocess.nextTick(function() {\n
console.log('glob1_nextTick');\n})\n以上就是我所理解的事件循环机制,有偏差之处烦请指正。欢迎大家来交流哇,嘻嘻嘻。参考资料:","updated":"T14:44:19.000Z","canComment":false,"commentPermission":"anyone","commentCount":17,"collapsedCount":0,"likeCount":32,"state":"published","isLiked":false,"slug":"","lastestTipjarors":[],"isTitleImageFullScreen":false,"rating":"none","titleImage":"/v2-ce7f979e0e0_r.jpg","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"前端开发"},{"url":"/topic/","id":"","name":"JavaScript"}],"adminClosedComment":false,"titleImageSize":{"width":656,"height":337},"href":"/api/posts/","excerptTitle":"","column":{"slug":"luckyabby","name":"一只萌媛的自我修炼"},"tipjarState":"activated","tipjarTagLine":"真诚赞赏,手留余香","sourceUrl":"","pageCommentsCount":17,"tipjarorCount":0,"annotationAction":[],"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T22:44:19+08:00","url":"/p/","lastestLikers":[{"bio":null,"isFollowing":false,"hash":"7a5f67ee878d9e2085ac7c","uid":609700,"isOrg":false,"slug":"qian-long-16-72","isFollowed":false,"description":"","name":"谦龙","profileUrl":"/people/qian-long-16-72","avatar":{"id":"da8e974dc","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"难的是:日复一日,分分秒秒。","isFollowing":false,"hash":"9e962e59a9f2959bb10ab","uid":789700,"isOrg":false,"slug":"benjamin-15-82","isFollowed":false,"description":"程序员。","name":"CV大虾","profileUrl":"/people/benjamin-15-82","avatar":{"id":"b93dc67abd0eeccb3df3169","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"前端菜比","isFollowing":false,"hash":"8a63ed7dcb0","uid":64,"isOrg":false,"slug":"liu-tao-74-74","isFollowed":false,"description":"/liutao\n/","name":"刘涛","profileUrl":"/people/liu-tao-74-74","avatar":{"id":"9e915fffc7ef2f","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"data mining","isFollowing":false,"hash":"d943fe7c9e12c1edcce6f","uid":20,"isOrg":false,"slug":"bin-li-25-58","isFollowed":false,"description":"","name":"bin li","profileUrl":"/people/bin-li-25-58","avatar":{"id":"a87e71e14","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"you know nothing","isFollowing":false,"hash":"e85a1f288cc8e05fabc6dd30de31decb","uid":92,"isOrg":false,"slug":"wang-fu-yan-10","isFollowed":false,"description":"见头像","name":"splash","profileUrl":"/people/wang-fu-yan-10","avatar":{"id":"38aeb03aeef2fcbdd59116a8","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false}],"summary":"在上一篇文章里面我大致介绍了JavaScript的事件循环机制,但是最后还留下了一段代码和几个问题。 那我们先从这段代码开始看哇(function test() {\n setTimeout(function() {console.log(4)}, 0);\n new Promise(function executor(resolve) {\n console.log(1)…","reviewingCommentsCount":0,"meta":{"previous":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/50/v2-ecf13e5a858_xl.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"前端开发"},{"url":"/topic/","id":"","name":"JavaScript"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"努力成为一只被自己认可的程序媛","isFollowing":false,"hash":"ca055e9eb1","uid":407600,"isOrg":false,"slug":"xia-yan-luo-47","isFollowed":false,"description":"一个有意思的人吧\n话虽然多但是不喜欢尬聊\n有狗勿强撩\nBlog: \nGithub: /LuckyAbby","name":"柳兮","profileUrl":"/people/xia-yan-luo-47","avatar":{"id":"v2-132df24b3dadee9cbc8f17cae8f39624","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"luckyabby","name":"一只萌媛的自我修炼"},"content":"最近琢磨了好久的Javascript的事件循环机制,看了很多国内的博客总觉得写的还是不够深,很多都只说了Javascript的事件分为同步任务和异步任务,遇到同步任务就放在执行栈中执行,而碰到异步任务就放到任务队列之中,等到执行栈执行完毕之后再去执行任务队列之中的事件。自己对大概的基础有所了解之后也没接着深入去查资料,这就导致我在面试的时候被面试官一点一点深挖的时候就懵了(囧函数调用栈与任务队列Javascript有一个main thread 主进程和call-stack(一个调用堆栈),在对一个调用堆栈中的task处理的时候,其他的都要等着。当在执行过程中遇到一些类似于setTimeout等异步操作的时候,会交给浏览器的其他模块(以webkit为例,是webcore模块)进行处理,当到达setTimeout指定的延时执行的时间之后,task(回调函数)会放入到任务队列之中。一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有task执行完毕之后,接着去执行任务队列之中的task(回调函数)。用Philip Roberts的演讲之中的一张图表示就是在上图中,调用栈中遇到DOM操作、ajax请求以及setTimeout等WebAPIs的时候就会交给浏览器内核的其他模块进行处理,webkit内核在Javasctipt执行引擎之外,有一个重要的模块是webcore模块。对于图中WebAPIs提到的三种API,webcore分别提供了DOM Binding、network、timer模块来处理底层实现。等到这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等栈中的task执行完之后再去执行任务队列之中的回调函数。从setTimeout看事件循环机制下面用Philip Roberts的演讲中的一个栗子来说明事件循环机制究竟是怎么执行setTimeout的。首先main()函数的执行上下文入栈(对执行上下文还不了解的可以看我的上一篇博客)。代码接着执行,遇到console.log(‘Hi’),此时log(‘Hi’)入栈,console.log方法只是一个webkit内核支持的普通的方法,所以log(‘Hi’)方法立即被执行。此时输出’Hi’。当遇到setTimeout的时候,执行引擎将其添加到栈中。调用栈发现setTimeout是之前提到的WebAPIs中的API,因此将其出栈之后将延时执行的函数交给浏览器的timer模块进行处理。timer模块去处理延时执行的函数,此时执行引擎接着执行将log(‘SJS’)添加到栈中,此时输出’SJS’。当timer模块中延时方法规定的时间到了之后就将其放入到任务队列之中,此时调用栈中的task已经全部执行完毕。调用栈中的task执行完毕之后,执行引擎会接着看执行任务队列中是否有需要执行的回调函数。这里的cb函数被执行引擎添加到调用栈中,接着执行里面的代码,输出’there’。等到执行结束之后再出栈。小结上面的这一个流程解释了当浏览器遇到setTimeout之后究竟是怎么执行的,相类似的还有前面图中提到的另外的API以及另外一些异步的操作。总结上文说的,主要就是以下几点:所有的代码都要通过函数调用栈中调用执行。当遇到前文中提到的APIs的时候,会交给浏览器内核的其他模块进行处理。任务队列中存放的是回调函数。等到调用栈中的task执行完之后再回去执行任务队列之中的task。测试for (var i = 0; i & 5; i++) {\n
setTimeout(function() {\n
console.log(new Date, i);\n
}, 1000);\n}\nconsole.log(new Date, i);\n这段代码是我从网上前不久的一篇文章中找到的,现在我们就分析一下这段代码究竟是怎么输出最后文章中所说的最后的执行状态:40% 的人会描述为:5 -& 5,5,5,5,5,即第 1 个 5 直接输出,1 秒之后,输出 5 个 5;首先i=0时,满足条件,执行栈执行循环体里面的代码,发现是setTimeout,将其出栈之后把延时执行的函数交给Timer模块进行处理。当i=1,2,3,4时,均满足条件,情况和i=0时相同,因此timer模块里面有5个相同的延时执行的函数。当i=5的时候,不满足条件,因此for循环结束,console.log(new Date, i)入栈,此时的i已经变成了5。因此输出5。此时1s已经过去,timer模块将5个回调函数按照注册的顺序返回给任务队列。执行引擎去执行任务队列中的函数,5个function依次入栈执行之后再出栈,此时的i已经变成了5。因此几乎同时输出5个5。因此等待的1s的时间其实只有输出第一个5之后需要等待1s,这1s的时间是timer模块需要等到的规定的1s时间之后才将回调函数交给任务队列。等执行栈执行完毕之后再去执行任务对列中的5个回调函数。这期间是不需要等待1s的。因此输出的状态就是:5 -& 5,5,5,5,5,即第1个 5 直接输出,1s之后,输出 5个5;问题看到这里,对事件循环机制有了一个大概的了解了,可是细想,其中还有一些另外值得深入的问题。下面通过一个栗子来说明:(function test() {\n
setTimeout(function() {console.log(4)}, 0);\n
new Promise(function executor(resolve) {\n
console.log(1);\n
for( var i=0 ; i&10000 ; i++ ) {\n
i == 9999 && resolve();\n
console.log(2);\n
}).then(function() {\n
console.log(5);\n
console.log(3);\n})()\n在这段代码里面,多了一个promise,那么我们可以思考下面这个问题:promise的task会放在不同的任务队列里面,那么setTimeout的任务队列和promise的任务队列的执行顺序又是怎么的呢?到这里大家看了我说了这么多的task,那么上文中一直提到的task究竟包括了什么?具体是怎么分的?如果到这里大家还是没太懂的话,那么接下来我会接着深入再细说不同task的事件循环机制。当然,以上都是我自己鄙陋的见解,欢迎大家批评指正。参考资料:","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T15:24:34+08:00","url":"/p/","title":"深入浅出Javascript事件循环机制(上)","summary":"最近琢磨了好久的Javascript的事件循环机制,看了很多国内的博客总觉得写的还是不够深,很多都只说了Javascript的事件分为同步任务和异步任务,遇到同步任务就放在执行栈中执行,而碰到异步任务就放到任务队列之中,等到执行栈执行完毕之后再去执行任务队列…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":20,"likesCount":34},"next":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/50/v2-c9c04b15cfd8c2e80be4_xl.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"JavaScript"},{"url":"/topic/","id":"","name":"ECMAScript 2015"},{"url":"/topic/","id":"","name":"前端开发"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"努力成为一只被自己认可的程序媛","isFollowing":false,"hash":"ca055e9eb1","uid":407600,"isOrg":false,"slug":"xia-yan-luo-47","isFollowed":false,"description":"一个有意思的人吧\n话虽然多但是不喜欢尬聊\n有狗勿强撩\nBlog: \nGithub: /LuckyAbby","name":"柳兮","profileUrl":"/people/xia-yan-luo-47","avatar":{"id":"v2-132df24b3dadee9cbc8f17cae8f39624","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"luckyabby","name":"一只萌媛的自我修炼"},"content":"扩展运算符与剩余操作符都是以三点开头的操作符,二者长的很像,只是在用法上有些差别。它们已经被 ES6 数组支持,能解决很多之前 arguments 解决起来很麻烦的问题。扩展运算符因为在React里面经常碰到,自己就做了很简单的几个归纳。主要几种在以下几个方面。1. 改变函数的调用将数组作为函数的参数传递进去的时候,通常使用的方法是Function.prototype.apply,例如下面的例子var students = ['Abby','Andy'];\nvar otherStudents = ['Jane','Tom'];\nArray.prototype.push.apply(students,otherStudents);\nconsole.log(students); // Abby Andy Jane Tom\n这样写起来就不太方便,现在有了延展操作符,它可以将数组,更确切的说是(可遍历对象)进行展开然后作为函数入参进行调用。现在只需要像下面简单地直接调用push方法就行了。var students = ['Abby','Andy'];\nvar otherStudents = ['Jane','Tom'];\nstudents.push(...otherStudents);\nconsole.log(students); // Abby Andy Jane Tom\n2. 数组构造在延展操作符之前,我们如果想让一个数组成为另一个数组的一部分,不可避免地就要用到push, splice, concat等等。现在,延展操作符允许我们简单地操作就能实现,如下面代码var parts = ['shoulders', 'knees'];\nvar lyrics = ['head', ...parts, 'and', 'toes'];\nconsole.log(lyrics)// ['head', 'shoulders, 'knees', 'and', 'toes']\n除此之外,我们还可以使用它来快速复制一个数组。var arr = [1, 2, 3];\nvar arr2 = [...arr]; // like arr.slice()\narr2.push(4);\n// arr2 becomes [1, 2, 3, 4]\n// arr remains unaffected\n3. 数组解构除了可以快速构造数组之外,还可以用来实现数组的解构。可以用来提取数组中的部分值,并且提取的结果是一个数组对象。注意延展操作符总是应该位于解构赋值的最后一个位置。举个栗子。var students = ['Abby', 'Andy', 'Jane', 'Tom'];\nvar somestudents, otherS\n[somestudents, ...otherStudents] =\nconsole.log(somestudents); // Abby\nconsole.log(otherStudents); // Andy Jane Tom\n4. 将类数组对象转换成数组扩展运算符可以将一个类数组对象中索引范围在[0,length)之间的所有属性的值添加到一个数组中,这样可以得到一个真正的数组:var nodeList = document.querySelectorAll('div');\nvar array = [...nodeList];\n5. React里面的延展属性(Spread Attributes)React里面,延展操作符可以在JSX中,{…props}可以将ReactElement的props属性展开。var props = {};\nprops.foo =\nprops.bar =\nvar component = &Component {...props} /&;\n传入对象的属性会被复制到组件内。它能被多次使用,也可以和其它属性一起用。注意顺序很重要,后面的会覆盖掉前面的。var props = { foo: 'default' };\nvar component = &Component {...props} foo={'override'} /&;\nconsole.log(component.props.foo); // 'override'\n剩余操作符剩余参数(rest parameter)允许长度不确定的实参表示为一个数组。如果一个函数的最后一个形参是以 … 为前缀的,则在函数被调用时,该形参会成为一个数组,数组中的元素都是传递给该函数的多出来的实参的值。1. 获取参数剩余操作符可以用来方便地获取传进来的参数。function fn(a, b, ...args) {
return args.\n}\nfn(1, 2, 3, 4, 5 ); // 传进来的参数的个数 3\n其中第一个形参a对应的是1,第二个形参b对应的2,…args表示的就是[3, 4, 5]。2. 与arguments的不同上面args可以有length属性,也说明了args是一个数组而不是像arguments一样是一个伪数组,因此能在在它上面直接使用所有的数组方法,比如 sort,map,forEach,pop。arguments想要变成数组,可以通过Array.prototype.slice.call方法,使用剩余操作符可以避免将arguments转为数组的麻烦。这种用法和上面扩展运算符的用法还有点异曲同工之妙。// 下面的代码模拟了剩余数组\nfunction fn1(a, b){\n
var args = Array.prototype.slice.call(arguments, fn1.length);\n
console.log(args.length);\n}\n \n// 现在代码可以简化为这样了\nfunction fn2(a, b, ...args) {\n
console.log(args.length);\n}\nfn1(1,2,3); // 1\nfn2(1,2,3); // 1\n扩展运算符与剩余操作符的区别关于扩展运算符与剩余操作符之间的区别,简单地说,在某种程度上,剩余操作符和扩展运算符相反,扩展运算符会“展开”数组变成多个元素,剩余操作符会收集多个元素和“压缩”成一个单一的元素。以上就是自己总结的扩展运算符与剩余操作符较为简单常用的用法,如有不恰当之处欢迎指正。参考资料","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T00:10:19+08:00","url":"/p/","title":"ES6--扩展运算符与剩余操作符","summary":"扩展运算符与剩余操作符都是以三点开头的操作符,二者长的很像,只是在用法上有些差别。它们已经被 ES6 数组支持,能解决很多之前 arguments 解决起来很麻烦的问题。 扩展运算符因为在React里面经常碰到,自己就做了很简单的几个归纳。主要几种在以下几个方…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":8,"likesCount":17}},"annotationDetail":null,"commentsCount":17,"likesCount":32,"FULLINFO":true}},"User":{"xia-yan-luo-47":{"isFollowed":false,"name":"柳兮","headline":"一个有意思的人吧\n话虽然多但是不喜欢尬聊\n有狗勿强撩\nBlog: \nGithub: /LuckyAbby","avatarUrl":"/v2-132df24b3dadee9cbc8f17cae8f39624_s.jpg","isFollowing":false,"type":"people","slug":"xia-yan-luo-47","bio":"努力成为一只被自己认可的程序媛","hash":"ca055e9eb1","uid":407600,"isOrg":false,"description":"一个有意思的人吧\n话虽然多但是不喜欢尬聊\n有狗勿强撩\nBlog: \nGithub: /LuckyAbby","profileUrl":"/people/xia-yan-luo-47","avatar":{"id":"v2-132df24b3dadee9cbc8f17cae8f39624","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{"experimentFeatures":{"ge3":"ge3_9","ge2":"ge2_1","isOffice":"false","nwebStickySidebar":"sticky","qrcodeLogin":"qrcode","favAct":"default","default":"None","mobileQaPageProxyHeifetz":"m_qa_page_nweb","newMore":"new","newBuyBar":"livenewbuy3","newMobileColumnAppheader":"new_header","appStoreRateDialog":"close","sendZaMonitor":"true","homeUi2":"default","answerRelatedReadings":"qa_recommend_with_ads_and_article","wechatShareModal":"wechat_share_modal_show","iOSNewestVersion":"4.2.0","qaStickySidebar":"sticky_sidebar","androidProfilePanel":"panel_b","liveStore":"ls_a2_b2_c1_f2","zcmLighting":"zcm"}},"columns":{"next":{},"luckyabby":{"following":false,"canManage":false,"href":"/api/columns/luckyabby","name":"一只萌媛的自我修炼","creator":{"slug":"xia-yan-luo-47"},"url":"/luckyabby","slug":"luckyabby","avatar":{"id":"v2-bd5c0f80461","template":"/{id}_{size}.jpg"}}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"edition":{},"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{},"message":{"newCount":0},"pushNotification":{"newCount":0}}}

我要回帖

更多关于 macro task 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信