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是很低效的。