Apple Javascript Coding Guidelines: Tools & Languages: Other Languages
Apple Javascript Coding Guidelines: Tools & Languages: Other Languages
2009-05-05
Apple Inc. 2009 Apple Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, mechanical, electronic, photocopying, recording, or otherwise, without prior written permission of Apple Inc., with the following exceptions: Any person is hereby authorized to store documentation on a single computer for personal use only and to print copies of documentation for personal use provided that the documentation contains Apples copyright notice. The Apple logo is a trademark of Apple Inc. No licenses, express or implied, are granted with respect to any of the technology described in this document. Apple retains all intellectual property rights associated with the technology described in this document. This document is intended to assist application developers to develop applications only for Apple-labeled computers. Apple Inc. 1 Infinite Loop Cupertino, CA 95014 408-996-1010 Apple, the Apple logo, Mac, Mac OS, Numbers, Quartz, Safari, and Tiger are trademarks of Apple Inc., registered in the United States and other countries. Java is a registered trademark of Oracle and/or its affiliates.
Even though Apple has reviewed this document, APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS DOCUMENT, ITS QUALITY, ACCURACY, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED AS IS, AND YOU, THE READER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OR INACCURACY IN THIS DOCUMENT, even if advised of the possibility of such damages. THE WARRANTY AND REMEDIES SET FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer, agent, or employee is authorized to make any modification, extension, or addition to this warranty. Some states do not allow the exclusion or limitation of implied warranties or liability for incidental or consequential damages, so the above limitation or exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other rights which vary from state to state.
Contents
Introduction to Apple JavaScript Coding Guidelines 7
Who Should Read This Document? 7 Organization of This Document 7 Getting WebKit Builds 7 See Also 7
JavaScript Basics 9
Variables and Assignments 9 Data Types 10 Conditional Statements 10 Iteration Using Loops 12 Functions 12 Other Special Characters 13 The Question Mark and Colon Connectors 13 The Comma Separator 13
Object-Oriented JavaScript 15
Declaring an Object 15 Freeing Objects 16 Arrays and Objects 16 Getters and Setters 17 Inheritance 17 Copying an Object 18
3
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
4
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
Tables
JavaScript Basics 9
Table 1 Table 2 Primitive types in JavaScript 10 Conditional Operators 11
5
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
6
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
JavaScript Basics (page 9) introduces you to the JavaScript language and its basic structures. Object-Oriented JavaScript (page 15) discusses implementing an object in JavaScript. JavaScript Best Practices (page 21) shows you best practices to consider when coding in JavaScript.
See Also
The Reference Library > Apple Applications > Safari provides useful information on WebKit, the technology that provides Apples JavaScript runtime. Read WebKit DOM Programming Topics to learn more about the WebKit DOM interfaces, useful when coding a webpage or Dashboard widget.
Read Dashboard Programming Topics for information on the technologies available to you when creating a Dashboard widget. Additional Dashboard documents and sample code can be found in the Reference Library > Apple Applications > Dashboard. Installer Tiger Examples includes information on using the JavaScript runtime included with Apples Installer application.
See Also
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
JavaScript Basics
JavaScript is a programming language used by web browsers to provide scripting access to webpages. Its also being used more and more by desktop applications to provide an embedded scripting environment. If youre already familiar with programming, JavaScript is easy to learn and provides many of the conventions youre already used to. This chapter introduces JavaScript language basics, including variables, iteration, and functions. Read it if youre familiar with programming principles and want to learn specifics about the JavaScript language.
Important: Although you can use variables without declaring them explicitly, this is strongly discouraged. If you assign a value to an undeclared variable within a function, you are effectively creating and using a global variable instead of a variable local to that function. If the function ever gets called recursively, or if it is simultaneously called by two different user actions, the two executing instances of the function will clobber each others storage, resulting in very hard-to-find bugs. You should get in the habit of always declaring variables with the var keyword prior to use unless you are intentionally storing data globally. JavaScript variables are type agnosticthey can hold data of any type, like a number or an object. Once you declare a variable, you assign it a value like this:
foo = 5;
The assignment operator, =, gives the variable to its left the value of the expression to its right. As shown, a variable can be given a literal value like a number. It can also be given the value of another variable, like this:
var foo, bar; foo = 5; bar = foo;
Notice that foo and bar are declared in the same statement. This is a convenience used to declare many variables at once. Also note that each statement ends with a semicolon; JavaScript doesnt require this behavior but its a helpful convention that prevents ambiguity since it clearly marks the end of a statement. As statements get more complex, leaving out a semicolon may cause unintended consequences, so its best to include one at the end of each statement.
JavaScript Basics
Data Types
There are five primitive types in JavaScript, as shown in Table 1. Table 1 Type
number boolean string null
Primitive types in JavaScript Description A numerical value, capable of handling integer and floating-point values A logical value, either true or false A sequence of characters No value
JavaScript freely converts between data types as needed. Consider this statement:
var x = 1 + "2";
JavaScript first promotes the number 1 to the string "1" and then assigns the newly concatenated string "12" to x. If you want to do a numeric addition, you must use the parseInt function. For example:
var x = 1 + parseInt("2", 10);
The second parameter is a radix value, and is guessed based on the number if omitted following the same rules as a number in C. (Numbers starting with 0x are hex, other numbers starting with 0 are octal, and all other numbers are base 10.) Anything that is not a primitive type is an object. Primitive types are promoted to objects as needed, like this:
var message = "Hello World"; var firstCharacter = message.charAt(0);
JavaScript promotes the string "Hello World" to an object in order to call the charAt member function.
Conditional Statements
JavaScript includes conditional statements where enclosed code runs only if its preceding statement evaluates to a Boolean true value. A typical conditional statement starts with the if keyword and is followed by a statement to test, like this:
if ( variable == true) { // code to execute when this statement is true }
10
Data Types
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
JavaScript Basics
As in most C-like languages, true is equivalent to the number one (1) and false is equivalent to the number zero (0). Within the brackets { ... }, you place the code to execute if the preceding statement is true. The statement itself needs to evaluate to a Boolean value, either true or false. Various operators are used to assemble conditional statements, as shown in Table 2. Table 2 Conditional Operator
==
Returns true if both values are equal. Coerces values of different types before evaluating. Returns true if both values are equal and of the same type. Returns false otherwise. Returns true if values are not equal. Coerces values of different types before evaluating Returns true if both values are not equal or not of the same type. Returns false otherwise. Returns true if the left value is less than the right value. Returns false otherwise. Returns true if the left value is less than or equal to the right value. Returns false otherwise. Returns true if the left value is greater than the right value. Returns false otherwise. Returns true if the left value is greater than or equal to the right value. Returns false otherwise.
=== != !==
< <=
> >=
Each if statement can also have a corresponding else statement, where the enclosed code is executed when the if statement evaluates to false, like this:
if ( variable == true) { // code to execute when this statement is true } else { // code to execute when this statement is false }
When you want to check a value versus a number of values, a switch statement is useful. In a switch statement, you provide a conditional statement or variable and cases to match the statements outcome or variables value, like this:
switch ( variable ){ case 1: // code to execute when variable equals 1 break; case 2: // code to execute when variable equals 2 break; default: // code to execute when variable equals something other than the cases break; }
Conditional Statements
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
11
JavaScript Basics
Note that each case ends with a break statement and that the default case, if provided, is called if none of the cases are matched.
A variation on the while loop is the do-while loop, which executes its code at least once before checking its conditional statement, like this:
do { // code to execute while variable is true } while ( variable == true );
The final loop type is the for loop. It uses three statements: an initialization statement, a conditional statement, and a iterative statement, called every time the loop completes. The most common pattern of use calls for the first statement to initialize a counter variable, the second to check the counters values, and the third to increment it, like this:
for ( var i = 0; i < variable; ++i ) { // code to execute within this loop }
Functions
JavaScript allows you to wrap code into a function that you can reuse anywhere. To create a function, use the function keyword, provide a name, and include a list of any variables you want to pass in:
function myFunction ( variableOne, variableTwo ) { // code to execute within this function }
Functions can be declared and used anywhere within a block of JavaScript code. Variables that contain primitive values are passed into a function by value, whereas objects are passed in by reference. Functions can optionally return a value using the return keyword:
function myFunction ( variableOne ) { // code to execute within this function return variableOne; }
12
JavaScript Basics
Functions implicitly return undefined if you dont supply a return value. Functions can also be assigned to a variable, like this:
var myFunctionVariable = function ( variable ) { // code to execute within this function }
Once a function is defined within a variable, it can be passed as an argument into any other function. Within the receiving function, you can call that function by placing parentheses after the variables name and optionally including arguments, like this:
function myFunction ( myFunctionVariable ) { myFunctionVariable( "aString" ); }
These two statements are equivalent in every way. Overuse of this syntax can make code less readable, but moderate use can sometimes significantly clean up what would otherwise be deeply nested control statements or unnecessary duplication of large amounts of code. For example, this syntax is often used for very simple conversion of numerical values to strings for output. The snippet below prints yes or no depending on whether the increment variable is set or not:
alert('Increment: '+(increment ? "yes" : "no"));
13
JavaScript Basics
When used to separate expressions, the comma separator instructs the interpreter to execute them in sequence. The result of the expression is the rightmost expression. This use of the comma is generally reserved for locations in your code where a semicolon has special meaning beyond separating statements. For example, you use a semicolon to separate the parts of a C-style for loop as shown in this snippet:
for ( var i = 0; i < variable; ++i ) { // code to execute within this loop }
If you need to increment multiple values at each stage of the loop, you could make your code harder to read by indexing one value off of the other (maybe) or by placing additional assignment statements prior to the start of the loop and as the last statement in the loop. However, this practice tends to make it harder to spot the loop iterator variables and thus makes the code harder to follow. Instead, you should write the for loop as shown in this snippet, which displays an alert with five characters each, taken from different places in two strings:
var string = "The quick brown fox jumped over the lazy dog."; var stringb = "This is a test. This is only a test."; for (var i=0, j=4; i<5; i++, j++) { alert('string['+i+'] = '+string[i]); alert('stringb['+j+'] = '+stringb[j]); }
Note: The comma separator behaves subtly differently in the first part of a for statement. This code creates two local variables, i and j. If you must mix local and global variables, you must declare the local variables outside the loop and omit the var keyword entirely. Within the middle part of a for loop, the comma separator is nearly useless except for code obfuscation purposes. The rightmost comparison statement in such a list is used for comparison purposes.
14
Object-Oriented JavaScript
JavaScript code doesnt necessarily follow object-oriented design patterns. However, the JavaScript language provides support for common object-oriented conventions, like constructors and inheritance, and benefits from using these principles by being easier to maintain and reuse. Read this chapter if youre already familiar with object-oriented programming and want to see how to create object-oriented code with JavaScript.
Declaring an Object
JavaScript provides the ability to create objects that contain member variables and functions. To define a new object, first create a new function with the desired objects name, like this:
function myObject() { // constructor code goes here }
This function works as the objects constructor, setting up any variables needed by the object and handling any initializer values passed in as arguments. Within the constructor or any member function, persistent instance variables are created by adding them to the this keyword and providing the variable with a value, like this:
this.myVariable = 5;
To create a new member function, create a new anonymous function and assign it to the prototype property of the object. For example:
myObject.prototype.memberMethod = function() { // code to execute within this method }
Important: You must do this assignment outside the constructor. Assigning a member function to a prototype should not be confused with assigning an anonymous function to a member variable of a class, which only adds the function to a particular instance of the class, and is probably not what you want. After you have created the constructor for your object, you can create new object instances by using the new keyword followed by a call to the constructor function. For example:
var myObjectVariable = new myObject();
This particular constructor takes no parameters. However, you can also create more complex constructors for classes that allow you to specify commonly used or mandatory initial values. For example:
Declaring an Object
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
15
Object-Oriented JavaScript
function myObject(bar) { this.bar = bar; } myobj = new myObject(3); alert('myobj.bar is '+myobj.bar); // prints 3.
One common use of this technique is to create initializers that duplicate the contents of an existing object. This is described further in Copying an Object (page 18).
Freeing Objects
Just as you used the new operator to create an object, you should delete objects when you are finished with them, like this:
delete myObjectVariable;
The JavaScript runtime automatically garbage collects objects when their value is set to null. However, setting an object to null doesnt remove the variable that references the object from memory. Using delete ensures that this memory is reclaimed in addition to the memory used by the object itself. Important: Do not call delete if you are keeping references to the object in other variables. Calling delete on the object can cause a performance hit when it is accessed from those other variables.
Once you create an array, you reference elements using integer values inside of brackets, like this:
myArray[0] = "first value"; myArray[1] = 5;
The previous example shows that arrays, like variables, can hold data of any type. Generic objects and arrays can be used with associative properties, where strings replace the index number:
myObject["indexOne"] = "first value"; myObject["indexTwo"] = 5;
When you use a string as an index, you can use a period instead of brackets to access and assign data to that property:
16
Freeing Objects
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
Object-Oriented JavaScript
A variation of the for loop is available for iterating within the properties of an array or object. Called a for-in loop, it looks like this:
for ( var index in myArray ) { // code to execute within this loop }
It is essential that a getter function return a value because, when used, the returned value is used in an assignment operation like this:
var someVariable = myObject.myGetter;
Like a getter, a setter is defined on an object and provides a keyword and a function:
myObject.__defineSetter__( "mySetter", function(aValue) { this.myVariable = aValue; } );
Note that setter functions need to take an argument thats used in an assignment statement, like this:
myObject.mySetter = someVariable;
Inheritance
JavaScript allows an object to inherit from other objects via the prototype keyword. To inherit from another object, set your objects prototype equal to the prototype of the parent object, like this:
MyChildObject.prototype = MyParentObject.prototype;
17
Object-Oriented JavaScript
Note: Because the prototype keyword is undefined on instances of a class, this action must be performed on the constructor function names, not on variables containing instances of the class. This copies all of the parents functions and variables to your object. It does not, however, copy the default values stored in that object. If your purpose is to subclass an object completely, you must also execute code equivalent to the original constructor or construct an instance of the original type and copy its values as described in Copying an Object (page 18). Recent WebKit builds also include the ability to extend an existing DOM object via the prototype keyword. The advantage of this over inheritance is a smaller memory footprint and easier function overloading.
Copying an Object
In object-oriented programming, you often need to make a copy of an object. The most straightforward (but painful) way to do this is to methodically copy each value by hand. For example:
function myObjectCopy(oldobj) { // copy values one at a time this.bar = oldobj.bar; }
For very simple objects, explicit copying is fine. However, as objects grow larger, this technique can become unmanageable, particularly if additional variables are added to an object outside the constructor. Fortunately, because a JavaScript object is essentially an associative array, you can manipulate it just as you would an array. For example:
function myObjectCopy(oldobj) { // copy values with an iterator for (myvar in oldobj) { this[myvar] = oldobj[myvar]; } }
You should be aware, however, that assigning an object to a variable merely stores a reference to that object, not a copy of that object, and thus any objects stored within this object will not be copied by the above code. If you need to perform a deep copy of a structured tree of data, you should explicitly call a function to copy the nested objects. The easiest way to do this is to include a clone function in each class, test each variable to see if it contains an object with a clone function, and call that function if it exists. For example:
// Create an inner object with a variable x whose default // value is 3. function innerObj() { this.x = 3; } innerObj.prototype.clone = function() { var temp = new innerObj(); for (myvar in this) {
18
Copying an Object
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
Object-Oriented JavaScript
// this object does not contain any objects, so // use the lightweight copy code. temp[myvar] = this[myvar]; } return temp; } // Create an outer object with a variable y whose default // value is 77. function outerObj() { // The outer object contains an inner object. Allocate it here. this.inner = new innerObj(); this.y = 77; } outerObj.prototype.clone = function() { var temp = new outerObj(); for (myvar in this) { if (this[myvar].clone) { // This variable contains an object with a // clone operator. Call it to create a copy. temp[myvar] = this[myvar].clone(); } else { // This variable contains a scalar value, // a string value, or an object with no // clone function. Assign it directly. temp[myvar] = this[myvar]; } } return temp; } // Allocate an outer object and assign non-default values to variables in // both the outer and inner objects. outer = new outerObj; outer.inner.x = 4; outer.y = 16; // Clone the outer object (which, in turn, clones the inner object). newouter = outer.clone(); // Verify that both values were copied. alert('inner x is '+newouter.inner.x); // prints 4 alert('y is '+newouter.y); // prints 16
Copying an Object
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
19
Object-Oriented JavaScript
20
Copying an Object
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
There are a number of considerations that you should take into account when writing JavaScript code. Whether youre trying to tune your code for better performance or test it for compatibility, these best practices can help your code perform better and be more compatible. If youre looking for performance and testing tips for JavaScript coding, read this chapter.
Release initialization functions. Code thats called once and never used again can be deleted after its execution. For instance, deleting a windows onload handler function releases any memory associated with the function, like this:
var foo = function() { // code that makes this function work delete foo; } window.addEventListener('load', foo, false);
Use delete statements. Whenever you create an object using a new statement, pair it with a delete statement. This ensures that all of the memory associated with the object, including its property name, is available for garbage collection. The delete statement is discussed more in Freeing Objects (page 16). Test for an elements existence. Before using nonstandard elements, check for their existence like this:
if ( "innerHTML" in document.getElementById("someDiv") ) { // code that works with innerHTML }
Avoid evaluated statements. Using the eval function disables performance and memory optimizations in the JavaScript runtime. Consider using function variables, as discussed in Functions (page 12), in cases where you are passing in lines of code to another function, like setTimeout or setInterval.
21
Try multiple browsers. Its a good practice to try any web-based JavaScript code you write in many browsers. In addition to the version of Safari included with Mac OS X, consider trying your code on these browsers:
Try other toolkit versions. If youre using a third-party JavaScript toolkit such as Dojo, MochiKit, or prototype, try different versions of the library to see if your code uses the library properly. Try a verifier. JavaScript verifiers such as JSLint are useful at pinpointing compatibility problems and cases where the JavaScript code you wrote doesnt conform to standards.
Store data in the DOM tree, in cookies, or in HTML 5 client-side storage objects. The DOM tree, cookies, and HTML 5 client-side storage are good ways to conceal data to avoid conflicts with pages outside your domain. For performance reasons, you should generally limit use of these techniques to infrequently-used data, but any of these three techniques is better than using global variables. You can store data in the DOM tree like this:
/* Store data in the DOM tree */ var DOMobj = document.getElementById('page_globals'); DOMobj.setAttribute('foo', myIntegerVar); /* for large text content, consider using this: */ DOMobj.style.display = "none"; DOMobj.innerHTML = myStringVar; /* or this: */ DOMobj.style.display = "none"; if (typeof(DOMobj.innerText) != 'undefined') { # Internet Explorer workaround DOMobj.innerText = myStringVar; } else { # Per the W3C standard DOMobj.textContent = myStringVar; } /* Recall the data later */ var DOMobj = document.getElementById('page_globals'); var myIntegerVar = parseInt(DOMobj.getAttribute('foo')); /* Recall the large text data with this: */
22
var myStringVar = DOMobj.innerHTML; /* or this: */ var myStringVar; if (typeof(DOMobj.innerText) != 'undefined') { # Internet Explorer workaround myStringVar = DOMobj.innerText; } else { # Per the W3C standard myStringVar = DOMobj.textContent; }
Cross-Browser Compatibility Note: Internet Explorer does not support the textContent attribute on elements. For maximum compatibility, as shown in the previous example, you should support not only the W3C-standard textContent attribute, but also the IE-specific innerText attribute. You can also store data in cookies using the cookie attribute of the document object. The syntax for creating and expiring cookies is somewhat complex and easy to get wrong. Thus, this method is discouraged as a global variable alternative unless you already have code for creating and modifying cookies. To learn more about cookies in JavaScript, read http://www.quirksmode.org/js/cookies.html. Finally, for data that should persist across page loads, you can use HTML 5 client-side storage as described in Safari Client-Side Storage and Offline Applications Programming Guide. Safari 3.1 and later provides both SQL database and local key-value storage.
Use the var keyword. Any variable created without the var keyword is created at the global scope and is not garbage collected when the function returns (because it doesnt go out of scope), presenting the opportunity for a memory leak. Use a global array, global object, or namespace prefix. If you need global variables, use a global object that contains all of the global variables, like this:
var myBulletinBoardAppGlobals = { foo: "some value", bar: null };
These practices prevent your global variables from colliding with global DOM objects present in the runtime.
Use an initialization function. If you have code thats run immediately when the JavaScript is loaded, place it in a function thats called when the window loads, like this:
myBulletinBoardAppGlobals.foo = function() { // code that makes this function work delete myBulletinBoardAppGlobals.foo; } window.addEventListener('load', globals.foo, false);
23
JavaScript Toolkits
JavaScript toolkits enhance productivity but often carry a significant memory footprint that may slow down your JavaScript application. When using a toolkit, try to reduce the toolkits footprint to just the APIs that you use in your application. Also, if youre developing a Dashboard widget, try to avoid bundling the Apple-provided Apple Classes within your widget; instead, follow the instructions provided in Introduction to the Apple Classes.
24
JavaScript Toolkits
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
This table describes the changes to Apple JavaScript Coding Guidelines. Date 2009-05-05 2008-11-19 2008-10-15 Notes Added additional clarification on use of delete, storage objects. Removed a related documents link to a document that no longer exists. Added additional content about using the DOM tree as an alternative to global variables. Made several typographical fixes. Changed the title from "JavaScript Coding Guidelines for Mac OS X." Added some additional tips and techniques. Fixed typographical errors. New document that provides an overview of the JavaScript language, its object-oriented features, and coding best practices.
25
2009-05-05 | 2009 Apple Inc. All Rights Reserved.
26
2009-05-05 | 2009 Apple Inc. All Rights Reserved.