Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save radovanx/a47321bb529c6969d77cc58158b5c1ce to your computer and use it in GitHub Desktop.

Select an option

Save radovanx/a47321bb529c6969d77cc58158b5c1ce to your computer and use it in GitHub Desktop.

Revisions

  1. @hallettj hallettj renamed this gist Feb 14, 2009. 1 changed file with 0 additions and 0 deletions.
  2. @hallettj hallettj renamed this gist Feb 14, 2009. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. @hallettj hallettj created this gist Feb 14, 2009.
    217 changes: 217 additions & 0 deletions gistfile1.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,217 @@
    // It is important to declare your variables.

    (function() {
    var foo = 'Hello, world!';
    print(foo); //=> Hello, world!
    })();

    // Because if you don't, the become global variables.

    (function() {
    foo = 'Hello, world!';
    print(foo) //=> Hello, world!
    })();

    print(foo) //=> Hello, world!


    // When global variables sneak into your code the can cause problems.
    // Especially in applications with concurrency.

    var count = function() {
    for (i = 0; i < 10; i += 1) {
    print(i);
    }
    };

    count(); //=> 0 1 2 3 4 5 6 7 8 9

    var countSilently = function() {
    for (i = 0; i < 10; i += 1) {
    // don't print anything;
    }
    };

    // Both loops increment i at the same time, which causes strange behavior.
    window.setTimeout(countSilently, 10);
    window.setTimeout(count, 10); //=> 2 3 7 8 9


    // You can use 'this' in method definitions to refer to attributes of the
    // method's object.

    var obj = {
    name: 'foo',
    introduce: function() {
    print(this.name);
    }
    };

    obj.introduce(); //=> foo

    // But 'this' does not follow the normal rules of scope in JavaScript. One
    // might expect 'this' to be available with the same value via closure in the
    // callback defined inside the method here.

    var obj = {
    name: 'foo',
    introduce: function() {
    window.setTimeout(function() {
    print(this.name);
    }, 3000);
    }
    };

    obj.introduce(); //=> *pause* undefined

    // In fact, this got bound to the global object in the callback. To get around
    // this, assign the object reference to a regular variable that will have the
    // same value inside the callback definition.

    var obj = {
    name: 'foo',
    introduce: function() {
    var that = this;
    window.setTimeout(function() {
    print(that.name);
    }, 3000);
    }
    };

    obj.introduce(); //=> *pause* foo


    // The keyword 'this' is actually dynamically assigned whenever a function is
    // invoked. When a function is invoked as a method, i.e. obj.method(), 'this'
    // is bound to 'obj'. But when a function is invoked by itself 'this' is bound
    // to the global object.

    var phrase = 'Hello, world!';
    var printPhrase() {
    print(this.phrase);
    }

    printPhrase(); //=> Hello, world!

    // This is true even of functions that were defined as a method.

    var obj = {
    name: 'foo',
    introduce: function() {
    print(this.name);
    }
    };

    // When the function is invoked without 'obj.' in front of it, 'this' becomes
    // the global namespace.

    var introduce = obj.introduce;
    introduce(); //=> undefined


    // Method invocation and function invocation are two of the invocation patterns
    // in JavaScript. A third is apply invocation, which gives us control over what
    // 'this' will be assigned to during function execution.

    introduce.apply(obj, null); //=> foo

    // 'apply' is a method on Function. The first argument is the value that 'this'
    // will be bound to. Successive arguments to apply are passed as arguments to
    // the function that is being invoked.

    var chatty = function(repeatTimes) {
    var i;
    for (i = 0; i < repeatTimes; i += 1) {
    print(this.name + ' ');
    }
    }
    chatty.apply(obj, 3) //=> foo foo foo


    // The fourth and final invocation pattern in JavaScript is constructor
    // invocation. This pattern was designed to provide a way to create new objects
    // that would appear familiar to programmers who are used to programming with
    // classes.

    var Cat = function(name) {
    this.name = name;
    };
    Cat.prototype = {
    query: function() {
    print(this.name + ' says, "meow"');
    }
    };

    // When a function is called with the 'new' keyword in front of it, a new
    // object is created and is bound to 'this' when the function runs. Special
    // constructor functions use this feature to customize new objects as they are
    // created.

    var whiskers = new Cat('whiskers');
    whiskers.query(); //=> whiskers says "meow"

    // When a new object is created with 'new', the prototype of the new object is
    // set to the prototype of the constructor function. So the new object inherits
    // all of the attributes of the constructor's prototype value. In this case,
    // new cat objects inherit the 'query' method from Cat.prototype.

    var nibbler = new Cat('nibbler');
    nibbler.query(); //=> nibbler says "meow"


    // If a constructor function is called without the 'new' keyword, it is invoked
    // with the ordinary function invocation pattern.

    var gotcha = Cat('gotcha!');
    gotcha.query(); //=> typein:165: TypeError: gotcha has no properties

    // So 'this' is assigned to the global object instead of to a newly created object. That means that any attributes assigned to the new object by the constructor function become global variables!

    print(name); //=> gotcha!


    // Constructor invocation is pretty complicated and prone to disastrous global
    // variable creation. Here is a cleaner way to create new objects that inherit
    // from other objects.

    // This defines Object.create, a method that simplifies the behavior of the
    // 'new' keyword. This method was invented by Douglas Crockford.
    // http://javascript.crockford.com/prototypal.html
    if (typeof Object.create !== 'function') {
    Object.create = function(o) {
    var F = function() {};
    F.prototype = o;
    return new F();
    };
    }

    // Object.create(obj) returns a new object that inherits all of the attributes
    // of obj. The 'cat' prototype object here defines a 'clone' method that wraps
    // around Object.create to customize new 'cat' objects as they are created.

    var cat = {
    query: function() {
    print(this.name + ' says "meow"');
    },
    clone: function(name) {
    var newCat = Object.create(this);
    newCat.name = name;
    return newCat;
    }
    };

    var fluffy = cat.clone('fluffy');
    fluffy.query(); //=> fluffy says "meow"

    // In addition to inheriting 'query', new cats also inherit 'clone'.

    var fluffy2 = fluffy.clone('fluffy2');
    fluffy2.query(); //=> fluffy2 says "meow"

    // Methods and attributes are inherited, not copied. If you change the
    // definition of 'clone' on 'cat' at this point, the change will be reflected
    // in cat objects that have already been created.

    fluffy2.hasOwnProperty('clone') //=> false
    fluffy.hasOwnProperty('clone') //=> false
    cat.hasOwnProperty('clone') //=> true