如果要用一句点睛之笔形容静态类型和动态类型最大的不同。 我会举C/C++和javascript的例子。
一个指针在runtime运行时就仅仅只是一个内存地址而已, 只有在写源代码文本的时候, 编译器的限制才使得它具有了类型, 这便是静态类型。
而对任意一个javascript引用, 我总是能在运行时了解该对象中所有的key字段, 即了解该对象具有怎样的shape(shape这个词是前不久从Typescript的文档上看到的, 觉得非常精妙, 就不翻译了), 这就是动态类型。
熟悉Java的朋友应该了解, 在runtime运行时动态获取一个类型的信息的方式叫做Reflect(一般译作"反射"),有可能会惊讶与javascript随时都在利用反射:)
不过这是后话了, 以后空了我许会写一篇有关反射的博客。
Javascript类型系统 – 动态类型清流
类型就是一个引用的shape, 这句话是我前不久从Typescript文档里看到的, 觉得尤为精妙。无论是静态类型还是动态类型, 都可以用这句话来描述。同样的, 要满足了这个原则,你也可以构建出千奇百怪百花齐放的类型系统, 而javascript的类型系统 – 原型链, 就是非常值得一提的类型系统。
动态反射
如果我实现了一个函数库Reflect, 这个函数库可以在运行时接受一个引用(内存指针), 从而了解这块内存(也许不是一块完整的内存而是由链表或者什么数据结构组织到一起的多块内存也说不定哦)所代表的对象含有哪些成员, 我就可以(不要脸地说)实现了动态类型系统。
构造/复用对象
复用对象最简单的做法是把一个对象所有的成员复制到新的内存去, 然后返回该内存头部的引用(内存指针)。但是这种做法太浪费了, 因为函数代码完全可以通过传入不同参数对象来实现复用而没必要让每一个构造出的对象都复制一份函数代码。
所以为了节省内存, 我们需要一个"模板对象", 这个模板对象的成员代表了一类,一群,一堆,一抹多对象的公有部分。
然后, 实现一种机制, 这种机制让新产生的每个对象里都保有一个成员, 该成员指向"模板对象", 并约定, 当用户代码调用了新产生的对象上某个没找到的成员时, 就往这个对象的"模板对象"上找, 递归到顶为止。
原型链
模板对象四个字太长了, 哪怕写成英文Pattern Object也是两个词13个字母, 干脆叫原型好了prototype 7个字母,