The Surprisingly Elegant Javascript Type Model

The Surprisingly Elegant Javascript Type Model

转自:http://vijayan.ca/blog/2012/02/21/javascript-type-model/

By now, Javascript programmers at large have generally gotten a handle on the prototype-based inheritance schemes that are possible within the language. Still, it seems that many people feel that JS’s idea of types is somewhat lacking. What I want to talk about is the reality that once you dust around the corners a little bit, it’s possible to see that Javascript contains within it the outlines of a reasonably well-defined, somewhat elegant (if not strictly enforced) type model.

I feel that a clear understanding of the nature of the existing informal system would be useful in informing further discussion on the nature of what a more formalized notion of types in Javascript might look like.

Let’s get started.

The Type Model

In idiomatic JS, we can consider three general categories of objects as composing the structural basis of types:

  • Type objects– These are just the constructor functions used to instantiate objects.
  • Traits objects– This is what I’ll call the objects referred to by theprototypefield of a constructor function. They provide the method bindings for instances of the type they are associated with. Traits objects are basically thebehaviour specificationfor the instances a type.
  • Instance objects– These are the actual instances. They are created with their prototype (i.e. delegation target) pointing to the trait object of their type.

Note that these categories are neither exclusive, nor complete. A single Javascript object can belong to more than one category, and there can exist Javascript objects that fall into none of the above categories. But I’ll get to that later. Here’s a diagram showing how these objects are structured:

As noted earlier,Type(in blue) is the constructor function,Type.prototypepoints to the traits object (in green), andnew Type()(in gray) is an instance ofType. The light-gray dotted line points out the instance-of relationship betweennew Type()andType. This structure is pretty simple: Types have a field namedprototypethat points to its traits object. The traits object has a field namedconstructorthat points back to the type it is associated with. The instance belongs to the type by virtue of the fact that its prototype(i.e. the object that it delegates to) refers to the traits object for the type.

Pretty straightforward so far, no?

The Subtyping Model

Let’s keep going with that diagram, and model what subtyping looks like in Javascript:

Here, the light-blue dotted line shows the implicitsubtype-ofrelationship betweenSubtypeandType. The key point to notice here is the following:Subtypeis a subtype ofTypeby virtue of the fact thatSubtype.prototype(the traits object forSubtype) delegates toType.prototype(the traits object forType). This is how the delegation-based inheritance in Javascript is leveraged by the type model to enable the behaviour sharing necessary to represent subtype relationships.

This set of relationships is not my construction, but the structure that Javascriptalready uses, albeit in an informal way, to express type and subtype relationships. To prove this to ourselves, we just need to take a look at how the core Javascript constructor functions are organized. The following diagram shows theactualrelationship between three of the core Javascript constructor functions (Object,Function, andArray) and their instances:

Well that got complicated fast, didn’t it? Well, not really. It’s the same simple structure that we were looking at above, except we arealsodiagramming the fact that functions (and thus constructor functions, and thus the built-in constructor functionsObject,Function, andArraythemselves) are also instances ofFunction.

Yes, this means thatFunctionis an instance of itself (naturally, since it’s a function, and thus an instance ofFunction). This is something we’ve all been dealing with, knowingly or not, for a long time now – all constructor functions are regular functions and thus instances ofFunction, andFunctionitself is just the constructor function for constructing other functions, so it too is an instance ofFunction.

The Pullback

Let’s step back from that rat’s nest of a relationship diagram above. It looks hairy, but in reality it reflects a very simple truth about how Javascript already structures its type relationships for all of its built-in objects. Let’s throw away the instances, the traits objects and all of that stuff, and just look at the types and subtypes. The following is what we see already going on inside the world of Javascript types:

That right there is the type model that’s been present in Javascript for a while now. Pretty neat, huh?

Let’s think about what that picture above tells us. It first tells us that Javascript’s type system and subtyping model is built around two core types:ObjectandFunction. The root of allsubtype-ofrelationships isObject, andFunctionis the onlymeta-typein the system. Every type in this system hasFunctionfor its own type, includingFunctionitself.Functionis basically a universal meta-type, hiding in plain view. Neat, huh?

The Caveats

As cool as observing the above may be, this detailing is more than a bit idealized. While these relationships arethere, embedded right into the core Javascript types we use every day, they are notstrict, and they don’t completelycoverthe language.

Consider, for example,Object.prototype(or, for that matter,Function.prototype, orString.prototype, or any such traits object). In the context of the type system described above, those objects don’t actually HAVE a type of their own. They’re purely structural – they exist only to model the inheritance chain, and provide the method bindings for instances. Despite this, we can easily access them, and pass them around to functions, or return them from functions, or store them in arrays, or anything else you can do with other regular objects which have well-defined types.

For another example, consider the result of the expressionObject.create(null). This creates a new object that has no prototype that it delegates to. This newly created object is completely un-represented and un-captured by the type-system above.

Furthermore, none of the ‘primitive’ values are covered at all by this system. Primitive strings, numbers, booleans, and thenullvalue – none are really represented. However, this is ameliorated somewhat by the fact that primitive values (except fornullandundefined) are auto-boxed when methods are called on them. So, if you squint at them the right way (and refrain from doing things liketypeofon them), you can basically get away with looking at them as instances of their respective object-constructors (i.e. a primitive string as an “instance” ofString).

All that is a bit.. well.. discomforting. The type system above has some amount of elegance, and it’s already “present” in some sense in the language, but it’s voluntary and does not cover all of the objects we can touch from Javascript code. Is that a good thing or a bad thing? Well, that all depends on your temperament.

Javascript has always been a language with warts, but those warts are a reflection of its history. It’s the ugly duckling that inadvertantly took over the world. It’s the language the dynamic web was built on. It is what it is. However, I understand if you disagree, if this eating-french-fries-off-the-fine-china really gets under your skin. I can feel it a bit too.. tickling the back of my neck.

The Takeaway

I believe that the simple structure used within Javascript to model types contains within it the sketches of a truly powerful system for organizing the behaviour of complex object relationships. Javascript already represents classes, subclasses, and at least one notional metaclass.

Moving on, I think we can use this realization and understanding to start thinking about simple ways in which the existing capabilities can be extended (loosened in certain places, tightened up in others) to allow JS programmers to tap their full power.

More on that when I get a chance to write it up…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值