2.3 - JavaScript Language

The JavaScript language was developed alongside HTML to enable the manipulation of web pages after they get downloaded to a client. JavaScript runs silently in the background to process user events and dynamically modify a web page. If JavaScript code has problems or produces errors it is designed to fail silently so that the average user has no clue something went wrong.

JavaScript and Java have nothing in common. They are totally different languages designed for different purposes.

The major distinctions of JavaScript are:

The syntax of JavaScript is closely related to the C programming language. This JavaScript Quick Reference Card contains most of the language syntax you should need for doing WebGL programming. Please take some time to study this reference card. The legend for the color coding on the card is at the end of the document.

Context

Everything in JavaScript is an object. Objects are instances of a class. Objects contain data and have functions that can manipulate that data. Functions of an object have a context in which they work. Their context is defined by the data they can access and the current state of that data. The idea that every function in JavaScript is executed from a “context” is central to understanding how JavaScript works. Let’s walk through some examples. If a function is called because a user clicked on a button, then the function is executed in the context of the HTML button element. If a function is called from code loaded from a JavaScript file, then it is executed in the context of the global address space. If a function is called from inside an object, then its context is that object. There is a keyword in JavaScript that always references the context from which a function is called. The keyword is called this. The keyword this in Java, C++ and other object-oriented programming languages means something totally different. Do not get them confused.

This is so important that it need to be repeated. In JavaScript, the keyword this references the context from which a function is called. You will see how to use this in powerful ways as you get more experience with JavaScript.

Classes and Objects in JavaScript

Defining classes and objects in JavaScript can be confusing, especially if you expect JavaScript to behave like other languages you know. Since we will be developing all of our WebGL code as objects, we need to specifically discuss how JavaScript implements object oriented programming.

A class is defined in the same way a normal function is defined. The function definition is basically the constructor of the class. If you call the function in the normal way, it will act like a normal function. If you use the new command in front of the function call you are creating an object of that class. Hopefully an example will make this clearer. Here is a simple function definition.

function Example(a,b,c) {
  // Do some stuff
}

You can call this function in the normal way and it will perform some processing, like this:

Example(alpha, beta, gamma);

Or, you can create an object from the definition like this:

var my_object = new Example(alpha, beta, gamma);

When you create an object, any data defined inside the function is retained inside the object and the data can be accessed and modified at a later time.

Public and Private Data in a Class

By default, variables declared inside a function that defines a class are private. In the following example, all of the data and functions are private.

function ExampleClass(a,b,c) {
  // Private class variables
  var s,t,u;

  // Private functions
  function _innerOne() {
    // can manipulate s, t, and u
  }

  function _innerTwo() {
    // can manipulate s, t, and u
  }
}

This is an example of an immutable object in JavaScript. An object created from this class can’t be modified because it has no public data or public functions. To make variables or functions public you add them to the object as properties. Properties of an object are accessed using dotted notation, as in object.property. Since JavaScript is an interpreted and dynamic language, new properties can be added to an object at any time. This can cause hard to find errors if you misspell property names. Instead of manipulating an existing property, a misspelled property name will add an new unwanted property to an object. So watch your spelling!

When an object is created by calling the new command, the this keyword is a reference to the new object (just like in Java and C++). Therefore, you can prefix any variable or function with the this keyword to make them public. Below is an example class definition that includes both private and public data and functions.

function ExampleClass(a,b,c) {
  // Private class variables
  var s,t,u;

  // Public class variables (actually properties of the object)
  this.m = value1;
  this.n = value2;

  // Public function
  this.doSomething = function () {
    // can manipulate all private and public data
    // can call all private and public functions
  }

  // Private function
  function _innerOne() {
    // can manipulate all private and public data
    // can call all private and public functions
  }
}

If we make an object from this example class like this:

var my_object = new ExampleClass(alpha, beta, gamma);

Then the following statements are valid because they are accessing the public members of the object.

my_object.doSomething();
my_object.m = 5;

However, the following statements are invalid because they are attempting to use the private members of the object.

my_object._innerOne();  // would cause an error
my_object.s = 5;        // would cause an error

But wait! The above example has a major flaw. The value of the keyword this changes with context. When the object is actually used, the keyword this will take on various other values besides a reference to the object. This will cause the code to fail. The solution is to not use the keyword this for accessing public members. We will use this in the constructor to get a reference to the object, not use it in the member functions.

The example below shows how this works. When the constructor is executed the keyword this will be a reference to the new object because of the new command context. It is a relatively standard practice to use a private variable called self to store a reference to the new object inside the object itself. Then the local private variable self is used throughout the rest of the class definition.

