================================ Nodejs/Javascript Course Notes ================================ :author: Dave Kuhlman :contact: dkuhlman (at) davekuhlman (dot) org :address: http://www.davekuhlman.org :revision: 1.0b :date: |date| .. |date| date:: %B %d, %Y :Copyright: Copyright (c) 2015 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the MIT License http://www.opensource.org/licenses/mit-license.php. :Abstract: This document provides notes and an outline of an introductory course on programming in Nodejs and Javascript. .. sectnum:: .. contents:: Introduction ============== You can think of this document as an extended cheat sheet on programming in Nodejs and Javascript. Hopefully, it can also serve as notes for teaching a class on Javascript/Nodejs. Nodejs vs. Javascript -- While much of this document likely applies to and is true of Javascript coding, it is specifically focused on Nodejs. The example code snippets in this document were tested with Nodejs. Other sources --------------- There are other good sources of learning materials. Here are a few: - W3shools -- http://www.w3schools.com/ - Nodejs -- https://nodejs.org/documentation/ - MDN -- Mozilla Developer Network -- https://developer.mozilla.org/en-US/docs/Web/JavaScript - Microsoft -- https://msdn.microsoft.com/en-us/library/aa902517.aspx - Javascript Kit -- http://www.javascriptkit.com/jsref/index.shtml - Oracle -- http://docs.oracle.com/cd/E19957-01/816-6408-10/contents.htm - Devguru -- http://www.devguru.com/technologies/javascript/home Basics ======== The interactive prompt and interpreter ---------------------------------------- Start the Nodejs interactive interpreter with ``$ node``:: $ node > Exit from the interactive interpreter with a double Ctrl-C:: > (^C again to quit) > $ On Linux in particular, you may get a more friendly interactive prompt by using ``rlwrap``. Try this alias:: alias node="env NODE_NO_READLINE=1 rlwrap node" For more on this, see: https://nodejs.org/api/repl.html In many cases, the interactive interpreter will echo back the values you want to see automatically. In other cases, you can use the ``console.log`` method:: > console.log("hello") hello Simple, built-in data types ----------------------------- For some built-in data types there is both a primitive and an object type. This is true of String, Number, and Boolean. Convert from the primitive type to the object with the constructor for that object, and from the object to the primitive type with ``valueOf``:: > a = new Number(25) [Number: 25] > b = new String('abcd') [String: 'abcd'] > c = new Boolean(true) [Boolean: true] > a.valueOf() 25 > b.valueOf() 'abcd' > c.valueOf() true Notice that equality and identity tests do not work the same on the primitive type and the equivalent object type. For example:: > a = 'abc' 'abc' > b = 'abc' 'abc' > a == b true > a === b true > a = new String('abc') [String: 'abc'] > b = new String('abc') [String: 'abc'] > a == b false > a === b false Declaring variables --------------------- Use the ``let`` and ``var`` statements to declare variables. See section `let and var statements`_. Starting the interactive interpreter -------------------------------------- :: $ node > console.log('hello'); hello For simple debugging (when *not* in the debugger) and to write output to the terminal window, use ``console.log(...)`` in your script. For example:: console.log('my_variable: ' + my_variable); You may find it convenient to use this:: log = console.log log('my_variable: ' + my_variable); Run a script -------------- :: $ node my_script.js Or:: $ nodejs my_script.js Using the debugger -------------------- To run a script under the debugger, do:: $ node debug my_script.js You can also drop into the debugger at a specific location in your script by adding this line to your script:: debugger; Notes: - For help with using the debugger, type ``help`` at the debugger prompt. - To inspect variables etc, type ``repl`` at the debugger prompt. - For more help with the debugger, see: http://nodejs.org/api/debugger.html Help with objects ------------------- 1. To learn the type of an object, try:: Object.prototype.toString.call(my_object); 2. In the interactive shell, to learn what properties an object has, type the object, then a dot ("."), then press the key. Node attempts to do tab completion on attribute names. 3. When you want to inspect and learn more about an object, also consider using the following:: console.dir(obj); // print to console util.inspect(obj); // return a string var options = {showHidden: true}; console.dir(obj, options) util.inspect(obj, options) There are additional options. For more on this, see: https://nodejs.org/docs/v0.12.3/api/console.html#console_console_dir_obj_options Data types ============ Additional information about the standard, built-in data types can be found here: - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects - http://www.w3schools.com/js/default.asp Numbers -------- Create numbers using the standard literal representations:: > var a = 123 undefined > var b = 12.34 undefined > var c = 1.23e6 undefined > var d = 1.23e-3 undefined > a 123 > b 12.34 > c 1230000 > d 0.00123 Convert a number to a string with the ``toString`` method:: > d.toString() '0.00123' Convert a string to a number with the ``parseInt`` and ``parseFloat`` methods. Example:: > var a = '123', b = '4.56'; undefined > parseInt(a); 123 > parseFloat(b); 4.56 Strings -------- Use single or double quotes to create a string:: > var a = 'one two' undefined > var b = "three four" undefined > a 'one two' > b 'three four' Get the length of a string with the ``length`` property:: > a.length 7 Get the character at a position (index) in a string with the array index operator (``[n]``) or the ``charAt`` method:: > a[1] 'n' > a[2] 'e' > a.charAt(2) 'e' Other ``String`` properties and methods are described here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String String formatting ~~~~~~~~~~~~~~~~~~~ Formatting strings in JavaScript are a bit limited. However, for padding a string on the left, you could use:: > var s1 = '12' undefined > var s2 = day = ("0000" + s1).substr(-4,4) undefined > s2 '0012' The Node.js ``util`` module has a ``format`` method which has a limited amount of ``sprintf`` capabilities. Example:: > util.format('name: "%s" size: %d', 'redwood tree', 100) 'name: "redwood tree" size: 100' And, there are implementations of ``printf/sprintf`` for JavaScript. Go to https://www.npmjs.com/ and search for "printf" or "sprintf". Regular expressions ~~~~~~~~~~~~~~~~~~~~ Learn more about JavaScript regular expressions here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp A few examples:: > /bbcc/.test('aabbccdd') true > /bbcc/.test('aabbcdd') false > /^bbcc/.test('bbccdd') true > /^bbcc/.test('aabbccdd') false Arrays -------- Create an array with square brackets or with the ``Array`` constructor. Example:: > a = [11, 22, 'abc', 33] [ 11, 22, 'abc', 33 ] > b = new Array(4) [ , , , ] > c = new Array('abc', 11, 22) [ 'abc', 11, 22 ] > a [ 11, 22, 'abc', 33 ] > b [ , , , ] > c [ 'abc', 11, 22 ] There is a difference between producing an array with ``[ ... ]`` as opposed to ``new Array()``. They produce different kinds of objects, even though for most uses they act the same. The use of ``[ ... ]`` to create arrays is recommended for most use cases. Caution: When the first argument to the ``Array`` constructor is a number and the other arguments are not, Nodejs produces an exception:: > b = new Array(1, 2, 'abc' 3) SyntaxError: Unexpected number at Object.exports.createScript (vm.js:44:10) at REPLServer.defaultEval (repl.js:118:23) at bound (domain.js:254:14) at REPLServer.runBound [as eval] (domain.js:267:12) at REPLServer. (repl.js:280:12) at REPLServer.emit (events.js:107:17) at REPLServer.Interface._onLine (readline.js:206:10) at REPLServer.Interface._line (readline.js:535:8) at REPLServer.Interface._ttyWrite (readline.js:812:14) at ReadStream.onkeypress (readline.js:108:10) Access and change an element of an array with the square bracket indexing operator:: > a [ 11, 22, 'abc', 33 ] > a[1] 22 > a[1a[1] = 99 99 > a [ 11, 99, 'abc', 33 ] > Note that there are special array types for each of the following:: - Int8Array - Uint8Array - Uint8ClampedArray - Int16Array - Uint16Array - Int32Array - Uint32Array - Float32Array - Float64Array For more information on arrays, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects Statements ============ let and var statements ------------------------ ``let`` and ``var`` are used to declare variables. **However**, ``let`` is experimental and may not be supported by your version of Javascript. In particular, I get an error when I use ``let`` with Nodejs. And, when I run ``jshint`` on my code containing a ``let`` statement, I see:: test01.js: line 32, col 3, 'let' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz). The scope of a variable declared with ``let`` is different from one declared with ``var``: - A variable declared with ``let`` has the enclosing block as its scope. - A variable declared with ``var`` has a function as its scope, if it is used in a function; the variable is global, if it is declared outside of any function. You can also use ``var`` to initialize the value of a variable. Examples:: var a; var b = 123; var c = new Map(); Assignment ------------ The standard assignment operators:: var a = 0 var n = 2 a += n a -= n a *= n a /= n There are also expressions:: x++ // return the value x, then increment it by 1 ++x // increment x by 1, then return the new value if statement -------------- The syntax for the ``if`` statement is as follows:: if (condition1) statement1 else if (condition2) statement2 else if (condition3) statement3 ... else statementN Notes: - The ``else if`` clauses and the ``else`` clause are optional. - The conditions must be expressions. The expression is evaluated to determine truth or falsity. The following explains which values count as true and false for the purposes of the ``if`` statement: - undefined -- false - null -- false - Boolean -- The result equals the input argument (no conversion). - Number -- The result is false if the argument is +0, −0, or NaN; otherwise the result is true. - String -- The result is false if the argument is the empty String (its length is zero); otherwise the result is true. - Object -- true. Note to Python programmers -- An empty ``Array`` evaluates to true. This differs from Python. for statement --------------- The standard form (inherited from C, C++, and Java):: var a = [11, 22, 33]; var idx; for (idx = 0; idx < a.length; idx++) { console.log("a[" + idx + "]: " + a[idx]); } When we run the above, we'll see:: a[0]: 11 a[1]: 22 a[2]: 33 Iterate over the **enumerable** properties of an object in **arbitrary** order with ``for .. in``:: var value; for (value in a) { console.log("value: " + value); } When we run the above, we see this:: value: 0 value: 1 value: 2 Notes: - The values that we iterate over are the *indexes* of the property, and *not* the elements contained by the array. - The order in which that values are received is undefined. To iterate over the items in an Array, you can use its ``forEach`` method. Example:: var data = [111, 222, 333]; data.forEach(function (item) { console.log('item: ' + item); }); while statement ----------------- The template/syntax for the ``while`` statement is:: while (condition) { statement } Where condition is an expression as in the ``i`` statement. Here is an example:: function test(container) { var idx = 0; while (idx < container.length) { console.log('item: ' + container[idx]); idx += 1; } } var container = [11, 22, 33, ]; test(container); And, running the above produces this:: $ node test03.js item: 11 item: 22 item: 33 Notes: - The use of the ``while`` statement above is effectively the same as the following ``for`` statement:: function test(container) { for (var idx = 0; idx < container.length; idx++) { console.log('item: ' + container[idx]); } } break statement ----------------- The break statement terminates a loop or switch. Here are several examples:: #!/usr/bin/env node function test1(max, divisor) { var idx; for (idx = max; idx > 0; idx--) { if ((idx % divisor) === 0) { break; } console.log('idx: ' + idx + ' divisor: ' + divisor); } } function test_break(code) { switch (code) { case 'a01': console.log('process a01'); break; case 'b01': case 'b02': console.log('process b01 and b02'); break; default: console.log('we do not know about code: ' + code); } } function main() { test1(20, 15); console.log('-----------------------'); test_break('a01'); test_break('b01'); test_break('b02'); test_break('x00'); } main(); Notes: - In the first case handled by our ``switch`` statement above, the break is necessary to prevent execution from flowing from one case into the next. - However, notice how we handle both cases 'b01' and 'b02' with the same code. In effect, when case 'b01' is matched, it flows into the same code as that used for case 'b02'. You can also use ``break`` with a label. See this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration continue statement -------------------- The ``continue`` statement causes control to go immediately to the top of the loop. You can also use the ``continue`` statement with a label. See here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration require statement ------------------- ``require`` is not actually a statement; it's a function. But, this is a good place to discuss it. Use ``require`` in your code as follows:: var utils = require('my_module'); var tools = require('./my_other_module'); Notes: - ``Node`` looks for modules in a set of locations/directories that are in the module search path. You can add to this set of locations with the ``NODE_PATH`` environment variable. Type ``node --help`` at the command line for information. Example (on Linux):: $ export $NODE_PATH:/my/other/location - You can also specify a path to the module in the argument to ``require``. For example, the example above will load ``my_other_module`` from the current working directory even when that directory is not in the module search path. The objects that are made available through ``require`` must be declared as exported. Example:: module.exports = { fn1: function () { console.log('from fn1'); }, fn2: function () { console.log('from fn2'); }, maximum_val: 999, label_val: 'a descriptive string', }; var fn3 = function () { console.log('from fn3'); }; Then we can do the following:: > var test_req = require('./test_req'); undefined > test_req.fn1(); from fn1 undefined > test_req.fn2(); from fn2 undefined > test_req.maximum_val; 999 > test_req.label_val; 'a descriptive string' But, we **cannot** do this:: > test_req.fn3(); TypeError: undefined is not a function o o o Functions =========== Two ways to define functions:: function func1(arg1, arg2) { statements } var func2 = function(arg1, arg2) { statements } The second form is important because you will often need to pass a call back function as an argument to a function. So, you can do:: var my_callback = function(x, y) { console.log(x + y); }; a_function(my_callback); or, much more commonly:: a_function(function(x, y) { console.log(x + y); }); Here is a slightly more complete version of the above that might help to make this more clear. Note that we can also capture the value returned by the function to which we pass the call back function:: function a_function(fn) { console.log('before'); // call the call back function. It requires 2 arguments. var result = fn(4, 5); console.log('after'); return result; } function test() { var result = a_function(function(x, y) { var value = x + y; console.log(value); return value; }); console.log('result: ' + result); } test(); Which produces the following when run:: $ node test04.js before 9 after result: 9 Notes: - A function's arguments are local to the function. They override global variables. - A function can access global variables from within its block, as long as those variables are not arguments nor are declared local (with ``var``) within the function's block. Here is an example of a function that uses both local and global variables:: var globalvar1 = 25; var z = 20; function test(x, y) { var z = 10; console.log('x: ' + x); console.log('y: ' + y); console.log('z: ' + z); console.log('globalvar1: ' + globalvar1); } test(11, 22); And, when we run the above, we see:: $ node test01.js x: 11 y: 22 z: 10 globalvar1: 25 Javascript functions support recursion, that is, a function can call itself. In the following example, the ``walktree`` function recursively calls itself:: function construct_tree() { var tree = {}; var node1 = {}; var node2 = {}; node1.name = 'node1'; node2.name = 'node2'; tree.name = 'tree_root'; node1.children = []; node2.children = []; tree.children = [node1, node2]; return tree; } function walktree(node) { console.log(node.name); for (var idx = 0; idx < node.children.length; idx++) { walktree(node.children[idx]); } } function test() { var tree = construct_tree(); walktree(tree); } test(); When we run the above, we should see:: $ node test02.js tree_root node1 node2 A function can implement a *closure*. A nested function can capture values from the scope that encloses it. Example:: #!/usr/bin/env node function makeAdder(addition) { var max = addition * 5; function adder(x) { x += addition; if (x > max) { x = max; } return x; } return adder; } function main() { var f1 = makeAdder(3); console.log(f1(11)); console.log(f1(18)); } main(); Additional notes on functions arguments: - If you pass fewer arguments to a function than the number of parameters defined by the function, then the parameters that are omitted will have the value ``undefined``. - If you pass additional arguments to a function over and above the number of parameters defined by the function, the additional argument values are normally ignored. *However*, you can access those additional values through the ``arguments`` variable. ``arguments`` is array-like: (1) you can index into it (for example: ``arguments[3]`` returns the fourth argument, if there is one; (2) the number of arguments is available through ``arguments.length``. Objects and object oriented programming ========================================= Think of an object as being a little like a dictionary. It has named attributes that can be "looked up" within the object. We can create attributes in an object and we can assign values to those attributes in the same way that we do to a dictionary. Maybe an object is *a lot* like a dictionary. You can create basic objects in a variety of ways. Here are several:: > var a = {} undefined > var b = {size: 25, color: 'red'} undefined > a {} > b { size: 25, color: 'red' } > var c = new Object() undefined > c {} > c.width = 12 12 > c { width: 12 } A simple object definition ---------------------------- Here is an example that defines an object type and then creates an instance of that object:: var TestObj1 = function (name) { this.name = name; this.show = function (label) { console.log(label + ': ' + this.name); }; }; function test() { var obj1 = new TestObj1('dave'); obj1.show('the name'); } test(); If you study the above simple example, you will notice that an object is just a function that assigns values to its attributes. The function accomplishes that by assigning values to ``this.attribute_name``. When we use the keyword ``new`` to create an instance, the JavaScript interpreter will create a new object and bind that newly created object as the value of the property ``this``. An object definition with inheritance --------------------------------------- Here is an example that does the JavaScript version of inheritance:: #!/usr/bin/env node var Graph = function () { this.vertices = []; this.edges = []; }; Graph.prototype = { vertices: [], edges: [], addVertex: function (vertex) { this.vertices.push(vertex); }, addEdge: function (edge) { this.edges.push(edge); }, show: function () { console.log('g:vertices: ' + this.vertices + ' edges: ' + this.edges); }, }; var ColorGraph = function (color, vertex, edge) { Graph.call(this, vertex, edge); // Alternatively, the following also works. //Graph.apply(this, [vertex, edge]); this.color = color; }; ColorGraph.prototype = Object.create(Graph.prototype); ColorGraph.prototype.color = 'no-color'; ColorGraph.prototype.show = function () { console.log('gc:color: ' + color); Graph.prototype.show.call(this); // But the following line also seems to work. //Graph.prototype.show(); }; function test() { var g = new Graph(); g.addVertex(4); g.addEdge(5); g.addEdge(15); g.show(); var cg = new ColorGraph('red', 20, 30); console.log('------------------------'); cg.show(); cg.addEdge(40); console.log('------------------------'); cg.show(); } test(); When we run this code, we see:: $ node test05.js g:vertices: 1,4 edges: 2,5,15 ------------------------ gc:color: red g:vertices: 20 edges: 30 ------------------------ gc:color: red g:vertices: 20,0,4,8,12,16 edges: 30,0,3,6,9,12 Notes: - Notice how we create the methods in separate objects and assign them to the ``.prototype`` of the class (actually the constructor function). We do this so that when we create instances of this class, we will not create separate and multiple copies of the methods (functions) for each instance. - We use ``Object.create(Graph.prototype)`` to create the prototype for our derived class because it effectively "copies" the prototype of the base class. Here is another example with more classes and another level of inheritance. This example implements the following hierarchy of object types:: Employee AdminEmployee ITEmployee SysAdminEmployee ProgrammerEmployee Here is the code:: #!/usr/bin/env node var Employee = function (name, dept) { this.name = name; this.dept = dept; }; Employee.prototype = { name: '', dept: '', }; Employee.prototype.show = function () { console.log('name: ' + this.name); console.log('dept: ' + this.dept); }; var AdminEmployee = function (name, dept, responsibility) { Employee.call(this, name, dept); this.responsibility = responsibility; }; AdminEmployee.prototype = Object.create(Employee.prototype); AdminEmployee.prototype.responsibility = null; AdminEmployee.prototype.show = function () { Employee.prototype.show.call(this); console.log('responsibility: ' + this.responsibility); }; var ITEmployee = function (name, dept, specialty) { Employee.call(this, name, dept); this.specialty = specialty; }; ITEmployee.prototype = Object.create(Employee.prototype); ITEmployee.prototype.specialty = ''; ITEmployee.prototype.show = function () { Employee.prototype.show.call(this); console.log('specialty: ' + this.specialty); }; var SysAdminEmployee = function (name, dept, specialty, system) { ITEmployee.call(this, name, dept, specialty); this.system = system; }; SysAdminEmployee.prototype = Object.create(ITEmployee.prototype); SysAdminEmployee.prototype.system = ''; SysAdminEmployee.prototype.show = function () { ITEmployee.prototype.show.call(this); console.log('system: ' + this.system); }; var ProgrammerEmployee = function (name, dept, specialty, language) { ITEmployee.call(this, name, dept, specialty); this.language = language; }; ProgrammerEmployee.prototype = Object.create(ITEmployee.prototype); ProgrammerEmployee.prototype.show = function () { ProgrammerEmployee.prototype.language = 'none'; ITEmployee.prototype.show.call(this); console.log('language: ' + this.language); }; var Roster = function () { this.employees = []; }; Roster.prototype = { add: function (empl) { this.employees.push(empl); }, show: function () { var idx; for (idx = 0; idx < this.employees.length; idx++) { console.log('-------------- Employee record --------------'); this.employees[idx].show(); } }, }; function test() { var employees = new Roster(); var empl; empl = new Employee('Abel', 'Admin'); employees.add(empl); empl = new AdminEmployee('Albert', 'Communications', 'payroll'); employees.add(empl); empl = new ITEmployee('Betty', 'Communications', 'network'); employees.add(empl); empl = new SysAdminEmployee( 'Craig', 'CalifOffice', 'repairs', 'Linux'); employees.add(empl); empl = new ProgrammerEmployee( 'Daniel', 'CustomerService', 'office apps', 'Python'); employees.add(empl); employees.show(); } function main() { test(); } main(); When we run the above, we see:: $ node test09.js name: Abel dept: Admin ------------------------------------- name: Albert dept: Communications responsibility: payroll ------------------------------------- name: Betty dept: Communications specialty: network ------------------------------------- name: Craig dept: CalifOffice specialty: repairs system: Linux ------------------------------------- name: Daniel dept: CustomerService specialty: office apps language: Python Notes: - In this example, we only used single inheritance, because JavaScript does not support multiple inheritance, and because single inheritance is less confusing than multiple inheritance. A few common tasks ==================== Reading and writing files --------------------------- For synchronous operations use ``fs.readFileSync`` and ``fs.writeFileSync``. Example:: var fs = require('fs'); var data = fs.readFileSync('source.txt'); fs.writeFileSync('sink.txt', data.toString().toUpperCase()); For asynchronous operations, use ``fs.readFile`` and ``fs.writeFile``. Example:: var fs = require('fs'); function test(infilename, outfilename) { fs.readFile(infilename, function (err, data) { if (err) { throw err; } data = data.toString().toUpperCase(); fs.writeFile(outfilename, data, function (err) { if (err) { throw err; } }); }); } function main() { var args = process.argv, infilename = args[2], outfilename = args[3]; test(infilename, outfilename); } main(); We can also use readable and writeable streams. Streams are an abstraction. So, there are a number of different concrete objects that implement the same interface and, therefore, can be used in similar way. Example:: var fs = require('fs'); var t2map = require('through2-map'); function test(infilename, outfilename) { var instream = fs.createReadStream(infilename), outstream = fs.createWriteStream(outfilename); instream.pipe(t2map(function (data) { return data.toString().toUpperCase(); })).pipe(outstream); } function main() { var args = process.argv, infilename = args[2], outfilename = args[3]; test(infilename, outfilename); } main(); Notes: - Note the use of ``through2-map`` in this example. When using streams, the module ``through2-map`` may be useful and convenient. You can install it using ``npm``. File names and directories ---------------------------- The ``path`` module has methods that are useful. Examples:: > var path = require('path') undefined > path.parse('/path/to/my/file.txt') { root: '/', dir: '/path/to/my', base: 'file.txt', ext: '.txt', name: 'file' } > path.extname('/path/to/my/file.txt') '.txt' > path.basename('/path/to/my/file.txt') 'file.txt' > path.sep '/' > path.join(path.sep, 'path', 'to', 'my', 'file.txt') '/path/to/my/file.txt' For more information see: https://nodejs.org/api/path.html Process a list of files in a directory with something like the following:: > var fs = require('fs') > fs.readdir('.', function (err, filenames) {filenames.forEach(function (name) {console.log(name); })}) For more information see: https://nodejs.org/api/fs.html JSON -- JavaScript Object Notation ------------------------------------- You can convert to and from JSON. Example:: > var a = {aa: 11, bb: [22, 33, 44], cc: 'abcd'} undefined > var b = JSON.stringify(a) undefined > b '{"aa":11,"bb":[22,33,44],"cc":"abcd"}' > JSON.parse(b) { aa: 11, bb: [ 22, 33, 44 ], cc: 'abcd' } Writing servers/applications with NodeJS ========================================== You can find help with learning how to implement network servers in the tutorials located here: http://nodeschool.io/ In particular, exercises that create network servers are at ``LearnYouNode``: https://github.com/workshopper/learnyounode. jQuery ======== ``jQuery`` is a JavaScript library intended to be used in Web pages. It gives us easy access to the DOM (document object model) and the (HTML) elements in it, enables us to modify those elements, and has a convenient model for responding to events from elements in that DOM. Here are several places where you can learn more about ``jQuery``: - http://api.jquery.com - http://www.w3schools.com/jquery/default. In this document, we are mostly interested in JavaScript itself, rather than creating Web pages. However, the development tools available within a browser (such as Firefox, Chromium, Opera, etc) can be very helpful. Those tools enable us (1) to step through code using and interactive debugger, (2) to inspect objects and values as we go, (3) to view output sent to the console with ``console.log(msg)``, and (4) to view the web page as it changes. So, here is a simple HTML template that you can use to test snippets of JavaScript code::

click me 1

Notes: - The script ``$(document).ready(fn)`` will be called when the document has been fully rendered. - The fragment ``$("p").click`` will be called when the user clicks on any ``

`` element in the page. - We catch the object that was clicked with the expression ``$(this)``. So, ``$(this).text()`` retrieves the text of that paragraph element. As you can see from this example, we can both query the element that was the target of the event (for example, with ``.text()``) and manipulate it (for example, change its text with ``.text(new_text)``. Related, useful technologies ============================== Here are a few worth thinking about: - jQuery -- http://jquery.com/ -- "jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript." Also see section `jQuery`_, in this document. The above list is nowhere near complete, and it will be even less complete next week. You can learn about additional JavaScript libraries, and maybe even find the one *you* need here: - https://en.wikipedia.org/wiki/List_of_JavaScript_libraries - https://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks - https://en.wikipedia.org/wiki/Ajax_framework .. vim:ft=rst: