November 16

Objects, References, Values

var myArray = [1, 2, 3, 4];
x = myArray;
x[1] = 9;
// now, what is myArray[1]?

(Answer: 9)
Sometimes, x and myArray are called “aliases” because they refer to the same thing. Changing one changes the other.

What about numbers and variables? E.g.
var myArray = 2; // of course, myArray is not a JavaScript array
x = myArray;
x = 9;
// now, what is myArray?

(Answer: 2)
Why isn’t myArray === 9 in this case?
It all depends on what assignment means. Does “=” copy the thing on the right into the container on the left, or does “=” make put a “reference” to the thing on the right into the container on the left.
In many languages (but not all), making copies is considered too expensive for “big” things, so arrays and objects are not copied by the assignment operator. Instead, a “reference” or “pointer” to the object is used as a proxy for the array or object. It is the reference that is copied from variable to variable, not the array or object that is referred to.

What about numbers? Numbers are small, and having to explicitly copy a number would be painful, so most languages copy numbers when you assign then.

Here’s an interesting thought question: Suppose assignment copied only references to numbers rather than numbers themselves. Would there be an aliasing problem? E.g. could something like x = myArray; x = 9; actually change myArray?

(Answer: In fact, no, so some languages choose to implement numbers with references and some with values — you really cannot tell the difference. I believe some early JavaScript compilers used references, but I believe all implementation have figured out that working directly with numbers rather than indirectly through references is faster.)

Array and Object Copies

To avoid aliasing that occurs when you assign an array or object, you can make a copy. See Golan’s notes for an illustration of the different behaviors you get making a copy by reference and making a shallow copy of an array.

To make a copy of the top-level of an array, use slice(), e.g.
x = myArray.slice();

If myArray contains arrays or objects, you might also need to copy them. Copying all nested arrays and objects is called deep copying. There is no standard way to make deep copies in JavaScript, and you might be best off writing your own deep copy function when you need it.

To make a copy of an object, I recommend writing a copy method for the object (or for all objects you might want to copy). There are various “tricks” to clone objects in JavaScript, especially using libraries, but nothing standard, robust, and built-in. The fastest way is to simply construct an object with known properties:

this.copy() = function () return {name: this.name, height: this.height, ...};

And if you need a deep copy, you will have to test each field of the object, see if it contains an object, and if so, invoke the copy() method to copy that objects.

Note that if you have object A with a property whose value is object B, and object B has a property whose value is object A, then a simple attempt to call copy() methods will result in an infinitely deep copy as A and B recursively try to copy each other. The general solution is quite elegant and one example of a large set of algorithms called graph algorithms. That’s a topic for an algorithms course.

Spring Example

We’ll talk about this in class.

Noise: Perlin, Gaussian, Uniform

perlin

function setup() {
    createCanvas(300, 300);
    frameRate(50);
}

function draw() {
    background(220);

    // If the mouse is pressed, increase the noise(x) parameter by
    //    framecount * 0.01 so that the graph scrolls to the left.
    // Note that all 3 graphs start at the same x value, so they
    //    are just scaled versions of the same function.
    var animate = 0;
    if (mouseIsPressed) {
        animate = 0.01;
    }

    // top third - graph of noise(x), increment x by 0.01/pixel
    fill(200, 100, 250);
    rect(0, 0, width, 100);
    for (var i = 0; i < width; i++) {
        fill(0);
        point(i, 100 * noise(frameCount * animate + i * 0.01));
    }
    // middle third - graph of noise(x), increment x by 0.02/pixel
    fill(200, 256, 100);
    rect(0, 100, width, 100);
    for (var i = 0; i < width; i++) {
        fill(0);
        point(i, 100 + 100 * noise(frameCount * animate + i * 0.02));
    }
    // bottom third - graph of noise(x), increment x by 0.04/pixel
    fill(256, 256, 100);
    rect(0, 200, width, 100);
    for (var i = 0; i < width; i++) {
        fill(0);
        point(i, 200 + 100 * noise(frameCount * animate + i * 0.04));
    }
}

Note: In the following code, I plotted two points at every x coordinate just to increase the density of the points to make the distribution more visible.
gausunif

function setup() {
    createCanvas(300, 200);
    frameRate(0.5);
}

function draw() {
    background(220);

    // top half - graph of random(-0.3, 0.3) -- uniform random numbers
    fill(200, 100, 250);
    rect(0, 0, width, 100);
    for (var i = 0; i < width; i++) {
        fill(0);
        point(i, 50 + 100 * random(-0.3, 0.3));
        point(i, 50 + 100 * random(-0.3, 0.3));
    }
    // bottom half - graph of randomGaussian(1, 1)
    fill(200, 256, 100);
    rect(0, 100, width, 100);
    for (var i = 0; i < width; i++) {
        fill(0);
        point(i, 100 + constrain(100 * randomGaussian(0.5, 0.1), 0, 100));
        point(i, 100 + constrain(100 * randomGaussian(0.5, 0.1), 0, 100));
    }
}