function ExampleClass(a,b,c) {

  var self = this; // store a local reference to the new object

  // Private class variables
  var s,t,u;

  // Public class variables (actually properties of the object)
  self.m = value1;
  self.n = value2;

  // Public function
  self.doSomething = function () {
    // can manipulate all private and public data using self.property
    // can call all private and public functions using self.property
  }

  function _innerOne() {
    // can manipulate all private and public data using self.property
    // can call all private and public functions using self.property
  }
}

You are encouraged to re-read the above description. Often the second reading makes more sense.

Some Examples

In the WebGL demo code below there are two examples of JavaScript class definitions. Do not attempt to understand the functionality of the code at this time, but rather exam the structure of the class definitions. As you study the examples, please note that:

  • All code that is not inside a member function composes the constructor of the class. This code executes once when objects of the class are created.
  • Using strict mode, which requires that all variables be defined before they are used, means that the constructor code is sometimes not contiguous.
  • Notice how the variable self is used to define and access the public members of an object.
  • The important thing to read in these examples is the comments!
Show: Code Canvas Run Info
x
 
1
/**
2
 * learn_webgl_render_01.js, By Wayne Brown, Fall 2015
3
 *
4
 * Given
5
 *   - a model definition as defined in learn_webgl_model_01.js, and
6
 *   - specific shader programs: vShader01.vert, fShader01.frag
7
 * Perform the following tasks:
8
 *   1) Build appropriate Vertex Object Buffers (VOB's)
9
 *   2) Create GPU VOB's for the data and copy the data into the buffers.
10
 *   3) Render the VOB's
11
 */
12
13
/**
14
 * The MIT License (MIT)
15
 *
16
 * Copyright (c) 2015 C. Wayne Brown
17
 *
18
 * Permission is hereby granted, free of charge, to any person obtaining a copy
19
 * of this software and associated documentation files (the "Software"), to deal
20
 * in the Software without restriction, including without limitation the rights
21
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22
 * copies of the Software, and to permit persons to whom the Software is
23
 * furnished to do so, subject to the following conditions:
24
 *
25
 * The above copyright notice and this permission notice shall be included in all
26
 * copies or substantial portions of the Software.
27
28
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34
 * SOFTWARE.
35
 */
36
37
"use strict";
38
39
// Global definitions used in this code:
40
//var Float32Array, Uint16Array, parseInt, parseFloat, console;
41
42
//-------------------------------------------------------------------------
43
// Build, create, copy and render 3D objects specific to a particular
44
// model definition and particular WebGL shaders.
45
//-------------------------------------------------------------------------
46
var Scene_object_example_render = function (learn, vshaders_dictionary,
47
                                fshaders_dictionary, models) {
48
49
  var self = this; // Store a local reference to the new object.
50
51
  //-----------------------------------------------------------------------
52
  // Public function to render the scene.
53
  self.render = function () {
54
    var j, model_names;
55
56
    // Clear the entire canvas window background with the clear color
57
    // out.display_info("Clearing the screen");
58
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
59
60
    // Build individual transforms
61
    matrix.setIdentity(transform);
62
    matrix.rotate(rotate_x_matrix, self.angle_x, 1, 0, 0);
63
    matrix.rotate(rotate_y_matrix, self.angle_y, 0, 1, 0);
64
65
    // Combine the transforms into a single transformation
66
    matrix.multiplySeries(transform, transform, rotate_x_matrix, rotate_y_matrix);
67
68
    // Draw each model
69
    model_names = Object.keys(model_VOBs);
70
    for (j = 0; j < model_names.length; j += 1) {
71
      model_VOBs[model_names[j]].render(gl, transform_location, transform);
72
    }
73
  };
74
75
  //-----------------------------------------------------------------------
76
  // Public function to delete and reclaim all rendering objects.
77
  self.delete = function () {
78
    var j, model_names;
79
80
    // Clean up shader programs
81
    gl.deleteShader(program.vShader);
82
    gl.deleteShader(program.fShader);
83
    gl.deleteProgram(program);
84
85
    // Delete each model's VOB
86
    model_names = Object.keys(model_VOBs);
87
    for (j = 0; j < model_names.length; j += 1) {
88
      model_VOBs[model_names[j]].delete(gl);
89
    }
90
91
    // Remove all event handlers
92
    var id = '#' + canvas_id;
93
    $( id ).unbind( "mousedown", events.mouse_drag_started );
94
    $( id ).unbind( "mouseup", events.mouse_drag_ended );
95
    $( id ).unbind( "mousemove", events.mouse_dragged );
96
    events.removeAllEventHandlers();
97
98
    // Disable any animation
99
    self.animate_active = false;
100
  };
101
102
  //-----------------------------------------------------------------------
103
  // Object constructor. One-time initialization of the scene.
104
105
  // Private variables
106
  var canvas_id = learn.canvas_id;
107
  var out = learn.out;
108
109
  var gl = null;
110
  var program = null;
111
  var model_VOBs = {};
112
113
  var matrix = new Learn_webgl_matrix();
114
  var transform = matrix.create();
115
  var transform_location = 0;
116
  var rotate_x_matrix = matrix.create();
117
  var rotate_y_matrix = matrix.create();
118
119
  // Public variables that will be changed by event handlers.
120
  self.canvas = null;
121
  self.angle_x = 0.0;
122
  self.angle_y = 0.0;
123
  self.animate_active = true;
124
125
  // Get the rendering context for the canvas
126
  self.canvas = learn.getCanvas(canvas_id);
127
  if (self.canvas) {
128
    gl = learn.getWebglContext(self.canvas);
129
  }
130
  if (!gl) {
131
    return null;
132
  }
133
134
  // Set up the rendering program and set the state of webgl
135
  program = learn.createProgram(gl, vshaders_dictionary["shader05"], fshaders_dictionary["shader05"]);
136
  gl.useProgram(program);
137
138
  // Enable hidden-surface removal
139
  gl.enable(gl.DEPTH_TEST);
140
141
  // Remember the location of the transformation variable in the shader program
142
  transform_location = gl.getUniformLocation(program, "u_Transform");
143
144
  // Create Vertex Object Buffers for the models
145
  var j, key_list;
146
  key_list = Object.keys(models);
147
  for (j = 0; j < key_list.length; j += 1) {
148
    model_VOBs[key_list[j]] = new Learn_webgl_vob_model_01(gl, program, models[key_list[j]], out);
149
  }
150
151
  // Set up callbacks for user and timer events
152
  var events;
153
  events = new Learn_webgl_events(self, controls);
154
  events.animate();
155
156
  var id = '#' + canvas_id;
157
  $( id ).mousedown( events.mouse_drag_started );
158
  $( id ).mouseup( events.mouse_drag_ended );
159
  $( id ).mousemove( events.mouse_dragged );
160
};
161
162
163
184
 
