Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrhorn424/59edcb3e4e0456a14402d57b71b4dffe to your computer and use it in GitHub Desktop.
Save jrhorn424/59edcb3e4e0456a14402d57b71b4dffe to your computer and use it in GitHub Desktop.
How and why to avoid global variables in JavaScript
// 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment