jMar"s Blog DevSmash Developer Portal

Friday, March 14, 2008

Understanding Loose Typing in JavaScript

For many front end developers, JavaScript was their first taste of a scripting and/or interpretive language. To these developers, the concept and implications of loosely typed variables may be second nature. However, the explosive growth in the demand for Web 2.0-ish applications has resulted in a growing number of back end developers that have had to dip their feet into pool of client side technologies. Many of these developers are coming from a background in strongly typed languages, such as C# and Java, and are unfamiliar with both the freedom and the potential pitfalls involved in working with loosely typed variables.

Since the concept of loose typing is so fundamental to scripting in JavaScript, an understanding of it is essential. This article is a top level discussion of loose typing in JavaScript. Since there may be subtle differences in loose typing from language to language, let me constrain this discussion to the context of JavaScript. OK, let's dig in...

What is Loose Typing?

Well, this seems like a good place to start. It is important to understand both what loose typing is, and what loose typing is not. Loose typing means that variables are declared without a type. This is in contrast to strongly typed languages that require typed declarations. Consider the following examples:

/* JavaScript Example (loose typing) */
var a = 13; // Number declaration
var b = "thirteen"; // String declaration

/* Java Example (strong typing) */
int a = 13; // int declaration
String b = "thirteen"; // String declaration

Notice that in the JavaScript example, both a and b are declared as type var. Please note, however, that this does not mean that they do not have a type, or even that they are of type "var". Variables in JavaScript are typed, but that type is determined internally. In the above example, var a will be type Number and var b will be type String. These are two out of the three primitives in JavaScript, the third being Boolean.

JavaScript also has other types beyond primitives. The type diagram for JavaScript is as follows (as per Mozilla):

Ya really - Null and Undefined too.

Note, however, that this distinction between primitives and objects will be dismissed in JavaScript 2.0. You can read more about that here.

Type Coercion

Type coercion is a topic that is closely associated with loose typing. Since data types are managed internally, types are often converted internally as well. Understanding the rules of type coercion is extremely important. Consider the following expressions, and make sure you understand them:

7 + 7 + 7; // = 21
7 + 7 + "7"; // = 147
"7" + 7 + 7; // = 777

In the examples above, arithmetic is carried out as normal (left to right) until a String is encountered. From that point forward, all entities are converted to a String and then concatenated.

Type coercion also occurs when doing comparisons. You can, however, forbid type coercion by using the === operator. Consider these examples:

1 == true; // = true
1 === true; // = false

7 == "7"; // = true
7 === "7"; // = false;

There are methods to explicitly convert a variable's type as well, such as parseInt and parseFloat (both of which convert a String to a Number).

Double negation (!!) can also be used to cast a Number or String to a Boolean. Consider the following example:

true == !"0"; // = false
true == !!"0"; // = true
Conclusion

This obviously is not a definitive reference to loose typing in JavaScript (or type coercion for that matter). I do hope, however, that this will be a useful resource to those who are not familiar with these topics, and a good refresher for those who already are. I have tried to insure that the above is accurate, but if you notice anything incorrect, please let me know! And as always, thanks for reading!



9 comments:

Anonymous said...

simple and elegant.

Anonymous said...

Found you via Google...

"Ensure" that you do not forget to use a spell-checker before posting.. Irony alert!! ;)

"I have tried to *insure* that the above is accurate, but *if you notice anything incorrect, please let me know!* And as always, thanks for reading!"

akropp said...

You are mixing loose (aka weak) and dynamic typing here. Loose typing is the "7" + 7 example, mixing types to produce a new value. In a strong typed language this would give you a compilation or runtime error.

In contrast a dynamically typed language is one where the types are determined at runtime. Statically typed languages are languages where the types are determined at compile time.

Anonymous said...

I just don't get the purpose of loosely-typed variables. All it seems to do is make a language appear less threatening to beginners while providing nothing but trouble to everyone else.

