Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91在线国产观看,成人福利在线视频,亚洲精品午夜视频

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制

          文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。

          不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發(fā)工作,我們經(jīng)常會(huì)遇到這樣的情況:給定的幾行代碼,我們需要知道其輸出內(nèi)容和順序。因?yàn)閖avascript是一門單線程語言,所以我們可以得出結(jié)論:

          • javascript是按照語句出現(xiàn)的順序執(zhí)行的

          看到這里讀者要打人了:我難道不知道js是一行一行執(zhí)行的?還用你說?稍安勿躁,正因?yàn)閖s是一行一行執(zhí)行的,所以我們以為js都是這樣的:

          let a = '1';
          console.log(a);
          
          let b = '2';
          console.log(b);

          然而實(shí)際上js是這樣的:

          setTimeout(function(){
              console.log('定時(shí)器開始啦')
          });
          
          new Promise(function(resolve){
              console.log('馬上執(zhí)行for循環(huán)啦');
              for(var i = 0; i < 10000; i++){
                  i == 99 && resolve();
              }
          }).then(function(){
              console.log('執(zhí)行then函數(shù)啦')
          });
          
          console.log('代碼執(zhí)行結(jié)束');

          依照js是按照語句出現(xiàn)的順序執(zhí)行這個(gè)理念,我自信的寫下輸出結(jié)果:

          //"定時(shí)器開始啦"
          //"馬上執(zhí)行for循環(huán)啦"
          //"執(zhí)行then函數(shù)啦"
          //"代碼執(zhí)行結(jié)束"

          去chrome上驗(yàn)證下,結(jié)果完全不對,瞬間懵了,說好的一行一行執(zhí)行的呢?

          我們真的要徹底弄明白javascript的執(zhí)行機(jī)制了。

          1.關(guān)于javascript

          javascript是一門單線程語言,在最新的HTML5中提出了Web-Worker,但javascript是單線程這一核心仍未改變。所以一切javascript版的"多線程"都是用單線程模擬出來的,一切javascript多線程都是紙老虎!

          2.javascript事件循環(huán)

          既然js是單線程,那就像只有一個(gè)窗口的銀行,客戶需要排隊(duì)一個(gè)一個(gè)辦理業(yè)務(wù),同理js任務(wù)也要一個(gè)一個(gè)順序執(zhí)行。如果一個(gè)任務(wù)耗時(shí)過長,那么后一個(gè)任務(wù)也必須等著。那么問題來了,假如我們想瀏覽新聞,但是新聞包含的超清圖片加載很慢,難道我們的網(wǎng)頁要一直卡著直到圖片完全顯示出來?因此聰明的程序員將任務(wù)分為兩類:

          • 同步任務(wù)
          • 異步任務(wù)

          當(dāng)我們打開網(wǎng)站時(shí),網(wǎng)頁的渲染過程就是一大堆同步任務(wù),比如頁面骨架和頁面元素的渲染。而像加載圖片音樂之類占用資源大耗時(shí)久的任務(wù),就是異步任務(wù)。關(guān)于這部分有嚴(yán)格的文字定義,但本文的目的是用最小的學(xué)習(xí)成本徹底弄懂執(zhí)行機(jī)制,所以我們用導(dǎo)圖來說明:

          導(dǎo)圖要表達(dá)的內(nèi)容用文字來表述的話:

          • 同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場所",同步的進(jìn)入主線程,異步的進(jìn)入Event Table并注冊函數(shù)。
          • 當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。
          • 主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
          • 上述過程會(huì)不斷重復(fù),也就是常說的Event Loop(事件循環(huán))。

          我們不禁要問了,那怎么知道主線程執(zhí)行棧為空啊?js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會(huì)去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。

          說了這么多文字,不如直接一段代碼更直白:

          let data = [];
          $.ajax({
              url:www.javascript.com,
              data:data,
              success:() => {
                  console.log('發(fā)送成功!');
              }
          })
          console.log('代碼執(zhí)行結(jié)束');

          上面是一段簡易的ajax請求代碼:

          • ajax進(jìn)入Event Table,注冊回調(diào)函數(shù)success
          • 執(zhí)行console.log('代碼執(zhí)行結(jié)束')
          • ajax事件完成,回調(diào)函數(shù)success進(jìn)入Event Queue。
          • 主線程從Event Queue讀取回調(diào)函數(shù)success并執(zhí)行。

          相信通過上面的文字和代碼,你已經(jīng)對js的執(zhí)行順序有了初步了解。接下來我們來研究進(jìn)階話題:setTimeout。

          3.又愛又恨的setTimeout

          大名鼎鼎的setTimeout無需再多言,大家對他的第一印象就是異步可以延時(shí)執(zhí)行,我們經(jīng)常這么實(shí)現(xiàn)延時(shí)3秒執(zhí)行:

          setTimeout(() => {
              console.log('延時(shí)3秒');
          },3000)

          漸漸的setTimeout用的地方多了,問題也出現(xiàn)了,有時(shí)候明明寫的延時(shí)3秒,實(shí)際卻5,6秒才執(zhí)行函數(shù),這又咋回事啊?

          先看一個(gè)例子:

          setTimeout(() => {
              task();
          },3000)
          console.log('執(zhí)行console');

          根據(jù)前面我們的結(jié)論,setTimeout是異步的,應(yīng)該先執(zhí)行console.log這個(gè)同步任務(wù),所以我們的結(jié)論是:

          //執(zhí)行console
          //task()
          復(fù)制代碼

          去驗(yàn)證一下,結(jié)果正確! 然后我們修改一下前面的代碼:

          setTimeout(() => {
              task()
          },3000)
          
          sleep(10000000)

          乍一看其實(shí)差不多嘛,但我們把這段代碼在chrome執(zhí)行一下,卻發(fā)現(xiàn)控制臺(tái)執(zhí)行task()需要的時(shí)間遠(yuǎn)遠(yuǎn)超過3秒,說好的延時(shí)三秒,為啥現(xiàn)在需要這么長時(shí)間啊?

          這時(shí)候我們需要重新理解setTimeout的定義。我們先說上述代碼是怎么執(zhí)行的:

          • task()進(jìn)入Event Table并注冊,計(jì)時(shí)開始。
          • 執(zhí)行sleep函數(shù),很慢,非常慢,計(jì)時(shí)仍在繼續(xù)。
          • 3秒到了,計(jì)時(shí)事件timeout完成,task()進(jìn)入Event Queue,但是sleep也太慢了吧,還沒執(zhí)行完,只好等著。
          • sleep終于執(zhí)行完了,task()終于從Event Queue進(jìn)入了主線程執(zhí)行。

          上述的流程走完,我們知道setTimeout這個(gè)函數(shù),是經(jīng)過指定時(shí)間后,把要執(zhí)行的任務(wù)(本例中為task())加入到Event Queue中,又因?yàn)槭菃尉€程任務(wù)要一個(gè)一個(gè)執(zhí)行,如果前面的任務(wù)需要的時(shí)間太久,那么只能等著,導(dǎo)致真正的延遲時(shí)間遠(yuǎn)遠(yuǎn)大于3秒。

          我們還經(jīng)常遇到setTimeout(fn,0)這樣的代碼,0秒后執(zhí)行又是什么意思呢?是不是可以立即執(zhí)行呢?

          答案是不會(huì)的,setTimeout(fn,0)的含義是,指定某個(gè)任務(wù)在主線程最早可得的空閑時(shí)間執(zhí)行,意思就是不用再等多少秒了,只要主線程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成,棧為空就馬上執(zhí)行。舉例說明:

          //代碼1
          console.log('先執(zhí)行這里');
          setTimeout(() => {
              console.log('執(zhí)行啦')
          },0);
          復(fù)制代碼
          //代碼2
          console.log('先執(zhí)行這里');
          setTimeout(() => {
              console.log('執(zhí)行啦')
          },3000);  

          代碼1的輸出結(jié)果是:

          //先執(zhí)行這里
          //執(zhí)行啦

          代碼2的輸出結(jié)果是:

          //先執(zhí)行這里
          // ... 3s later
          // 執(zhí)行啦

          關(guān)于setTimeout要補(bǔ)充的是,即便主線程為空,0毫秒實(shí)際上也是達(dá)不到的。根據(jù)HTML的標(biāo)準(zhǔn),最低是4毫秒。有興趣的同學(xué)可以自行了解。

          4.又恨又愛的setInterval

          上面說完了setTimeout,當(dāng)然不能錯(cuò)過它的孿生兄弟setInterval。他倆差不多,只不過后者是循環(huán)的執(zhí)行。對于執(zhí)行順序來說,setInterval會(huì)每隔指定的時(shí)間將注冊的函數(shù)置入Event Queue,如果前面的任務(wù)耗時(shí)太久,那么同樣需要等待。

          唯一需要注意的一點(diǎn)是,對于setInterval(fn,ms)來說,我們已經(jīng)知道不是每過ms秒會(huì)執(zhí)行一次fn,而是每過ms秒,會(huì)有fn進(jìn)入Event Queue。一旦setInterval的回調(diào)函數(shù)fn執(zhí)行時(shí)間超過了延遲時(shí)間ms,那么就完全看不出來有時(shí)間間隔了。這句話請讀者仔細(xì)品味。

          5.Promise與process.nextTick(callback)

          傳統(tǒng)的定時(shí)器我們已經(jīng)研究過了,接著我們探究Promise與process.nextTick(callback)的表現(xiàn)。

          Promise的定義和功能本文不再贅述,不了解的讀者可以學(xué)習(xí)一下阮一峰老師的Promise。而process.nextTick(callback)類似node.js版的"setTimeout",在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)。

          我們進(jìn)入正題,除了廣義的同步任務(wù)和異步任務(wù),我們對任務(wù)有更精細(xì)的定義:

          • macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval
          • micro-task(微任務(wù)):Promise,process.nextTick

          不同類型的任務(wù)會(huì)進(jìn)入對應(yīng)的Event Queue,比如setTimeout和setInterval會(huì)進(jìn)入相同的Event Queue。

          事件循環(huán)的順序,決定js代碼的執(zhí)行順序。進(jìn)入整體代碼(宏任務(wù))后,開始第一次循環(huán)。接著執(zhí)行所有的微任務(wù)。然后再次從宏任務(wù)開始,找到其中一個(gè)任務(wù)隊(duì)列執(zhí)行完畢,再執(zhí)行所有的微任務(wù)。聽起來有點(diǎn)繞,我們用文章最開始的一段代碼說明:

          setTimeout(function() {
             console.log('setTimeout');
          })
          
          new Promise(function(resolve) {
              console.log('promise');
          }).then(function() {
              console.log('then');
          })
          
          console.log('console');
          • 這段代碼作為宏任務(wù),進(jìn)入主線程。
          • 先遇到setTimeout,那么將其回調(diào)函數(shù)注冊后分發(fā)到宏任務(wù)Event Queue。(注冊過程與上同,下文不再描述)
          • 接下來遇到了Promise,new Promise立即執(zhí)行,then函數(shù)分發(fā)到微任務(wù)Event Queue。
          • 遇到console.log(),立即執(zhí)行。
          • 好啦,整體代碼script作為第一個(gè)宏任務(wù)執(zhí)行結(jié)束,看看有哪些微任務(wù)?我們發(fā)現(xiàn)了then在微任務(wù)Event Queue里面,執(zhí)行。
          • ok,第一輪事件循環(huán)結(jié)束了,我們開始第二輪循環(huán),當(dāng)然要從宏任務(wù)Event Queue開始。我們發(fā)現(xiàn)了宏任務(wù)Event Queue中setTimeout對應(yīng)的回調(diào)函數(shù),立即執(zhí)行。
          • 結(jié)束。

          事件循環(huán),宏任務(wù),微任務(wù)的關(guān)系如圖所示:

          我們來分析一段較復(fù)雜的代碼,看看你是否真的掌握了js的執(zhí)行機(jī)制:

          console.log('1');
          
          setTimeout(function() {
              console.log('2');
              process.nextTick(function() {
                  console.log('3');
              })
              new Promise(function(resolve) {
                  console.log('4');
                  resolve();
              }).then(function() {
                  console.log('5')
              })
          })
          process.nextTick(function() {
              console.log('6');
          })
          new Promise(function(resolve) {
              console.log('7');
              resolve();
          }).then(function() {
              console.log('8')
          })
          
          setTimeout(function() {
              console.log('9');
              process.nextTick(function() {
                  console.log('10');
              })
              new Promise(function(resolve) {
                  console.log('11');
                  resolve();
              }).then(function() {
                  console.log('12')
              })
          })

          第一輪事件循環(huán)流程分析如下:

          • 整體script作為第一個(gè)宏任務(wù)進(jìn)入主線程,遇到console.log,輸出1。
          • 遇到setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中。我們暫且記為setTimeout1。
          • 遇到process.nextTick(),其回調(diào)函數(shù)被分發(fā)到微任務(wù)Event Queue中。我們記為process1。
          • 遇到Promise,new Promise直接執(zhí)行,輸出7。then被分發(fā)到微任務(wù)Event Queue中。我們記為then1。
          • 又遇到了setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中,我們記為setTimeout2。

          宏任務(wù)Event Queue

          微任務(wù)Event Queue

          setTimeout1

          process1

          setTimeout2

          then1

          • 上表是第一輪事件循環(huán)宏任務(wù)結(jié)束時(shí)各Event Queue的情況,此時(shí)已經(jīng)輸出了1和7。
          • 我們發(fā)現(xiàn)了process1和then1兩個(gè)微任務(wù)。
          • 執(zhí)行process1,輸出6。
          • 執(zhí)行then1,輸出8。

          好了,第一輪事件循環(huán)正式結(jié)束,這一輪的結(jié)果是輸出1,7,6,8。那么第二輪時(shí)間循環(huán)從setTimeout1宏任務(wù)開始:

          • 首先輸出2。接下來遇到了process.nextTick(),同樣將其分發(fā)到微任務(wù)Event Queue中,記為process2。new Promise立即執(zhí)行輸出4,then也分發(fā)到微任務(wù)Event Queue中,記為then2。

          宏任務(wù)Event Queue

          微任務(wù)Event Queue

          setTimeout2

          process2


          then2

          • 第二輪事件循環(huán)宏任務(wù)結(jié)束,我們發(fā)現(xiàn)有process2和then2兩個(gè)微任務(wù)可以執(zhí)行。
          • 輸出3。
          • 輸出5。
          • 第二輪事件循環(huán)結(jié)束,第二輪輸出2,4,3,5。
          • 第三輪事件循環(huán)開始,此時(shí)只剩setTimeout2了,執(zhí)行。
          • 直接輸出9。
          • 將process.nextTick()分發(fā)到微任務(wù)Event Queue中。記為process3。
          • 直接執(zhí)行new Promise,輸出11。
          • 將then分發(fā)到微任務(wù)Event Queue中,記為then3。

          宏任務(wù)Event Queue

          微任務(wù)Event Queue


          process3


          then3

          • 第三輪事件循環(huán)宏任務(wù)執(zhí)行結(jié)束,執(zhí)行兩個(gè)微任務(wù)process3和then3。
          • 輸出10。
          • 輸出12。
          • 第三輪事件循環(huán)結(jié)束,第三輪輸出9,11,10,12。

          整段代碼,共進(jìn)行了三次事件循環(huán),完整的輸出為1,7,6,8,2,4,3,5,9,11,10,12。 (請注意,node環(huán)境下的事件監(jiān)聽依賴libuv與前端環(huán)境不完全相同,輸出順序可能會(huì)有誤差)

          6.寫在最后

          (1)js的異步

          我們從最開頭就說javascript是一門單線程語言,不管是什么新框架新語法糖實(shí)現(xiàn)的所謂異步,其實(shí)都是用同步的方法去模擬的,牢牢把握住單線程這點(diǎn)非常重要。

          (2)事件循環(huán)Event Loop

          事件循環(huán)是js實(shí)現(xiàn)異步的一種方法,也是js的執(zhí)行機(jī)制。

          (3)javascript的執(zhí)行和運(yùn)行

          執(zhí)行和運(yùn)行有很大的區(qū)別,javascript在不同的環(huán)境下,比如node,瀏覽器,Ringo等等,執(zhí)行方式是不同的。而運(yùn)行大多指javascript解析引擎,是統(tǒng)一的。

          (4)setImmediate

          微任務(wù)和宏任務(wù)還有很多種類,比如setImmediate等等,執(zhí)行都是有共同點(diǎn)的,有興趣的同學(xué)可以自行了解。

          (5)最后的最后

          • javascript是一門單線程語言
          • Event Loop是javascript的執(zhí)行機(jī)制

          牢牢把握兩個(gè)基本點(diǎn),以認(rèn)真學(xué)習(xí)javascript為中心,早日實(shí)現(xiàn)成為前端高手的偉大夢想!

          、前提簡介

          1.1什么是JavaScript

          JavaScript是一種動(dòng)態(tài)的計(jì)算機(jī)編程語言。它是輕量級的,最常用作網(wǎng)頁的一部分,其實(shí)現(xiàn)允許客戶端腳本與用戶交互并創(chuàng)建動(dòng)態(tài)頁面。它是一種具有面向?qū)ο蠊δ艿慕忉屝途幊陶Z言。

          1.2JavaScript和Java語言的區(qū)別

          Javascript和Java沒有任何關(guān)系,它們是不同的兩種語言(java是一種程序設(shè)計(jì)語言,javascript 是客戶端的腳本語言),只是名字上都有一個(gè)Java而已。

          對了,在這里說一下,我目前是在職web前端開發(fā),如果你現(xiàn)在正在學(xué)習(xí)前端,了解前端,渴望成為一名合格的web前端開發(fā)工程師,在入門學(xué)習(xí)前端的過程當(dāng)中有遇見任何關(guān)于學(xué)習(xí)方法,學(xué)習(xí)路線,學(xué)習(xí)效率等方面的問題,都可以隨時(shí)關(guān)注并私信我:前端,我都會(huì)根據(jù)大家的問題給出針對性的建議,缺乏基礎(chǔ)入門的視頻教程也可以直接來找我,我這邊有最新的web前端基礎(chǔ)精講視頻教程, 還有我做web前端技術(shù)這段時(shí)間整理的一些學(xué)習(xí)手冊,面試題,開發(fā)工具,PDF文檔書籍教程,都可以直接分享給大家。


          1.3Html、Css和Javascript

          這三個(gè)要素共同構(gòu)成了Web開發(fā)的基礎(chǔ)。

          HTML:頁面的結(jié)構(gòu)-標(biāo)題,正文,要包含的任何圖像
          CSS:控制該頁面的外觀(這將用于自定義字體,背景顏色等)
          JavaScript:不可思議的第三個(gè)元素。創(chuàng)建結(jié)構(gòu)(HTML)和美學(xué)氛圍(CSS)后,JavaScript使您的網(wǎng)站或項(xiàng)目充滿活力。

          1.4Javascript作用

          1. 表單數(shù)據(jù)驗(yàn)證:表單數(shù)據(jù)驗(yàn)證是JavaScript最基本也是最能體現(xiàn)效率的功能。
          2. 動(dòng)態(tài)HTML(即DHTML):動(dòng)態(tài)HTML指不需要服務(wù)器介入而動(dòng)態(tài)變化的網(wǎng)頁效果,包括動(dòng)態(tài)內(nèi)容、動(dòng)態(tài)樣式、動(dòng)態(tài)布局等。 比如改變盒子的尺寸,背景顏色,圖片等。
          3. 用戶交互:用戶交互指根據(jù)用戶的不同操作進(jìn)行的響應(yīng)處理。例如:聯(lián)動(dòng)菜單等。
          4. 數(shù)據(jù)綁定:HTML中表單和表格能夠以.txt文件定義的數(shù)據(jù)源,通過對位于服務(wù)器端的數(shù)據(jù)源文件的訪問,便可以將數(shù)據(jù)源中的數(shù)據(jù)傳送到客戶端,并將這些數(shù)據(jù)保存在客戶端。
          5. 少量數(shù)據(jù)查找:能夠?qū)崿F(xiàn)在當(dāng)前網(wǎng)頁中進(jìn)行字符串的查找和替換。
          6. AJAX核心技術(shù):AJAX即異步JavaScript+XML。該對象提供一種支持異步請求的技術(shù),使客戶端可以使用JavaScript向服務(wù)器提出請求并處理響應(yīng),但并不影響用戶在客戶端的瀏覽。
          7. Nodejs就是使用的javascript做后端,是目前為止唯一的一個(gè)既能做前端、又能做后端的語言。

          (上面這個(gè)作用是直接用的我的老師的課件,我可沒這么6懂這么多。他一個(gè)10多年開發(fā)經(jīng)驗(yàn)的資深程序員哈哈哈哈哈哈,有點(diǎn)想幫忙宣傳一下他的網(wǎng)課,但想想還是算了吧,感覺打廣告有點(diǎn)不好)

          *********************************************一條華麗的分割線***************************************************

          二、實(shí)操代碼

          2.1Javascript寫在本html內(nèi)

          1. js程序必須寫在script標(biāo)簽中。
          2. script:可以寫在網(wǎng)頁中的任何位置。
          3. type=“text/javascript”:表示當(dāng)前的語言是javascript語言。這個(gè)屬性是可以省略的

          舉例:上代碼

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8" />
          		<title></title>
          		
          	</head>
          	<body>
          		<script type="text/javascript">
          			alert("出錯(cuò)啦")
          		</script>
          	</body>
          </html>

          拿代碼去運(yùn)行一下就知道了

          2.2Javascript可以寫在單獨(dú)的文件中(外聯(lián)方式)

          創(chuàng)建一個(gè)js文件,在js文件中編寫js代碼。(外部文件中編寫js代碼就直接寫代碼就可以了,不用再添加script標(biāo)簽)

          比如說在js目錄下面創(chuàng)建一個(gè) test.js文件 里面的代碼為alert(“出錯(cuò)啦!”)

          舉例上代碼

          a.html

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="UTF-8">
          		<title></title>
          	</head>
          	<body>
          		
          		<script src="js/test.js" type="text/javascript" charset="UTF-8">
          			
          		</script>
          		
          	</body>
          </html>

          拿代碼去運(yùn)行一下就知道了

          2.3實(shí)戰(zhàn):點(diǎn)擊一個(gè)盒子,讓另外一個(gè)盒子變色

          舉例上代碼:

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          		<style type="text/css">
          			#box1{
          				width: 100px;
          				height: 100px;
          				background-color: red;
          			}
          			
          			#box2{
          				width: 100px;
          				height: 100px;
          				background-color: blue;
          			}
          		</style>
          	</head>
          	<body>
          		<div id="box1">
          			
          		</div>
          		<div id="box2">
          			
          		</div>
          		
          		<script type="text/javascript">
          			//目標(biāo):點(diǎn)擊box1時(shí),讓box2變顏色
          			var b1 = document.getElementById("box1")
          			b1.onclick=function(){
          				// 當(dāng)點(diǎn)擊b1的時(shí)候,執(zhí)行此處的代碼
          				document.getElementById("box2").style.backgroundColor="pink"
          			}
          		</script>
          	</body>
          </html>

          運(yùn)行效果拿去試試就知道了,點(diǎn)一下第一個(gè)小盒子

          2.4實(shí)戰(zhàn):一個(gè)按鈕綁定一個(gè)事件

          1. 在js中,使用關(guān)鍵字function可以定義一個(gè)函數(shù),函數(shù)里面的代碼不會(huì)自動(dòng)執(zhí)行,只有函數(shù)被調(diào)用后,函數(shù)里面的代碼才會(huì)執(zhí)行。
          2. 可以給網(wǎng)頁中的任何html容器標(biāo)簽綁定點(diǎn)擊事件。οnclick=“add();” onclick表示點(diǎn)擊的時(shí)候執(zhí)行。
          3. js中有兩個(gè)函數(shù)parseInt 將字符串轉(zhuǎn)為數(shù)字。 parseFloat():將字符串專為浮點(diǎn)類型。

          舉例上代碼

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="UTF-8">
          		<title></title>
          	</head>
          	<body>
          	<input type="text" name="tb1" id="tb1" value="" />+<input type="text" name="tb2" id="tb2" value="" /> =<input type="text" name="tb3" id="tb3" value="" />
          	<input type="button" id="btnjisuan" value="計(jì)算" onclick="add();" />
          	
          	<a href="javascript:void(0);"  onclick="bb();">騰訊</a>
          	
          	
          	<script type="text/javascript">
          		
          		function add()
          		{
          			
          			var v1=document.getElementById("tb1").value;
          			var v2=document.getElementById("tb2").value;
          			var v3=parseInt(v1) + parseInt(v2);
          			document.getElementById("tb3").value=v3;
          		}
          		
          		function bb()
          		{
          			location.href="http://www.qq.com"; //通過js代碼實(shí)現(xiàn)頁面的跳轉(zhuǎn) 
          			
          		}
          	</script>
          	</body>
          </html>

          拿去運(yùn)行一個(gè)就知道了哈哈哈哈,這個(gè)學(xué)會(huì)了,下面那個(gè)就容易多啦!
          *********************************************一條華麗的哈哈哈哈哈哈哈哈***************************************************

          2.4實(shí)戰(zhàn):變換皮膚

          實(shí)現(xiàn)效果:點(diǎn)擊什么顏色代表的小框框,就會(huì)彈出穿啥衣服的 fairy


          (哈哈哈哈 本人敲愛看這些美麗的事物哈哈哈哈)

          自己可以下載一些圖片或者顏色漸變圖片用來做背景,放在img里面,可自己命名。基本格式如下圖:


          上代碼:

          網(wǎng)頁換膚.html

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="UTF-8">
          		<title></title>
          		<link rel="stylesheet" type="text/css" href="css/css2.css" id="btnlink"/>
          	</head>
          	<body>
          		
          		<div id="box1">
          			<span id="s1" onclick="a1();">志玲</span><span id="s2" onclick="a2();">依林</span><span id="s3" onclick="a3();">昆凌</span>
          			
          			
          		</div>
          		
          		<script type="text/javascript">
          			function a1()
          			{
          				document.getElementById("btnlink").href="css/css1.css";
          			}
          			
          			function a2()
          			{
          				document.getElementById("btnlink").href="css/css2.css";
          			}
          			
          			function a3()
          			{
          				document.getElementById("btnlink").href="css/css3.css";
          			}
          			
          			
          			
          		</script>
          		
          	</body>
          </html>

          css1.css

          *{
          	margin: 0;
          	padding: 0;
          }
          
          
          html,body{
          	width:100%;
          	height: 100%;
          }
          
          body{
          	background-image: url(../img/blue.jpg);
          	background-repeat: repeat-x;  /* 設(shè)置不重復(fù)平鋪 */
          }
          
          #box1{
          	width: 186px;
          	height: 60px;
          	background-color: white;
          	margin: 0 auto;
          	position: relative;
          }
          #s1{
          	width: 60px;
          	height: 60px;
          	background-color: blue;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;  /* 子絕父相 */
          	left: 0;
          	top: 0;
          }
          #s2{
          	width: 60px;
          	height: 60px;
          	background-color:green;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	left: 62px;
          	top: 0;
          }
          #s3{
          	width: 60px;
          	height: 60px;
          	background-color: pink;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	right: 0;
          	top: 0;
          }

          css2.css

          *{
          	margin: 0;
          	padding: 0;
          }
          
          
          html,body{
          	width:100%;
          	height: 100%;
          }
          
          body{
          	background-image: url(../img/green.jpg)
          }
          
          #box1{
          	width: 186px;
          	height: 60px;
          	background-color: white;
          	margin: 0 auto;
          	position: relative;
          }
          #s1{
          	width: 60px;
          	height: 60px;
          	background-color: blue;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;  /* 子絕父相 */
          	left: 0;
          	top: 0;
          }
          #s2{
          	width: 60px;
          	height: 60px;
          	background-color:green;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	left: 62px;
          	top: 0;
          }
          #s3{
          	width: 60px;
          	height: 60px;
          	background-color: pink;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	right: 0;
          	top: 0;
          }

          css3.css

          *{
          	margin: 0;
          	padding: 0;
          }
          
          
          html,body{
          	width:100%;
          	height: 100%;
          }
          
          body{
          	background-image: url(../img/pink.jpg)
          }
          
          #box1{
          	width: 186px;
          	height: 60px;
          	background-color: white;
          	margin: 0 auto;
          	position: relative;
          }
          #s1{
          	width: 60px;
          	height: 60px;
          	background-color: blue;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;  /* 子絕父相 */
          	left: 0;
          	top: 0;
          }
          #s2{
          	width: 60px;
          	height: 60px;
          	background-color:green;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	left: 62px;
          	top: 0;
          }
          #s3{
          	width: 60px;
          	height: 60px;
          	background-color: pink;
          	display: inline-block;
          	margin: 1px;
          	cursor: pointer;
          	position: absolute;
          	right: 0;
          	top: 0;
          }

          一些很基礎(chǔ)的東西,要是寫起來那就太多了,很多不常用的,到了我們需要它的時(shí)候谷歌和百度就行了。
          由于時(shí)間關(guān)系,暫時(shí)更到這里。

          原文鏈接:https://link.zhihu.com/?target=https%3A//blog.csdn.net/hanhanwanghaha/article/details/109188646

          作者:我一個(gè)超級無敵可愛的人鴨

          出處:CSDN

          介:本文是一個(gè) V8 編譯原理知識(shí)的介紹文章,旨在讓大家感性的了解 JavaScript 在 V8 中的解析過程。

          作者 | 子弈
          來源 | 阿里技術(shù)公眾號

          一 簡介

          本文是一個(gè) V8 編譯原理知識(shí)的介紹文章,旨在讓大家感性的了解 JavaScript 在 V8 中的解析過程。本文主要的撰寫流程如下:

          • 解釋器和編譯器:計(jì)算機(jī)編譯原理的基礎(chǔ)知識(shí)介紹
          • V8 的編譯原理:基于計(jì)算機(jī)編譯原理的知識(shí),了解 V8 對于 JavaScript 的解析流程
          • V8 的運(yùn)行時(shí)表現(xiàn):結(jié)合 V8 的編譯原理,實(shí)踐 V8 在解析流程中的具體運(yùn)行表現(xiàn)

          本文僅代表個(gè)人觀點(diǎn),文中若有錯(cuò)誤歡迎指正。

          二 解釋器和編譯器

          大家可能一直疑惑的問題:JavaScript 是一門解釋型語言嗎?要了解這個(gè)問題,首先需要初步了解什么是解釋器和編譯器以及它們的特點(diǎn)是什么。

          1 解釋器

          解釋器的作用是將某種語言編寫的源程序作為輸入,將該源程序執(zhí)行的結(jié)果作為輸出,例如 Perl、Scheme、APL 等都是使用解釋器進(jìn)行轉(zhuǎn)換執(zhí)行:

          2 編譯器

          編譯器的設(shè)計(jì)是一個(gè)非常龐大和復(fù)雜的軟件系統(tǒng)設(shè)計(jì),在真正設(shè)計(jì)的時(shí)候需要解決兩個(gè)相對重要的問題:

          • 如何分析不同高級程序語言設(shè)計(jì)的源程序
          • 如何將源程序的功能等價(jià)映射到不同指令系統(tǒng)的目標(biāo)機(jī)器

          中間表示(IR)

          中間表示(Intermediate Representation,IR)是程序結(jié)構(gòu)的一種表現(xiàn)方式,它會(huì)比抽象語法樹(Abstract Syntax Tree,AST)更加接近匯編語言或者指令集,同時(shí)也會(huì)保留源程序中的一些高級信息,具體作用包括:

          • 易于編譯器的錯(cuò)誤調(diào)試,容易識(shí)別是 IR 之前的前端還是之后的后端出的問題
          • 可以使得編譯器的職責(zé)更加分離,源程序的編譯更多關(guān)注如何轉(zhuǎn)換成 IR,而不是去適配不同的指令集
          • IR 更加接近指令集,從而相對于源碼可以更加節(jié)省內(nèi)存空間

          優(yōu)化編譯器

          IR 本身可以做到多趟迭代從而優(yōu)化源程序,在每一趟迭代的過程中可以研究代碼并記錄優(yōu)化的細(xì)節(jié),方便后續(xù)的迭代查找并利用這些優(yōu)化信息,最終可以高效輸出更優(yōu)的目標(biāo)程序:

          優(yōu)化器可以對 IR 進(jìn)行一趟或者多趟處理,從而生成更快執(zhí)行速度或者更小體積的目標(biāo)程序(例如找到循環(huán)中不變的計(jì)算并對其進(jìn)行優(yōu)化從而減少運(yùn)算次數(shù)),也可能用于產(chǎn)生更少異常或者更低功耗的目標(biāo)程序。除此之外,前端和后端內(nèi)部還可以細(xì)分為多個(gè)處理步驟,具體如下圖所示:

          3 兩者的特性比較

          解釋器和編譯器的具體特性比較如下所示:

          需要注意早期的 Web 前端要求頁面的啟動(dòng)速度快,因此采用解釋執(zhí)行的方式,但是頁面在運(yùn)行的過程中性能相對較低。為了解決這個(gè)問題,需要在運(yùn)行時(shí)對 JavaScript 代碼進(jìn)行優(yōu)化,因此在 JavaScript 的解析引擎中引入了 JIT 技術(shù)。

          4 JIT 編譯技術(shù)

          JIT (Just In Time)編譯器是一種動(dòng)態(tài)編譯技術(shù),相對于傳統(tǒng)編譯器而言,最大的區(qū)別在于編譯時(shí)和運(yùn)行時(shí)不分離,是一種在運(yùn)行的過程中對代碼進(jìn)行動(dòng)態(tài)編譯的技術(shù)。

          5 混合動(dòng)態(tài)編譯技術(shù)

          為了解決 JavaScript 在運(yùn)行時(shí)性能較慢的問題,可以通過引入 JIT 技術(shù),并采用混合動(dòng)態(tài)編譯的方式來提升 JavaScript 的運(yùn)行性能,具體思路如下所示:

          采用上述編譯框架后,可以使得 JavaScript 語言:

          • 啟動(dòng)速度快:在 JavaScript 啟動(dòng)的時(shí)候采用解釋執(zhí)行的方式運(yùn)行,利用了解釋器啟動(dòng)速度快的特性
          • 運(yùn)行性能高:在 JavaScript 運(yùn)行的過程中可以對代碼進(jìn)行監(jiān)控,從而使用 JIT 技術(shù)對代碼進(jìn)行編譯優(yōu)化

          三 V8 的編譯原理

          V8 是一個(gè)開源的 JavaScript 虛擬機(jī),目前主要用在 Chrome 瀏覽器(包括開源的 Chromium)以及 Node.js 中,核心功能是用于解析和執(zhí)行 JavaScript 語言。為了解決早期 JavaScript 運(yùn)行性能差的問題,V8 經(jīng)歷了多個(gè)歷史的編譯框架衍變之后(感興趣的同學(xué)可以了解一下早期的 V8 編譯框架設(shè)計(jì)),引入混合動(dòng)態(tài)編譯的技術(shù)來解決問題,具體詳細(xì)的編譯框架如下所示:

          1 Ignition 解釋器

          Ignition 的主要作用是將 AST 轉(zhuǎn)換成 Bytecode(字節(jié)碼,中間表示)。在運(yùn)行的過程中,還會(huì)使用類型反饋(TypeFeedback)技術(shù)并計(jì)算熱點(diǎn)代碼(HotSpot,重復(fù)被運(yùn)行的代碼,可以是方法也可以是循環(huán)體),最終交給 TurboFan 進(jìn)行動(dòng)態(tài)運(yùn)行時(shí)的編譯優(yōu)化。Ignition 的解釋執(zhí)行流程如下所示:

          在字節(jié)碼解釋執(zhí)行的過程中,會(huì)將需要進(jìn)行性能優(yōu)化的運(yùn)行時(shí)信息指向?qū)?yīng)的 Feedback Vector(反饋向量,之前也被稱為 Type Feedback Vector),F(xiàn)eeback Vector 中會(huì)包含根據(jù)內(nèi)聯(lián)緩存(Inline Cache,IC)來存儲(chǔ)的多種類型的插槽(Feedback Vector Slot)信息,例如 BinaryOp 插槽(二進(jìn)制操作結(jié)果的數(shù)據(jù)類型)、Invocation Count(函數(shù)的調(diào)用次數(shù))以及 Optimized Code 信息等。

          這里不會(huì)過多講解每個(gè)執(zhí)行流程的細(xì)節(jié)問題。

          2 TurboFan 優(yōu)化編譯器

          TurboFan 利用了 JIT 編譯技術(shù),主要作用是對 JavaScript 代碼進(jìn)行運(yùn)行時(shí)編譯優(yōu)化,具體的流程如下所示:

          圖片出處 An Introduction to Speculative Optimization in V8。

          需要注意 Profiling Feedback 部分,這里主要提供 Ignition 解釋執(zhí)行過程中生成的運(yùn)行時(shí)反饋向量信息 Feedback Vector ,Turbofan 會(huì)結(jié)合字節(jié)碼以及反饋向量信息生成圖示(數(shù)據(jù)結(jié)構(gòu)中的圖結(jié)構(gòu)),并將圖傳遞給前端部分,之后會(huì)根據(jù)反饋向量信息對代碼進(jìn)行優(yōu)化和去優(yōu)化。

          這里的去優(yōu)化是指讓代碼回退到 Ignition 進(jìn)行解釋執(zhí)行,去優(yōu)化本質(zhì)是因?yàn)闄C(jī)器碼已經(jīng)不能滿足運(yùn)行訴求,例如一個(gè)變量從 string 類型轉(zhuǎn)變成 number 類型,機(jī)器碼編譯的是 string 類型,此時(shí)已經(jīng)無法再滿足運(yùn)行訴求,因此 V8 會(huì)執(zhí)行去優(yōu)化動(dòng)作,將代碼回退到 Ignition 進(jìn)行解釋執(zhí)行。

          四 V8 的運(yùn)行時(shí)表現(xiàn)

          在了解 V8 的編譯原理之后,接下來需要使用 V8 的調(diào)試工具來具體查看 JavaScript 的編譯和運(yùn)行信息,從而加深我們對 V8 的編譯過程認(rèn)知。

          1 D8 調(diào)試工具

          如果想了解 JavaScript 在 V8 中的編譯時(shí)和運(yùn)行時(shí)信息,可以使用調(diào)試工具 D8。D8 是 V8 引擎的命令行 Shell,可以查看 AST 生成、中間代碼 ByteCode、優(yōu)化代碼、反優(yōu)化代碼、優(yōu)化編譯器的統(tǒng)計(jì)數(shù)據(jù)、代碼的 GC 等信息。D8 的安裝方式有很多,如下所示:

          • 方法一:根據(jù) V8 官方文檔 Using d8 以及 Building V8 with GN 進(jìn)行工具鏈的下載和編譯
          • 方法二:使用別人已經(jīng)編譯好的 D8 工具,可能版本會(huì)有滯后性,例如 Mac 版
          • 方法三:使用 JavaScript 引擎版本管理工具,例如 jsvu,可以下載到最新編譯好的 JavaScript 引擎

          本文使用方法三安裝 v8-debug 工具,安裝完成后執(zhí)行 v8-debug --help 可以查看有哪些命令:

          # 執(zhí)行 help 命令查看支持的參數(shù)
          v8-debug --help
          
          Synopsis:
            shell [options] [--shell] [<file>...]
            d8 [options] [-e <string>] [--shell] [[--module|--web-snapshot] <file>...]
          
            -e        execute a string in V8
            --shell   run an interactive JavaScript shell
            --module  execute a file as a JavaScript module
            --web-snapshot  execute a file as a web snapshot
          
          SSE3=1 SSSE3=1 SSE4_1=1 SSE4_2=1 SAHF=1 AVX=1 AVX2=1 FMA3=1 BMI1=1 BMI2=1 LZCNT=1 POPCNT=1 ATOM=0
          The following syntax for options is accepted (both '-' and '--' are ok):
            --flag        (bool flags only)
            --no-flag     (bool flags only)
            --flag=value  (non-bool flags only, no spaces around '=')
            --flag value  (non-bool flags only)
            --            (captures all remaining args in JavaScript)
          
          Options:
              # 打印生成的字節(jié)碼
            --print-bytecode (print bytecode generated by ignition interpreter)
                  type: bool  default: --noprint-bytecode
          
              
              # 跟蹤被優(yōu)化的信息
               --trace-opt (trace optimized compilation)
                  type: bool  default: --notrace-opt
            --trace-opt-verbose (extra verbose optimized compilation tracing)
                  type: bool  default: --notrace-opt-verbose
            --trace-opt-stats (trace optimized compilation statistics)
                  type: bool  default: --notrace-opt-stats
          
              # 跟蹤去優(yōu)化的信息
            --trace-deopt (trace deoptimization)
                  type: bool  default: --notrace-deopt
            --log-deopt (log deoptimization)
                  type: bool  default: --nolog-deopt
            --trace-deopt-verbose (extra verbose deoptimization tracing)
                  type: bool  default: --notrace-deopt-verbose
            --print-deopt-stress (print number of possible deopt points)
          
              
              # 查看編譯生成的 AST
            --print-ast (print source AST)
                  type: bool  default: --noprint-ast
          
              # 查看編譯生成的代碼
            --print-code (print generated code)
                  type: bool  default: --noprint-code
          
              # 查看優(yōu)化后的代碼
            --print-opt-code (print optimized code)
                  type: bool  default: --noprint-opt-code
          
              # 允許在源代碼中使用 V8 提供的原生 API 語法
            --allow-natives-syntax (allow natives syntax)
                  type: bool  default: --noallow-natives-syntax

          2 生成 AST

          我們編寫一個(gè) index.js 文件,在文件中寫入 JavaScript 代碼,執(zhí)行一個(gè)簡單的 add 函數(shù):

          function add(x, y) {
              return x + y
          }
          
          console.log(add(1, 2));

          使用 --print-ast 參數(shù)可以打印 add 函數(shù)的 AST 信息:

          v8-debug --print-ast ./index.js
          
          [generating bytecode for function: ]
          --- AST ---
          FUNC at 0
          . KIND 0
          . LITERAL ID 0
          . SUSPEND COUNT 0
          . NAME ""
          . INFERRED NAME ""
          . DECLS
          . . FUNCTION "add" = function add
          . EXPRESSION STATEMENT at 41
          . . ASSIGN at -1
          . . . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"
          . . . CALL
          . . . . PROPERTY at 49
          . . . . . VAR PROXY unallocated (0x7fb8c080e6f0) (mode = DYNAMIC_GLOBAL, assigned = false) "console"
          . . . . . NAME log
          . . . . CALL
          . . . . . VAR PROXY unallocated (0x7fb8c080e470) (mode = VAR, assigned = true) "add"
          . . . . . LITERAL 1
          . . . . . LITERAL 2
          . RETURN at -1
          . . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"
          
          [generating bytecode for function: add]
          --- AST ---
          FUNC at 12
          . KIND 0
          . LITERAL ID 1
          . SUSPEND COUNT 0
          . NAME "add"
          . PARAMS
          . . VAR (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
          . . VAR (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
          . DECLS
          . . VARIABLE (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
          . . VARIABLE (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
          . RETURN at 25
          . . ADD at 34
          . . . VAR PROXY parameter[0] (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
          . . . VAR PROXY parameter[1] (0x7fb8c080e580) (mode = VAR, assigned = false) "y"

          我們以圖形化的方式來描述生成的 AST 樹:

          VAR PROXY 節(jié)點(diǎn)在真正的分析階段會(huì)連接到對應(yīng)地址的 VAR 節(jié)點(diǎn)。

          3 生成字節(jié)碼

          AST 會(huì)經(jīng)過 Ignition 解釋器的 BytecodeGenerator 函數(shù)生成字節(jié)碼(中間表示),我們可以通過 --print-bytecode 參數(shù)來打印字節(jié)碼信息:

          v8-debug --print-bytecode ./index.js
          
          [generated bytecode for function:  (0x3ab2082933f5 <SharedFunctionInfo>)]
          Bytecode length: 43
          Parameter count 1
          Register count 6
          Frame size 48
          OSR nesting level: 0
          Bytecode Age: 0
                   0x3ab2082934be @    0 : 13 00             LdaConstant [0]
                   0x3ab2082934c0 @    2 : c3                Star1 
                   0x3ab2082934c1 @    3 : 19 fe f8          Mov <closure>, r2
                   0x3ab2082934c4 @    6 : 65 52 01 f9 02    CallRuntime [DeclareGlobals], r1-r2
                   0x3ab2082934c9 @   11 : 21 01 00          LdaGlobal [1], [0]
                   0x3ab2082934cc @   14 : c2                Star2 
                   0x3ab2082934cd @   15 : 2d f8 02 02       LdaNamedProperty r2, [2], [2]
                   0x3ab2082934d1 @   19 : c3                Star1 
                   0x3ab2082934d2 @   20 : 21 03 04          LdaGlobal [3], [4]
                   0x3ab2082934d5 @   23 : c1                Star3 
                   0x3ab2082934d6 @   24 : 0d 01             LdaSmi [1]
                   0x3ab2082934d8 @   26 : c0                Star4 
                   0x3ab2082934d9 @   27 : 0d 02             LdaSmi [2]
                   0x3ab2082934db @   29 : bf                Star5 
                   0x3ab2082934dc @   30 : 63 f7 f6 f5 06    CallUndefinedReceiver2 r3, r4, r5, [6]
                   0x3ab2082934e1 @   35 : c1                Star3 
                   0x3ab2082934e2 @   36 : 5e f9 f8 f7 08    CallProperty1 r1, r2, r3, [8]
                   0x3ab2082934e7 @   41 : c4                Star0 
                   0x3ab2082934e8 @   42 : a9                Return 
          Constant pool (size = 4)
          0x3ab208293485: [FixedArray] in OldSpace
           - map: 0x3ab208002205 <Map>
           - length: 4
                     0: 0x3ab20829343d <FixedArray[2]>
                     1: 0x3ab208202741 <String[7]: #console>
                     2: 0x3ab20820278d <String[3]: #log>
                     3: 0x3ab208003f09 <String[3]: #add>
          Handler Table (size = 0)
          Source Position Table (size = 0)
          [generated bytecode for function: add (0x3ab20829344d <SharedFunctionInfo add>)]
          Bytecode length: 6
          // 接受 3 個(gè)參數(shù), 1 個(gè)隱式的 this,以及顯式的 x 和 y
          Parameter count 3
          Register count 0
          // 不需要局部變量,因此幀大小為 0 
          Frame size 0
          OSR nesting level: 0
          Bytecode Age: 0
                   0x3ab2082935f6 @    0 : 0b 04             Ldar a1
                   0x3ab2082935f8 @    2 : 39 03 00          Add a0, [0]
                   0x3ab2082935fb @    5 : a9                Return 
          Constant pool (size = 0)
          Handler Table (size = 0)
          Source Position Table (size = 0)

          add 函數(shù)主要包含以下 3 個(gè)字節(jié)碼序列:

          // Load Accumulator Register
          // 加載寄存器 a1 的值到累加器中
          Ldar a1
          // 讀取寄存器 a0 的值并累加到累加器中,相加之后的結(jié)果會(huì)繼續(xù)放在累加器中
          // [0] 指向 Feedback Vector Slot,Ignition 會(huì)收集值的分析信息,為后續(xù)的 TurboFan 優(yōu)化做準(zhǔn)備
          Add a0, [0]
          // 轉(zhuǎn)交控制權(quán)給調(diào)用者,并返回累加器中的值
          Return 

          這里 Ignition 的解釋執(zhí)行這些字節(jié)碼采用的是一地址指令結(jié)構(gòu)的寄存器架構(gòu)。

          關(guān)于更多字節(jié)碼的信息可查看 Understanding V8’s Bytecode。

          4 優(yōu)化和去優(yōu)化

          JavaScript 是弱類型語言,不會(huì)像強(qiáng)類型語言那樣需要限定函數(shù)調(diào)用的形參數(shù)據(jù)類型,而是可以非常靈活的傳入各種類型的參數(shù)進(jìn)行處理,如下所示:

          function add(x, y) { 
              // + 操作符是 JavaScript 中非常復(fù)雜的一個(gè)操作
              return x + y
          }
          
          add(1, 2);
          add('1', 2);
          add(, 2);
          add(undefined, 2);
          add([], 2);
          add({}, 2);
          add([], {});

          為了可以進(jìn)行 + 操作符運(yùn)算,在底層執(zhí)行的時(shí)候往往需要調(diào)用很多 API,比如 ToPrimitive(判斷是否是對象)、ToString、ToNumber 等,將傳入的參數(shù)進(jìn)行符合 + 操作符的數(shù)據(jù)轉(zhuǎn)換處理。

          在這里 V8 會(huì)對 JavaScript 像強(qiáng)類型語言那樣對形參 x 和 y 進(jìn)行推測,這樣就可以在運(yùn)行的過程中排除一些副作用分支代碼,同時(shí)這里也會(huì)預(yù)測代碼不會(huì)拋出異常,因此可以對代碼進(jìn)行優(yōu)化,從而達(dá)到最高的運(yùn)行性能。在 Ignition 中通過字節(jié)碼來收集反饋信息(Feedback Vector),如下所示:

          為了查看 add 函數(shù)的運(yùn)行時(shí)反饋信息,我們可以通過 V8 提供的 Native API 來打印 add 函數(shù)的運(yùn)行時(shí)信息,具體如下所示:

          function add(x, y) {
              return x + y
          }
          
          // 注意這里默認(rèn)采用了 ClosureFeedbackCellArray,為了查看效果,強(qiáng)制開啟 FeedbackVector
          // 更多信息查看: A lighter V8:https://v8.dev/blog/v8-lite
          %EnsureFeedbackVectorForFunction(add);
          add(1, 2);
          // 打印 add 詳細(xì)的運(yùn)行時(shí)信息
          %DebugPrint(add);

          通過 --allow-natives-syntax 參數(shù)可以在 JavaScript 中調(diào)用 %DebugPrint 底層 Native API(更多 API 可以查看 V8 的 runtime.h 頭文件):


          點(diǎn)擊鏈接查看原文V8 編譯淺談,關(guān)注公眾號【阿里技術(shù)】獲取更多福利!

          版權(quán)聲明:本文內(nèi)容由阿里云實(shí)名注冊用戶自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,阿里云開發(fā)者社區(qū)不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。具體規(guī)則請查看《阿里云開發(fā)者社區(qū)用戶服務(wù)協(xié)議》和《阿里云開發(fā)者社區(qū)知識(shí)產(chǎn)權(quán)保護(hù)指引》。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,填寫侵權(quán)投訴表單進(jìn)行舉報(bào),一經(jīng)查實(shí),本社區(qū)將立刻刪除涉嫌侵權(quán)內(nèi)容。


          主站蜘蛛池模板: 一区二区三区国模大胆| 精品无码日韩一区二区三区不卡 | 一区二区三区免费视频网站 | 亚洲一区电影在线观看| 国产成人一区二区精品非洲| 国产精品视频一区二区三区四| 日韩一区二区在线播放| 亚洲一区二区电影| 国产午夜精品一区二区三区漫画| 国产一国产一区秋霞在线观看| 蜜桃无码一区二区三区| 无码人妻精品一区二区三区东京热| 肥臀熟女一区二区三区| 亚洲一区二区三区在线播放| 国产一区二区三区在线免费观看| 欧美日本精品一区二区三区 | 国产99精品一区二区三区免费| 国产萌白酱在线一区二区| 亚洲综合在线一区二区三区| 国产精品制服丝袜一区| 亚洲一区中文字幕在线电影网| 无码少妇一区二区性色AV | 日本在线视频一区| 国产一区二区视频免费| 国产一区二区三区露脸| 日本免费一区二区三区最新| 国产精品成人一区二区三区| 欧美日韩一区二区成人午夜电影| 国内精品视频一区二区三区| 韩国福利视频一区二区| 国产产一区二区三区久久毛片国语| 国模极品一区二区三区| 日韩视频在线观看一区二区| 视频一区二区在线观看| 三上悠亚精品一区二区久久| 无码av免费一区二区三区试看| 78成人精品电影在线播放日韩精品电影一区亚洲 | 日韩精品一区二区三区中文| 亚洲av无码片区一区二区三区 | 精品国产毛片一区二区无码| 日韩精品无码一区二区三区AV|