worked
worked

Reputation: 5880

Should global variables be avoided?

Learning Javascript and have a question about global variables. From my reading, most recommend not to use them. However, in class based javascripting, does this unwritten rule still apply? For instance:

var width = 0;
var height = 0;

<!-- constructor -->
function Rectangle(){}

<!-- getters/setters -->
Rectangle.prototype.getWidth = function(){
    return width;   
}

Rectangle.prototype.setWidth = function(w){
    width = w;  
}

Rectangle.prototype.getHeight = function(){
    return height;  
}

Rectangle.prototype.setHeight = function(h){
    height = h; 
}

<!-- methods -->
Rectangle.prototype.area = function(){
    return height * width;  
}

var myRect = new Rectangle();
myRect.setWidth(5);
myRect.setHeight(4);
console.log(myRect.area()); //20
console.log(myRect.getWidth()); //5
myRect.setWidth(10);
console.log(myRect.getWidth()); //10
console.log(myRect.area()); //40

I'm familiar with Java and the ability to use access modifiers for classes, properties and methods. Is it because access modifiers do not exist in Javascript that globals should be avoided?

Upvotes: 4

Views: 3541

Answers (8)

Brian Hoover
Brian Hoover

Reputation: 7991

It's more of a general coding standard. Global variables are evil, to be blunt.

So, let's say that you have the following code.

    var width = 10;
    
    <!-- constructor -->
    function Rectangle(){}
    
    <!-- getters/setters -->
    Rectangle.prototype.getWidth = function(){
        return width;   
    }
    
    Rectangle.prototype.setWidth = function(w){
        width = w;  
    }

    var myRect = new Rectangle();
    console.log(myRect.getWidth());

Well, that will do exactly what you would expect it to do, output 10 to the console.

But, now let's say that a developer comes in a little later, and adds a new line between constructing the myRect and the console.log like:

width=width * 10;

Now, console.log will return a 100. Well, you say, this isn't so bad. I can still read this.

But let's modify the last two lines to be:

var myRect = new Rectangle();
doSomethingCool();
console.log(myRect.getWidth());

function doSomethingCool() {
   LOTS o code;
   width = "10px"; // I need this to update the style sheet.
   LOTS o code;
}

Suddenly it's much more difficult to figure out what's actually happening.

Sure the console.log will output 10px, but why did it happen?

Upvotes: 1

Davide
Davide

Reputation: 2339

Usually in OO JavaScript you create a closure with the class definition to avoid polluting the global scope.

var Rectangle;

(function() {
  var width = 0;
  var height = 0;

  Rectangle = function (){}
  ...

})();  // the () execute the function with the closure

This way you are able to define private properties and still have a clean global space :)

Also, to prevent the problem MДΓΓ БДLL described in the comment while still keeping the private properties and getter/setters, this is the way to go

function Rectangle() {
  // Private properties
  var width = 0,
      height = 0;

  // Public methods using private properties
  this.setWidth = function(value) {
    width = value;
  }

  this.setHeight = function(value) {
    height = value;
  }
}

Much easier is anyway to use public properties as stated in other answer, unless you really care for the integrity of the width and height vars.

Upvotes: 1

Fabrizio Calderan
Fabrizio Calderan

Reputation: 123428

You would probably use this code instead

<!-- constructor -->
var Rectangle = function(w, h) {
    this.width = w || 0;
    this.height = h || 0;
}

<!-- getters/setters -->
Rectangle.prototype.getWidth  = function( ) { return this.width;  }
Rectangle.prototype.setWidth  = function(w) { this.width = w;    }
Rectangle.prototype.getHeight = function()  { return this.height;      }
Rectangle.prototype.setHeight = function(h) { this.height = h;    }

<!-- methods -->
Rectangle.prototype.area = function(){
    return this.height * this.width;  
}

var myRect = new Rectangle(5, 4);
console.log(myRect.area()); //20
console.log(myRect.getWidth()); //5
myRect.setWidth(10);
  1. width and height should be this.width and this.height, otherwise you will modify the global width and height variables and those values won't be tied to the single instance of Rectangle.
  2. it's better use a function expression than a function declaration for the constructor function: var Rectangle = function(w, h) { ... } rather than function Rectangle() { ... }.
  3. You can also wrap all code inside an immediately self executed function so to completely avoid the global namespace pollution (look at jAndy answer)
  4. Initial width and height of your object can be passed in the constructor function for initialization (otherwise width and height are set to 0 - or other any value - by default).
  5. These rule are definitely written (see for reference Javascript : the good parts by D.Crockford).

Upvotes: 3

Matt Ball
Matt Ball

Reputation: 360016

does this unwritten rule still apply?

Yes, you should always avoid globals. I don't know why width and height are declared outside of Rectangle in your code. You need to make heavy use of this:

function Rectangle(h, w) {
    this.height = h || 0;
    this.width = w || 0;
}

<!-- getters/setters -->
Rectangle.prototype.getWidth = function(){
    return this.width;   
}

Rectangle.prototype.setWidth = function(w){
    this.width = w;  
}

Rectangle.prototype.getHeight = function(){
    return this.height;  
}

Rectangle.prototype.setHeight = function(h){
    this.height = h; 
}

<!-- methods -->
Rectangle.prototype.area = function(){
    return this.height * this.width;  
}

Is it because access modifiers do not exist in Javascript that globals should be avoided?

No. Globals should always be avoided, regardless of the programming language.

Upvotes: 3

Gary Chambers
Gary Chambers

Reputation: 25868

To get this to behave how you are expecting, you will need to create instance properties, rather than using the global variables width and height. Your current method would result in multiple Rectangle instances using the same variables.

Try:

function Rectangle () {
  this.width = 0;
  this.height = 0;
}

Rectangle.prototype.setWidth = function(w) {
  this.width = w;
};

etc...

As others have noted, the global variables width and height will be shared by all other code in the current scope (NB. JavaScript only has function-level scoping, not block-level).

Upvotes: 2

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230531

Yes, it's still true. You should avoid global variables at all times.

In your example width and height are global. If this is the full code, it's not so much of a problem. But what if you later decide to introduce another "class" called, say, GameField. It also has width and height, naturally. I smell trouble here.

Upvotes: 0

jAndy
jAndy

Reputation: 236162

The simple reason for avoiding global variables at all costs is, that you can never be sure which other scripts get loaded at the same time. So to avoid conflicts which might end up in real ugly error scenarios, you should at least wrap your own code into one closured function context.

(function() {
    // all of your code here
}());

This simple pattern totally avoids any conflict.

Upvotes: 2

Daniel A. White
Daniel A. White

Reputation: 191058

Globals should be avoided. You should wrap stuff in closures.

Only use globals when you want to expose functionality.

Upvotes: 1

Related Questions