UnityScript versus JavaScript

From Unify Community Wiki
Revision as of 01:27, 31 March 2013 by Ppeterson (Talk | contribs)

Jump to: navigation, search

Though many in the Unity development community (and even in the Unity corporation) refer to UnityScript and JavaScript as if they were equivalent or interchangeable, the two are very different languages. Though they resemble each other syntactically, they have very different semantics.



While "JavaScript" is merely a generic name and could refer to any one of many implementations of the ECMAScript specification, Unity's ".js" language doesn't even come close to conforming to that specification — nor does it try to. The vast majority of JavaScript libraries you can find will probably not work just by copying them into Unity. If anything, the language is most similar to Microsoft's JScript.NET, although it is not quite identical.

Some people consider this to be "just semantics," but when people call "JavaScript" what is properly UnityScript, it becomes a lot harder to search the internet for solutions and help with UnityScript. It's also quite a lot of trouble to put oneself through to constantly specify whether you mean "real JavaScript" or "Unity's JavaScript," so instead, I just stick with "JavaScript" for real JavaScript and "UnityScript" for Unity's JavaScript. Unity sometimes calls UnityScript "UnityScript" too, so it's not like I'm the only one doing this.

This page attempts to explain the differences between JavaScript (ECMAScript) and UnityScript as succinctly and clearly as possible. If you have any suggestions, feel free to add them to the author's talk page.

JavaScript is class-free