One of the nice things about specifying your data type is the inherent safety it provides: for example it often produces compiler errors if you mix up your arguments when calling a function with multiple params.

The main problem is why would you not know what your data will represent? If a variable can be named then it can be given a type too: for example, if you've named a variable "lastName" when why would you ever put an integer into it?

If you don't know what your variable is intending to represent then you shouldn't be using it. These kinds of languages seem obscenely bug-prone and then have these needlessly complicated rules on how the variables are used.

gngl said...

"Loose typing means that variables are declared without a type."

Whoa, stop right there, cowboy. That's hardly what I'd call "loose typing". Typing with untyped variables can be pretty strong, if the values used in the computation carry their type and you have proper operators to manipulate them. Javascript does have the former, but it doesn't have the latter - the horrible coercion rules are what's messy about JS types, not the dynamic typing nature. (Common Lisp, Scheme, Python etc. deal with this just fine, but they don't implicitly convert stuff as in 1+"2" -> "12", that's why they're more sensible.)

"Notice that in the JavaScript example, both a and b are declared as type var."

They're not declared as "type var". In fact, they're not declared at all! They're simply values bound to names. var does not declare variables, it establishes variable bindings.

"JavaScript also has other types beyond primitives."

JavaScript doesn't actually have any primitives. Everything is an object in JS. Unless, by "primitives", you mean "atomic values" (as opposed to compound/aggregate data structures), but that is a different issue obviously.

@Anonymous: "I just don't get the purpose of loosely-typed variables. All it seems to do is make a language appear less threatening to beginners while providing nothing but trouble to everyone else."

If you really think this, you're a very undemanding person, with respect to your tools!

The purpose of static type systems is to admit correct, sensible programs, AND to reject nonsensical programs. Real-world static type systems, however, have one of the two following problems:

1) They either reject some correct programs (as in, they're overly restrictive), or
2) they accept some incorrect programs (can't catch some typing errors at compile time).

Some languages, such as Java, delight in doing both at the same time: on one hand, you can't specify covariance or contravariance of method parameter types when subclassing, thus being restricted to invariant parameters, but at the same time, Java arrays are covariant by default, thus making it possible for them to cause run-time type errors in some scenarios.

It doesn't seem to be possible to deal with 1) and 2) at the same time. If you try to do that, you'll find yourself designing increasingly complex type systems involving such things as phantom types, existential types, dependent types and what not; and at one moment, the typing of certain programs may even become undecidable, thus making your compilers stuck in a loop or halting with an "I don't know how to check this" kind of message.

Dynamically typed languages simply adopt the "we don't want to reject any program that might make sense, let's just check it at runtime" approach (and with JIT, they at least try to remove as many checks as possible to have it run at a decent speed, and again, this approach seems to be quite successful). Surprisingly, for many people, it works just fine! Let me conclude with a quote by Alan Kay, one of the inventors of Smalltalk, which summarizes the whole issue nicely: "I'm not against types, but I don't know of any type systems that aren't a complete pain, so I still like dynamic typing."

Jeremiah Deasey said...
This comment has been removed by the author.
Jeremiah Deasey said...

Thanks for the examples and walk through.

Also, in your closing statement, the correct word would be "ensure".

1.
make certain that (something) shall occur or be the case.
"the client must ensure that accurate records be kept"
synonyms: make sure, make certain, see to it;

Anonymous said...

7 + 7 + 7; // = 21
7 + 7 + "7"; // = 147
"7" + 7 + 7; // = 777

In the examples above, arithmetic is carried out as normal (left to right) until a String is encountered. From that point forward, all entities are converted to a String and then concatenated.


Might be useful to mention that the arithmetic is carried out until a string is encountered only if you're using the + operator.

"37" + 7 //results in "377"
"37" - 7 //results in 30

Makayla Charleston said...

This is what I have been searching in many websites and I finally found it here. Amazing article. I am so impressed. Could never think of such a thing is possible with it...I think you have a great knowledge especially while dealings with such subjects.
clipping path