1
/**
2
 * learn_webgl_events_01.js, By Wayne Brown, Fall 2015
3
 *
4
 * These event handlers can modify the characteristics of a scene.
5
 * These will be specific to a scene's models and the models' attributes.
6
 */
7
8
/**
9
 * The MIT License (MIT)
10
 *
11
 * Copyright (c) 2015 C. Wayne Brown
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
"use strict";
33
34
//-------------------------------------------------------------------------
35
function Learn_webgl_events(scene, control_id_list) {
36
37
  var self = this; // Store a local reference to the new object.
38
39
  //-----------------------------------------------------------------------
40
  self.mouse_drag_started = function (event) {
41
42
    //console.log("started mouse drag event x,y = " + event.clientX + " " + event.clientY + "  " + event.which);
43
    start_of_mouse_drag = event;
44
    event.preventDefault();
45
46
    if (animate_is_on) {
47
      scene.animate_active = false;
48
    }
49
  };
50
51
  //-----------------------------------------------------------------------
52
  self.mouse_drag_ended = function (event) {
53
54
    //console.log("ended mouse drag event x,y = " + event.clientX + " " + event.clientY + "  " + event.which);
55
    start_of_mouse_drag = null;
56
57
    event.preventDefault();
58
59
    if (animate_is_on) {
60
      scene.animate_active = true;
61
      self.animate();
62
    }
63
  };
64
65
  //-----------------------------------------------------------------------
66
  self.mouse_dragged = function (event) {
67
    var delta_x, delta_y;
68
69
    //console.log("drag event x,y = " + event.clientX + " " + event.clientY + "  " + event.which);
70
    if (start_of_mouse_drag) {
71
      delta_x = event.clientX - start_of_mouse_drag.clientX;
72
      delta_y = -(event.clientY - start_of_mouse_drag.clientY);
73
      //console.log("moved: " + delta_x + " " + delta_y);
74
75
      scene.angle_x += delta_y;
76
      scene.angle_y -= delta_x;
77
      scene.render();
78
79
      start_of_mouse_drag = event;
80
      event.preventDefault();
81
    }
82
  };
83
84
  //-----------------------------------------------------------------------
85
  self.key_event = function (event) {
86
    var bounds, keycode;
87
88
    bounds = canvas.getBoundingClientRect();
89
    console.log("bounds = " + bounds.left + " " + bounds.right + "  " + bounds.top + "  " + bounds.bottom);
90
    console.log("target = " + event.target);
91
    if (event.clientX >= bounds.left &&
92
      event.clientX <= bounds.right &&
93
      event.clientY >= bounds.top &&
94
      event.clientY <= bounds.bottom) {
95
      keycode = (event.keyCode ? event.keyCode : event.which);
96
      console.log(keycode + " keyboard event in canvas");
97
    }
98
99
    event.preventDefault();
100
101
    return false;
102
  };
103
104
  //------------------------------------------------------------------------------
105
  self.html_control_event = function (event) {
106
    var control;
107
108
    control = $(event.target);
109
    if (control) {
110
      switch (control.attr('id')) {
111
        case "my_pause":
112
          if (control.is(":checked"))  {
113
            animate_is_on = true;
114
            scene.animate_active = true;
115
            self.animate();
116
          } else {
117
            animate_is_on = false;
118
            scene.animate_active = false;
119
          }
120
      }
121
    }
122
  };
123
124
  //------------------------------------------------------------------------------
125
  self.animate = function () {
126
127
    var now, elapsed_time;
128
129
    if (scene.animate_active) {
130
131
      now = Date.now();
132
      elapsed_time = now - previous_time;
133
134
      if (elapsed_time >= frame_rate) {
135
        scene.angle_x -= 0.5;
136
        scene.angle_y += 1;
137
        scene.render();
138
        previous_time = now;
139
      }
140
141
      requestAnimationFrame(self.animate);
142
    }
143
  };
144
145
  //------------------------------------------------------------------------------
146
  self.removeAllEventHandlers = function () {
147
    var j;
148
    for (j = 0; j < control_id_list.length; j += 1) {
149
      var control = $('#' + control_id_list);
150
      if (control) {
151
        control.unbind("click", self.html_control_event);
152
      }
153
    }
154
  };
155
156
  //------------------------------------------------------------------------------
157
  // Constructor code for the class.
158
159
  // Private variables
160
  var out = scene.out;    // Debugging and output goes here.
161
  var canvas = scene.canvas;
162
163
  // Remember the current state of events
164
  var start_of_mouse_drag = null;
165
  var previous_time = Date.now();
166
  var animate_is_on = scene.animate_active;
167
168
  // Control the rate at which animations refresh
169
  var frame_rate = 16; // 16 milliseconds = 1/60 sec
170
171
  // Add an onclick callback to each HTML control
172
  var j;
173
  for (j = 0; j < control_id_list.length; j += 1) {
174
    var control = $('#' + control_id_list);
175
    if (control) {
176
      control.click( self.html_control_event );
177
    }
178
  }
179
}
180
181
182
183
184
Open this webgl program in a new tab or window

Coding Standard

Before leaving this discussion of JavaScript, please review the coding standard we will be using.

  • Always include "use strict"; to force the declaration of variables.

  • Avoid global variables whenever possible.

  • Use JSLint to check for errors. (The Pycharm IDE will do this for you.)

  • Use two-space indentation.

  • Use shorthand for conditional statements where appropriate: var results = (test === 5) ? alert(1) : alert(2);

  • The closing brace should be on the same indent as the original statement:

    function func() {
      return {
        "name": "Batman"
      };
    }
    
  • Naming conventions

    • Constructors start with a capital letter.

    • Methods/functions start with a small letter.

    • Methods/functions should use camel case. thisIsAnExample

    • Variables should always use an underscore between words. this_is_an_example

    • When appropriate, include the variable type in the name. value_list

    • Element ID’s and class names should always use an underscore between words.

    • Private methods should use a leading underscore to separate them from public methods.

    • Abbreviations should not be used in names.

    • Plurals should not be used when assigning names.

    • Comments should be used within reason.

    • Use YUIDoc to document functions.

      /**
       * Reverse a string
       *
       * @param  {String} input_string String to reverse
       * @return {String} The reversed string
       */
      function reverseString(input_string) {
        // ...
        return output_string;
      };
      

Glossary

JavaScript
JavaScript is a high-level, dynamic, untyped, and interpreted programming language that is used to manipulate the HTML and CSS code of a web page after the code has been downloaded to a client.
class
A construct that holds data and functions that can manipulate that data.
object
An instance of a class. For example, a class might hold data and functions related to an automobile. Multiple instances of the class can be created to store specific data about individual automobiles.
object property
A specific piece of data stored in an object.
JavaScript this keyword
A builtin variable in JavaScript that always references the context in which a function is executing.
Next Section - 2.4 - DOM and jQuery