JavaScript (i.e. the language that's not UnityScript) has no classes. This is because it's a prototypal language, and not a classical one. Inheritance happens with [more dynamic] objects, rather than [unchanging] classes, in JavaScript.

function Machine(x) {
   this.kind = ["bulldozer", "lathe", "car"][x];
var c = new Machine(2);
print(typeof c.announce); // "undefined"
Machine.prototype.announce = function() {
   print("I am a "+this.kind+".");
print(typeof c.announce); // "function"
c.announce(); // prints "I am a car."

As shown above, in JavaScript, a function can create an object, when called with the new keyword. After that happens, the prototype (template) object Machine can be extended to provide additional functionality, and all class instances, past and future, are affected by this extension.

UnityScript has classes, unlike JavaScript. Also, in UnityScript, once you've defined a class, that class is more or less fixed for the duration of the runtime of your program. (N.B. There may be some exceptions to this rule, such as Reflection, but you probably do not need this and should not be using it because it's not very efficient.) However, the class system has the added benefit of being an easier-to-read, more familiar (to most) language.

class Machine {
   var kind : String; // fields are public by default
   function Machine(x : int) {
      this.kind = ["bulldozer", "lathe", "car"][x];
   function announce() {
      print("I am a "+this.kind+".");
print(typeof Machine.prototype); // causes a compile-time error
var c = new Machine(2);
c.announce(); // prints  "I am a car."

One variable declaration at a time

JavaScript supports multiple variable declarations in one var statement.

var x = 3, y = 4;

UnityScript does not.

Assignment cannot be an expression

In JavaScript, assignment is treated as an expression.

var x = 3; // x is 3
var y = (x=x+2); // x is 5, y is 5

In Unity, assignment is always a statement, so this must be broken up into steps.

var x = 3; // x is 3
  // var y = (x=x+2); // Error!
x = x + 2; // x is 5
var y = x; // y is 5

The exceptions are the [pre/post]-[in/de]crement operations:

var x = 3;
var y = x++; // x is 4, y is 3
x = 3;
var z = ++x; // x is 4, z is 4

No Global Variables

Every top-level variable in JavaScript is global. Additionally, any variable declaration not preceded by the var statement is global. This is not the case in UnityScript, as every object-owned .js file is essentially wrapped in a class JSFileName extends MonoBehaviour {...} block. The absent-var behavior is avoided by the language prohibiting missing var keywords.

By extension, there is no global object, so the this keyword will always point to the innermost object's instance, in contrast to JS's this keyword, which will sometimes point to the global object.

Dynamic typing can be inefficient

Both UnityScript and Javascript support dynamic typing.

var x;
x = 3;
x = new Array(); // works in both UnityScript and JavaScript

However, in UnityScript, when one uses this feature, it can become inefficient to access x many times. This is because the compiler recognizes that the type of the object may change over time. Therefore, the compiler basically produces what would be produced by the following C# code:

Object x;
x = 3;
x = new Array();

This works just fine. Anything can be cast to an object. But when you want to call a method on x, such as Push:

var x;
x = 3;
x = new Array();
x.Push(2); // Error! BCE0019: 'Push' is not a member of 'Object'.

Basically the compiler doesn't know what type to cast x to. So we have to do it ourselves by replacing the last line with something like (x cast Array).Push(2);, and a lot of casting can make things inefficient.

If we never change the type of x, however, the compiler will recognize this and not use the Object datatype for our variable.

var x;
x = 3; 
x += 1;
// x is typed as an integer because we only use one type throughout our program

By contrast to all of this, in JavaScript, everything is dynamically typed, so instead of having an inefficient dynamic typing system, everything in JavaScript is inefficient.


In JavaScript, privacy is rather unconventional.

function Person() { // (this is the *JavaScript* way of doing privacy)
   var secret = "I am a mass murderer."; // private
   this.speak = function() { print("Don't make me tell you my secret! "+secret); }; // prints secret
var bob = new Person();
print(bob.secret); // undefined
bob.speak(); // prints "Don't make me tell you my secret! I am a mass murderer."

In UnityScript, it can be more intuitive.

class Person { // UnityScript only; impossible in JavaScript
   private var secret : String;
   function Person() {
      secret = "I am a mass murderer.";
   function speak() {
      print("Don't make me tell you my secret! "+secret);
var bob = new Person();
print(bob.secret); // undefined
bob.speak(); // prints "Don't make me tell you my secret! I am a mass murderer."

No Bling

Dollar signs ($) are not allowed in UnityScript identifiers as they are in JS identifiers. (In JS, the symbol is often used for c-style namespacing or as the name of a do-everything function.)

var lib$cosine = 3; // ERROR! in UnityScript

No with statement

There is no with statement in UnityScript. This is probably for the best, as JavaScript's with statement causes the whole language to be slower, regardless of whether the statement is used or not. It is also considered harmful.

UnityScript has .NET's OOP features

UnityScript supports classes, as well as "protection levels" (public, private, protected) and "static" keyword options. Since it also supports explicit typing, it also has support for "generics" (runtime type enforcement), which JavaScript has no notion of.

No delete in UnityScript

JavaScript allows a way for you to remove declared variables from the namespace. UnityScript doesn't.

No Regular Expression Literals (RegExp or regex)

In JavaScript (and even JScript.NET, the language on which UnityScript is based), one can directly define a regular expression using a syntax like the following:

var sentence = /[A-Z].*[\.\?!]/; // JavaScript/JScript.NET only

UnityScript does not support this, understandably, as regular expressions are pretty uncommon in game code, and the syntax is hard to tokenize/lex.

The this keyword

In JavaScript, this can refer to one of two things, depending on the context:

  • The global object (best not explained here)
  • The object to which the current method or constructor belongs

In Java, as in Oracle Java (not JavaScript), only the second of these is true. That is, this will always refer to an instance of an object, specifically the object on which a method is being called.

class Person {
   var species : String;
   var eyeColor : String;
   var hairColor : String;
   function Person(eyeColor : String) {
      species = "homo sapiens";
      this.eyeColor = eyeColor;
      hairColor = "brown";

Note that without the this. qualifier, by default, an identifier (e.g. hairColor) will refer to a class field (this.hairColor) UNLESS a variable of the same name has already been defined (e.g. the argument eyeColor). In such a case, this. must be explicitly included to disambiguate between the variable name and the field (property) name, as seen on line 7 of the code snippet.

See Also

Personal tools