javascript代碼爲什麼運行這麼慢?

Js之所以比起c++慢,是因爲js是一個解析型無類型的語言,而c++等是編譯型的靜態類型的語言。編譯型的語言是在編譯的時候就確定了每一個變量的位置、類型、偏移量。但是js語言是一邊執行一邊確定變量的位置和類型的,大家也都知道,程序的執行本質上就是對一些數據的操作,這會帶來嚴重的性能損失。

下面像是所以下這兩種語言在處理代碼的時候的過程,從中可以發現不同之處。
c++代碼執行

class class1 {
    int x;
    int y;
}
int add(class1 a, class1 b){
    return a.x*a.y+b.x*b.y;
}

編譯階段:
遇到對class1的聲明,根據代碼會保存class1的結構類型,類型如下:

|x |基地址+0 |
|y |基地址+4 |

因爲明顯的聲明x,y爲int類型,所以偏移量也就確定下來了,這也意味着在後面執行過程中不能對變量的類型進行修改是靜態的。

然後遇到函數add後,知道參數a b爲class1類型,所以在編譯函數體內的代碼的時候,遇到a.x 知道class1這個類型在之前聲明過,所以可以根據聲明確定大致的位置,所以根據聲明信息就會用a的基地址+0來代替,a.y用a的基地址+4來代替,後面同理,這裏的a.x和a.y只是變量內存中某個位置的標識,再編譯的時候將使用詳細的地址來代替。

執行階段:
由於變量的地址在編譯的時候已經都確定了,所以在執行的時候只要利用數組和位運算就可以很好的對變量存取或其他操作,這個過程僅僅需要幾行很快的代碼,大多硬件已經集成實現了。

Js執行過程:

function add(a,b) {
    return a.x*a.y+b.x*b.y;
}

在執行之前的一個階段:
解析器會首先將代碼中可能擁戴的變量都提出來,知道有這麼一個東西存在,但是至於這個變量類型等詳細信息並不知道。比如上述代碼,僅僅知道在add函數作用域內有a b兩個變量存在,他是什麼類型都不知道。

執行階段:
當調用add函數的時候,會根據傳入的參數確定ab的類型等信息,所以說可以在執行的過程中對ab的類型進行改變。
還有與編譯型語言不同的是,由於在執行的時候纔會知道ab的信息,所以會對每一個變量創建結構
a
|x |3.3 |
|y |4.4 |
b
|x |3.3 |
|y |5.5 |

注意是會對每一個對象變量創建如上形式的結構,因爲在js中同一個類型對象中同一個屬性可能保存着不同類型的值,所以也可以得出,在執行階段可以對每一個對象的任意一個屬性進行刪除添加操作不會影響到其他的對象。
開始執行函數代碼,當執行帶a.x的時候會通過a的地址使用x去匹配a結構中屬性名一欄看看是否有與之匹配的項,這個並不是通過位移幾行代碼完事的,這種查找很消耗時間。

所以總結如下:
在編譯的時候c++中因爲變量類型都是以知的,所以,可以保存對象的結構,確定好沒一個屬性的偏移量。再編譯代碼的時候用基地址+偏移地址的方式表示。由於js是弱類型語言,所以再所謂的編譯階段僅僅是知道有哪些變量罷了,剩下的任務交給了執行階段。
在執行階段c++通過基地址+偏移地址的方式通過幾行位移指令就可以存取操作變量。但是js中,每執行到一個變量聲明語句的時候,纔會將這個變量的相關信息通過屬性名和值的形式保存起來,在對變量進行操作的時候,需要通過屬性名的索引找到這個變量才能進行值得操作,這個可能很消耗時間。
不過,js也有c++沒有的好處,正式因爲js實在執行的時候每一個對象都有自己的描述的結構信息,所以可以對對象的屬性進行增刪改,c++卻不幸,即使可以修改,也是牽一髮而動全身。

Js性能低除了本身屬於解析型動態類型語言之外,還有一個就是DOM操作。

Js引擎提供調用接口以便於渲染引擎能夠通過這個接口處理《script》標籤中的js代碼,但是js引擎也可以通過橋接接口訪問操作渲染引擎中的DOM結構,這種通過橋接器訪問操作DOM是很低效的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章