// Declaration, calling and returning function add(a, b) { return a + b; } assert(add(1, 2) == 3); assert(add("a", "b") == "ab"); assert(add(add(1, 2), add(3, 4)) == 10); // Functions are there from the start of their block assert(later(2) == 4); function later(n) { return n * 2; } // No return, or a bare return, gives undefined function nothing() { } function bare() { return; } assert(isundefined(nothing())); assert(isundefined(bare())); function early(flag) { if(flag) return "early"; return "late"; } assert(early(true) == "early"); assert(early(false) == "late"); // Recursion function factorial(n) { if(n <= 1) return 1; return n * factorial(n - 1); } assert(factorial(1) == 1); assert(factorial(5) == 120); function countdown(n) { if(n == 0) return 0; return 1 + countdown(n - 1); } assert(countdown(5) == 5); // Default parameters function greet(name, greeting = "Hello") { return greeting + " " + name; } assert(greet("Ann") == "Hello Ann"); assert(greet("Ann", "Hi") == "Hi Ann"); function scale(value, factor = 2, offset = 1) { return value * factor + offset; } assert(scale(3) == 7); assert(scale(3, 3) == 10); assert(scale(3, 3, 0) == 9); function defaultsToArray(list = [1, 2]) { return len(list); } assert(defaultsToArray() == 2); assert(defaultsToArray([1]) == 1); // Extra arguments are ignored function one(a) { return a; } assert(one(1, 2, 3) == 1); // Undefined argument takes the default assert(greet("Bo", undefined) == "Hello Bo"); // Functions are values var fn = add; assert(fn(2, 3) == 5); assert(isfunction(fn)); assert(isfunction(add)); assert(!isfunction(1)); assert(!isfunction("add")); assert(!isfunction(null)); var anon = function(x) { return x * x; }; assert(anon(4) == 16); assert(isfunction(anon)); var list = [function() { return 1; }, function() { return 2; }]; assert(list[0]() == 1); assert(list[1]() == 2); var table = {double: function(x) { return x * 2; }, name: "t"}; assert(table.double(4) == 8); assert(table["double"](5) == 10); // Passing and returning functions function apply(f, v) { return f(v); } assert(apply(anon, 5) == 25); assert(apply(function(v) { return v + 1; }, 1) == 2); function pick(flag) { if(flag) return add; return anon; } assert(pick(true)(1, 1) == 2); assert(pick(false)(3) == 9); // A function sees and changes the variables of the script var counter = 0; function increment() { counter++; return counter; } increment(); increment(); assert(counter == 2); assert(increment() == 3); // Arguments are by value, but arrays and structs are shared function reassign(v) { v = 99; return v; } var original = 1; assert(reassign(original) == 99); assert(original == 1); function append(l) { push(l, 4); } var shared = [1, 2, 3]; append(shared); assert(len(shared) == 4); function rename(s) { s.name = "changed"; } var record = {name: "start"}; rename(record); assert(record.name == "changed"); function replace(l) { l = []; return len(l); } assert(replace(shared) == 0); assert(len(shared) == 4); // Locals are not shared between calls function local() { var inner = 0; inner++; return inner; } assert(local() == 1); assert(local() == 1); // Parameter named like an outer variable var value = "outer"; function shadows(value) { return value; } assert(shadows("inner") == "inner"); assert(value == "outer"); // Name of a function function named() {} assert(tostr(named) == "named"); // Mutual recursion function isEven(n) { if(n == 0) return true; return isOdd(n - 1); } function isOdd(n) { if(n == 0) return false; return isEven(n - 1); } assert(isEven(4)); assert(isOdd(3)); assert(!isEven(3)); // inline and noinline inline function twice(n) { return n * 2; } noinline function thrice(n) { return n * 3; } assert(twice(4) == 8); assert(thrice(4) == 12); var t = 0; for(var i = 0; i < 3; i++) t += twice(i) + thrice(i); assert(t == 0 + 5 + 10);