How to Write a Zotero Translator:
A Practical Beginners Guide for Humanists

By: Adam Crymble

 

Chapter 10: JavaScript Functions

 previous button  next button

This Chapter

Functions

Functions are like blocks of reusable code. If you knew you were going to need ten lines of code fifty times, you could type them out fifty times, or you could create a Function once, and then run that Function the desired number of times to achieve the same goals. For your Zotero translator, you will be making several Functions.

Function Syntax

Defining a Function looks a lot like defining an If Statement or a Loop.

Example 10.1

function myFunction() {
var x = "abc";
var y = x + "def";
if (y == "abcdef") {
y = "ghi";
}
Zotero.debug(y);
}

"function" is to declaring a Function as "var" is to declaring a Variable. The names of Functions are governed by the same naming rules as those which govern Variable names. Therefore anything but reserved words are ok. However, keep in mind that a name which describes what the Function does is generally best.

{ } create a container for all the code in the Function, just like in Loops or If Statements.

Functions can contain any legitimate code.

Calling a Function

This can be translated into layman's terms as "telling the computer to run the code in the Function".

Simply declaring a Function doesn't mean it will run. In the last chapter you learned that code is not simply read by a computer from top to bottom. Creating a Loop causes the computer to jump back to the top of the Loop a specified number of times before moving on. Similarly, Functions do not run at all unless they are "called," just like your microwave doesn't automatically run unless you push start. When you call a Function, the computer jumps to that Function and runs all the code contained within that Function, no matter where in your program that code happens to be.

There are two exceptions to this rule: the "detectWeb" Function and the "doWeb" Function, which you will create when writing your translator. These Functions will automatically run once when you execute the code because of the nature of Scaffold. As mentioned, Scaffold is a "sandbox" and hides some of the important code from you. Follow this rule of thumb and you should be ok: If you have created a Function yourself that is not "detectWeb" or "doWeb" then you will have to call it for it to run. If you want to run "detectWeb" or "doWeb" more than once, you will have to call it; otherwise they will run automatically. You will see this in action in the coming chapters.

When you get to a point in your program where you want the Function to run, simply call it like this:

Example 10.2

myFunction();

You could run the Function as many times as you like:

myFunction();
myFunction();
myFunction();
myFunction();

(Although a Loop could do this more efficiently).

In some cases you will have to include Arguments between the parentheses. These Arguments tell the computer to look elsewhere for the Variables used in the Function because they were declared elsewhere. Without including these Arguments, the Function will assign a value of "undeclared" to the Variables in question — not what you want. This is due to something called Variable Scope. If you try to debug a Variable and find the result is "x is not defined" or "(undefined)" then you have a problem with Scope.

Variable Scope

Functions can only see Global Variables and their own Local Variables.

A Global Variable is a Variable that is not defined within any other Functions. If you have a blank "Detect Code" window open in Scaffold and you declare a Simple Variable, that Variable is Global. That means all Loops, Statements and Functions on that "Detect Code" tab can use that Variable. If you were to place that same Variable declaration inside a Loop, Statement, or a Function, it is now Local. Now, only that Loop, Statement or Function can "see" that Variable. This is known as Variable Scope.

Copy the following code into a blank "Detect Code" window in Scaffold.

Example 10.3

var x = 9;
var y = 1;

function sampleFunction() {
var z = 2;
var m = z + y;
Zotero.debug(m);
}
sampleFunction();

In this example, the Variables "x" and "y" are Global Variables. "m" and "z" are Local Variables declared within "sampleFunction."

Now try this altered code:

Example 10.4

function abc(){
var x = 9;
var y = 1;
}

function sampleFunction() {
var z = 2;
var m = z + y;
Zotero.debug(m);
}
sampleFunction();

The "x" and "y" Variables are no longer Global, but Local to the "abc" Function. Since "sampleFunction" can no longer see "y" this code will now debug as:

Example 10.5

"ReferenceError: y is not defined in parsing detectCode"

If you needed to find the value of "m" in the above code, you would have to pass the proper Arguments to your Functions. In this case, the Argument is the Variable "y."

Example 10.6

function abc(){
var x = 9;
var y = 1;
sampleFunction(y);
}

function sampleFunction(y) {
var z = 2;
var m = z + y;
Zotero.debug(m);
}

abc();

Now what happens is that the "abc" Function runs when the program is executed. This declares the Variables "x" and "y" and in turn runs "sampleFunction." "sampleFunction" is passed the Argument "y", which it then uses.

Notice that the Function declaration for "sampleFunction" now expects to receive one Argument, meaning that "sampleFunction" knows it cannot produce an answer without receiving an outside value. In this case it is a "y". This outside value must be included between the parentheses whenever the Function is called.

There is one more important thing to understand here. The "y" used in Function "abc" is not the same as the "y" used in "sampleFunction". These are actually two different Variables using the same name. That is why the term "Scope" is important.

Try changing the argument passed to "sampleFunction" from "y" to "x".

Example 10.7

function abc(){
var x = 9;
var y = 1;
sampleFunction(x);
}

function sampleFunction(y) {
var z = 2;
var m = z + y;
Zotero.debug(m);
}

abc();

It might surprise you to discover this now gives you a debug output of 11, or "z + x". That is because all that the "y" in "sampleFunction" does is create a new Variable named "y". Because the two "y" Variables have a different Scope and can't see each other, there is no conflict.

Any time "sampleFunction" is called, the first Argument is stored in the "y" Variable of "sampleFunction". The fact that these two different Variables have the same name is likely confusing. Try changing the Argument in "sampleFunction" to "q" and try the code again.

Example 10.8

function abc(){
var x = 9;
var y = 1;
sampleFunction(x);
}

function sampleFunction(q) {
var z = 2;
var m = z + q;
Zotero.debug(m);
}

abc();

This is equivalent to assigning the value of "x" to the Variable "q".

Example 10.9

q=x;

If more than one Argument is required for a Function, separate them with a comma and space and be sure to include the extra Arguments in both the Function declaration and when calling the Function. Eg:

Example 10.10

function sampleFunction(w, x, y, z){

}

var a;
var b;
var c;

sampleFunction(a, b, c, 5);

Variable Scope is a tricky thing to master. If your Functions are not giving you the desired results, or you are seeing the following error message, chances are you have a problem with Variable Scope.

Example 10.11

ReferenceError: x is not defined in parsing detectCode
(undefined)

If you can't figure out where you are going wrong, this is the type of problem that posting on a JavaScript message board might solve.

All Zotero translators contain a Function named "detectWeb" and "doWeb" which you will learn how to write in the coming chapters. These Functions and any other functions that use XPaths, or the URL of a website must be passed the Arguments (doc, url). This means your Function can use the doc (DOM) and the url of the current page as sources. Unless you are creating a Function of your own, all of your Functions should be passed these two Arguments.

Returning a Value

Sometimes all you want a Function to do is spit out a result. To do this, use "return". You can then debug the entire Function to see the result.

Example 10.12

function myFunction(){
var x = "abc";
if (x == "abc") {
return "true";
} else {
return 3;
}

}

Zotero.debug(myFunction());

Practice

What you should understand before moving on

Further Reading