在静态语言中,虚表(方法表)是一个紧凑的数组结构,所有的方法条目(即每个方法的代码在内存中的存放地址)都是一个挨一个紧凑排列的。编译期在编译方法 调用语句的时候,实际上是该方法把虚表中的相对位置编入了目标指令中。当程序运行到该指令的时候,就会根据这个相对位置去查虚表中的对应条目,从而查到该 方法代码真实的存放地址,然后调用那段代码,从而实现方法的调用。静态类型语言之所以关注“类型”,是为了获得某个方法在那个类型的虚表中的相对位置。 即,静态语言真正关心的是“内存位置”。
在动态语言中,我们可以把方法表理解为一个Hash Table,每一个方法名都是Hash Table中的键值,其对应的内容是方法实现代码的真正存放地址。动态语言只关心对象的行为,而不关心对象的类型。Hash Table这种结构,能够使得解释器根据方法名迅速定位到对应的方法实现代码。
我们可以用两个简单的等式,来描述静态类型语言和动态类型语言的方法表的数据结构。
静态类型语言:方法表 = 数组
动态类型语言:方法表 = Hash Table
当然,Javascript是一元语言,不存在类型和实例的区别。而在C++、Java、C#、Python、Ruby等二元语言中,类型和实例是两个明确分开的概念。
如果要讲类型的话,每一个Javascript对象都是一个潜在的扩展类型。只要属性替换和填充发生了,一个新的扩展类型就产生了。
每个Javascript对象都有一个最基本的原型(prototype)对象,每次新生成一个Javascript对象,实际上就是把那个原型对象复制了一遍。这就像把一个Hash Table完全复制一遍一样。
如果你需要改变所有新对象的某个属性,你只要修改原型(prototype)对象的对应属性就可以了。之后,从那个原型复制出来的所有新对象的对应属性都会跟着改变。
Javascript访问原型对象相当容易。myObject.prototype就可以获得myObject的原型对象。myObject就是从myObject.prototype这个对象复制出来的。
Javascript并没有类型的概念,最初的原型也都是对象实例。这种语言叫做一元语言。与之相对的,有类型和实例概念的语言,比如,C、Java、C#、Python、Ruby等语言,叫做二元语言。
Javascript比Python、Ruby更具有动态性,主要就是因为它的“一元类型系统”的特性。
Python和Ruby是不关心类型,但毕竟还有类型的概念。但是,在Javascript这种一元语言中,干脆就取消类型的概念,所有的对象实例都自成一个类型,从而获得了更大的动态性。
Javascript语言把“Duck Type”的思想发挥到了极致——我们不关心它是不是鸭子,我们只关心它会不会像鸭子一样叫,我们只关心它会不会像鸭子一样摇摇摆摆走路。啊,不,事实 上,我们根本就不关心它是否像鸭子,因为在我们的系统中根本就没有鸭子这个东西。我们只关心它会不会叫,会不会摇摇摆摆走路。
读者可能会问了,既然一元类型系统比二元类型系统更加灵活,那么为什么大部分语言都采用二元类型系统的设计思路?为什么不能像Javascript一样,取消类型的概念呢?所有的原初“类型”都是“原型”对象,这样不是很好吗?
实际上,我也是这么想的。我更欣赏Javascript这种类型系统设计思路。在我看来,之所以大部分语言不采取这种设计思路,是出于两种原因。
第一个原因说起来有些无聊,那就是,Javascript语言的这种一元类型系统太动态、太灵活了,使得类型检查排错成为几乎不可能的事情,大大增加了代码的风险性。
第二个原因是空间原因。在二元类型系统中,所有的方法都存放在方法表中。而一个类型的方法表只存在一份。无论创建多少个对象实例,都共用本类型的 那个方法表。而在一元类型系统中,每个对象实例都有一份自己的方法表。这就造成了空间浪费。对象实例越多,空间浪费就越大。当然,正是因为这种空间浪费的 设计,才使得Javascript如此灵活强大。
-----------------
动态类型语言,具有动态期间进行类型检查的特点。这就意味着编译(解释)结果中,一定要包含type metadata的信息,供程序运行的时候,进行类型检查。
Java的编译结果中,包含这些Type metadata信息。但是,JVM的大多数指令的运行,并不需要使用这些Type metadata信息,只有在使用Reflection API的时候,才使用了这些Type metadata信息,而且这些Type metadata都是只读的,不可以操作的,还算不上真正的可以操作的data (数据)。
动态性就是带来了松耦合,把静态语言的dispatch by type强契约,变成了dispatch by name,甚至dispatch by pattern等弱契约。
到了最后,dispatch 干脆就弱化为 DSL,Message Protocal。到了Message Protocal,这就是SOA。动态性最终带来的就是DSL, SOA。
关于静态类型的更为臭名昭著的例子是Visitor Pattern的Type Dispatch。有两个选择,
或者使用 instanceof/Class.isAssignableFrom/Type Cast,或者引入visitable (or visited) interface。
由于摆脱了类型契约,动态语言中就没有这个麻烦。
在动态语言中,我们可以把方法表理解为一个Hash Table,每一个方法名都是Hash Table中的键值,其对应的内容是方法实现代码的真正存放地址。动态语言只关心对象的行为,而不关心对象的类型。Hash Table这种结构,能够使得解释器根据方法名迅速定位到对应的方法实现代码。
我们可以用两个简单的等式,来描述静态类型语言和动态类型语言的方法表的数据结构。
静态类型语言:方法表 = 数组
动态类型语言:方法表 = Hash Table
当然,Javascript是一元语言,不存在类型和实例的区别。而在C++、Java、C#、Python、Ruby等二元语言中,类型和实例是两个明确分开的概念。
如果要讲类型的话,每一个Javascript对象都是一个潜在的扩展类型。只要属性替换和填充发生了,一个新的扩展类型就产生了。
每个Javascript对象都有一个最基本的原型(prototype)对象,每次新生成一个Javascript对象,实际上就是把那个原型对象复制了一遍。这就像把一个Hash Table完全复制一遍一样。
如果你需要改变所有新对象的某个属性,你只要修改原型(prototype)对象的对应属性就可以了。之后,从那个原型复制出来的所有新对象的对应属性都会跟着改变。
Javascript访问原型对象相当容易。myObject.prototype就可以获得myObject的原型对象。myObject就是从myObject.prototype这个对象复制出来的。
Javascript并没有类型的概念,最初的原型也都是对象实例。这种语言叫做一元语言。与之相对的,有类型和实例概念的语言,比如,C、Java、C#、Python、Ruby等语言,叫做二元语言。
Javascript比Python、Ruby更具有动态性,主要就是因为它的“一元类型系统”的特性。
Python和Ruby是不关心类型,但毕竟还有类型的概念。但是,在Javascript这种一元语言中,干脆就取消类型的概念,所有的对象实例都自成一个类型,从而获得了更大的动态性。
Javascript语言把“Duck Type”的思想发挥到了极致——我们不关心它是不是鸭子,我们只关心它会不会像鸭子一样叫,我们只关心它会不会像鸭子一样摇摇摆摆走路。啊,不,事实 上,我们根本就不关心它是否像鸭子,因为在我们的系统中根本就没有鸭子这个东西。我们只关心它会不会叫,会不会摇摇摆摆走路。
读者可能会问了,既然一元类型系统比二元类型系统更加灵活,那么为什么大部分语言都采用二元类型系统的设计思路?为什么不能像Javascript一样,取消类型的概念呢?所有的原初“类型”都是“原型”对象,这样不是很好吗?
实际上,我也是这么想的。我更欣赏Javascript这种类型系统设计思路。在我看来,之所以大部分语言不采取这种设计思路,是出于两种原因。
第一个原因说起来有些无聊,那就是,Javascript语言的这种一元类型系统太动态、太灵活了,使得类型检查排错成为几乎不可能的事情,大大增加了代码的风险性。
第二个原因是空间原因。在二元类型系统中,所有的方法都存放在方法表中。而一个类型的方法表只存在一份。无论创建多少个对象实例,都共用本类型的 那个方法表。而在一元类型系统中,每个对象实例都有一份自己的方法表。这就造成了空间浪费。对象实例越多,空间浪费就越大。当然,正是因为这种空间浪费的 设计,才使得Javascript如此灵活强大。
-----------------
动态类型语言,具有动态期间进行类型检查的特点。这就意味着编译(解释)结果中,一定要包含type metadata的信息,供程序运行的时候,进行类型检查。
Java的编译结果中,包含这些Type metadata信息。但是,JVM的大多数指令的运行,并不需要使用这些Type metadata信息,只有在使用Reflection API的时候,才使用了这些Type metadata信息,而且这些Type metadata都是只读的,不可以操作的,还算不上真正的可以操作的data (数据)。
动态性就是带来了松耦合,把静态语言的dispatch by type强契约,变成了dispatch by name,甚至dispatch by pattern等弱契约。
到了最后,dispatch 干脆就弱化为 DSL,Message Protocal。到了Message Protocal,这就是SOA。动态性最终带来的就是DSL, SOA。
关于静态类型的更为臭名昭著的例子是Visitor Pattern的Type Dispatch。有两个选择,
或者使用 instanceof/Class.isAssignableFrom/Type Cast,或者引入visitable (or visited) interface。
由于摆脱了类型契约,动态语言中就没有这个麻烦。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7399830/viewspace-707389/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/7399830/viewspace-707389/