subtle differences between JavaScript and Lua

stefs picture stefs · Jun 20, 2009 · Viewed 40.8k times · Source

I simply love JavaScript. It's so elegant (imagine the quiet sound of lovestruck fanboy sighing in the background).

So, recently I have played with Lua via the löve2d framework (nice!) - and I think Lua is also great. They way I see it, those two languages are very similar.

There are obvious differences, like

  • syntax
  • problem domain
  • libraries
  • types (a bit)

but which are the more subtle ones? Is there anything a JavaScript coder would take for granted that works in Lua just slightly different? Are there any pitfalls that may not be obvious to the experienced coder of one language trying the other one?

For example: in Lua, arrays and hashes are not separate (there are only tables) - in JavaScript, they are numerical Arrays and hashed Objects. Well, this is one of the more obvious differences.

But are there differences in variable scope, immutability or something like this?

Answer

outis picture outis · Jun 21, 2009

Some more differences:

  • Lua has native support for coroutines.
    • UPDATE: JS now contains the yield keyword inside generators, giving it support for coroutines.
  • Lua doesn't convert between types for any comparison operators. In JS, only === and !== don't type juggle.
  • Lua has an exponentiation operator (^); JS doesn't. JS uses different operators, including the ternary conditional operator (?: vs and/or), and, as of 5.3, bitwise operators (&, |, etc. vs. metamethods ).
    • UPDATE: JS now has the exponentiation operator **.
  • JS has increment/decrement, type operators (typeof and instanceof), additional assignment operators and additional comparison operators.
  • In JS, the ==, ===, != and !== operators are of lower precedence than >, >=, <, <=. In Lua, all comparison operators are the same precedence.
  • Lua supports tail calls.
  • Lua supports assignment to a list of variables. While it isn't yet standard in Javascript, Mozilla's JS engine (and Opera's, to an extent) has supported a similar feature since JS 1.7 (available as part of Firefox 2) under the name "destructuring assignment". Destructuring in JS is more general, as it can be used in contexts other than assignment, such as function definitions & calls and loop initializers. Destructuring assignment has been a proposed addition to ECMAScript (the language standard behind Javascript) for awhile.
    • UPDATE: Destructuring (and destructuring assignment) is now part of the spec for ECMAScript - already implemented in many engines.
  • In Lua, you can overload operators.
  • In Lua, you can manipulate environments with getfenv and setfenv in Lua 5.1 or _ENV in Lua 5.2 and 5.3.
  • In JS, all functions are variadic. In Lua, functions must be explicitly declared as variadic.
  • Foreach in JS loops over object properties. Foreach in Lua (which use the keyword for) loops over iterators and is more general.
    • UPDATE: JS has Iterables now too, many of which are built into the regular data structures you'd expect, such as Array. These can be looped over with the for...of syntax. For regular Objects, one can implement their own iterator functions. This brings it much closer to Lua.
  • JS has global and function scope. Lua has global and block scope. Control structures (e.g. if, for, while) introduce new blocks.

    • Due to differences in scoping rules, a closure's referencing of an outer variable (called "upvalues" in Lua parlance) may be handled differently in Lua and in Javascript. This is most commonly experienced with closures in for loops, and catches some people by surprise. In Javascript, the body of a for loop doesn't introduce a new scope, so any functions declared in the loop body all reference the same outer variables. In Lua, each iteration of the for loop creates new local variables for each loop variable.

      local i='foo'
      for i=1,10 do
        -- "i" here is not the local "i" declared above
        ...
      end
      print(i) -- prints 'foo'
      

      The above code is equivalent to:

      local i='foo'
      do
        local _i=1
        while _i<10 do
          local i=_i
          ...
          _i=_i+1
        end
      end
      print(i)
      

      As a consequence, functions defined in separate iterations have different upvalues for each referenced loop variable. See also Nicolas Bola's answers to Implementation of closures in Lua? and "What are the correct semantics of a closure over a loop variable?", and "The Semantics of the Generic for".

      UPDATE: JS has block scope now. Variables defined with let or const respect block scope.

  • Integer literals in JS can be in octal.
  • JS has explicit Unicode support, and internally strings are encoded in UTF-16 (so they are sequences of pairs of bytes). Various built-in JavaScript functions use Unicode data, such as "pâté".toUpperCase() ("PÂTÉ"). Lua 5.3 and up have Unicode code point escape sequences in string literals (with the same syntax as JavaScript code point escape sequences) as well as the built-in utf8 library, which provides basic support for the UTF-8 encoding (such as encoding code points into UTF-8 and decoding UTF-8 into code points, getting the number of code points in a string, and iterating over code points). Strings in Lua are sequences of individual bytes and can contain text in any encoding or arbitrary binary data. Lua does not have any built-in functions that use Unicode data; the behavior of string.upper depends on the C locale.
  • In Lua, the not, or, and keywords are used in place of JS's !, ||, &&.
  • Lua uses ~= for "not equal", whereas JS uses !==. For example, if foo ~= 20 then ... end.
  • Lua 5.3 and up use ~ for binary bitwise XOR, whereas JS uses ^.
  • In Lua, any type of value (except nil and NaN) can be used to index a table. In JavaScript, all non-string types (except Symbol) are converted to strings before being used to index an object. For example, after evaluation of the following code, the value of obj[1] will be "string one" in JavaScript, but "number one" in Lua: obj = {}; obj[1] = "number one"; obj["1"] = "string one";.
  • In JS, assignments are treated as expressions, but in Lua they are not. Thus, JS allows assignments in conditions of if, while, and do while statements, but Lua does not in if, while, and repeat until statements. For example, if (x = 'a') {} is valid JS, but if x = 'a' do end is invalid Lua.
  • Lua has syntactic sugar for declaring block-scoped function variables, functions that are fields, and methods (local function() end, function t.fieldname() end, function t:methodname() end). JS declares these with an equals sign (let funcname = function optionalFuncname() {}, objectname.fieldname = function () {}).